gitmine 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/VERSION +1 -1
- data/gitmine.gemspec +11 -3
- data/lib/gitmine.rb +92 -1
- data/lib/gitmine/branch.rb +102 -0
- data/lib/gitmine/cli.rb +47 -43
- data/lib/gitmine/colors.rb +45 -0
- data/lib/gitmine/commit.rb +21 -23
- data/lib/gitmine/config.rb +53 -0
- data/lib/gitmine/git.rb +34 -0
- data/lib/gitmine/hudson_job.rb +41 -0
- data/lib/gitmine/issue.rb +36 -17
- data/spec/gitmine_spec.rb +3 -3
- data/spec/issue_spec.rb +0 -6
- data/spec/lib/branch_spec.rb +78 -0
- data/spec/lib/config_spec.rb +8 -0
- metadata +13 -5
- data/lib/gitmine/gitmine.rb +0 -105
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.6
|
data/gitmine.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{gitmine}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.6"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Philippe Creux"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-12-10}
|
13
13
|
s.default_executable = %q{gitmine}
|
14
14
|
s.description = %q{Git log with status of associated redmine tickets}
|
15
15
|
s.email = %q{pcreux@gmail.com}
|
@@ -32,15 +32,21 @@ Gem::Specification.new do |s|
|
|
32
32
|
"bin/gitmine",
|
33
33
|
"gitmine.gemspec",
|
34
34
|
"lib/gitmine.rb",
|
35
|
+
"lib/gitmine/branch.rb",
|
35
36
|
"lib/gitmine/cli.rb",
|
37
|
+
"lib/gitmine/colors.rb",
|
36
38
|
"lib/gitmine/commit.rb",
|
37
|
-
"lib/gitmine/
|
39
|
+
"lib/gitmine/config.rb",
|
40
|
+
"lib/gitmine/git.rb",
|
41
|
+
"lib/gitmine/hudson_job.rb",
|
38
42
|
"lib/gitmine/issue.rb",
|
39
43
|
"spec/commit_msg_to_issue_id_spec.rb",
|
40
44
|
"spec/commit_spec.rb",
|
41
45
|
"spec/config.yml",
|
42
46
|
"spec/gitmine_spec.rb",
|
43
47
|
"spec/issue_spec.rb",
|
48
|
+
"spec/lib/branch_spec.rb",
|
49
|
+
"spec/lib/config_spec.rb",
|
44
50
|
"spec/spec_helper.rb"
|
45
51
|
]
|
46
52
|
s.homepage = %q{http://github.com/pcreux/gitmine}
|
@@ -53,6 +59,8 @@ Gem::Specification.new do |s|
|
|
53
59
|
"spec/commit_spec.rb",
|
54
60
|
"spec/gitmine_spec.rb",
|
55
61
|
"spec/issue_spec.rb",
|
62
|
+
"spec/lib/branch_spec.rb",
|
63
|
+
"spec/lib/config_spec.rb",
|
56
64
|
"spec/spec_helper.rb"
|
57
65
|
]
|
58
66
|
|
data/lib/gitmine.rb
CHANGED
@@ -4,6 +4,97 @@ require 'grit'
|
|
4
4
|
require 'yaml'
|
5
5
|
require 'httparty'
|
6
6
|
|
7
|
-
|
7
|
+
class Gitmine
|
8
|
+
|
9
|
+
def self.list
|
10
|
+
gm = Gitmine.new
|
11
|
+
gm.commits.each do |commit|
|
12
|
+
status = commit.issue ? commit.issue.status : 'N/A'
|
13
|
+
puts "#{commit.id[0..6]} #{status.ljust(12)} #{commit.committer.name.ljust(15)} #{commit.message[0..50].gsub("\n", '')}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@repo = Grit::Repo.new(ENV['PWD'])
|
19
|
+
@branch = File.read('./.git/HEAD').match(/^ref: refs\/heads\/(.+)/)[1]
|
20
|
+
end
|
21
|
+
|
22
|
+
def commits
|
23
|
+
@repo.commits(@branch).map do |c|
|
24
|
+
Commit.new(c)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# TODO specs
|
30
|
+
def self.branch(branch_name)
|
31
|
+
issue_id = branch_name[/^\d+/]
|
32
|
+
original_branch = File.read('./.git/HEAD').match(/^ref: refs\/heads\/(.+)/)[1]
|
33
|
+
|
34
|
+
raise "Invalid branch name. It should look like 123-my-branch" unless branch_name[/^\d+-/]
|
35
|
+
|
36
|
+
issue = Issue.find(issue_id)
|
37
|
+
|
38
|
+
raise "Issue ##{issue_id} does not exists" if issue.nil?
|
39
|
+
|
40
|
+
puts yellow("Create the branch #{branch_name}")
|
41
|
+
run_cmd("git checkout -b #{branch_name}")
|
42
|
+
|
43
|
+
puts yellow("Push it to origin")
|
44
|
+
run_cmd("git push origin #{branch_name}")
|
45
|
+
|
46
|
+
puts yellow("Make the local branch tracking the remote")
|
47
|
+
run_cmd("git branch --set-upstream #{branch_name} origin/#{branch_name}")
|
48
|
+
|
49
|
+
puts yellow("Adding a note to the Issue ##{issue_id}")
|
50
|
+
note = "Branch *#{branch_name}* created from #{original_branch}"
|
51
|
+
if Config.github
|
52
|
+
note << %{ - "See on Github":https://github.com/#{Config.github}/tree/#{branch_name}}
|
53
|
+
note << %{ - "Compare on Github":https://github.com/#{Config.github}/compare/#{original_branch}...#{branch_name}}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# TODO specs
|
58
|
+
def self.checkout(issue_id)
|
59
|
+
local_branch = LocalBranch.find(issue_id).name
|
60
|
+
if local_branch
|
61
|
+
run_cmd("git checkout #{local_branch}")
|
62
|
+
return
|
63
|
+
end
|
64
|
+
|
65
|
+
remote_branch = RemoteBranch.find(issue_id).name
|
66
|
+
if remote_branch
|
67
|
+
run_cmd("git checkout -b #{remote_branch} origin/#{remote_branch}")
|
68
|
+
return
|
69
|
+
end
|
70
|
+
|
71
|
+
raise "Can't find branch starting with #{issue_id}"
|
72
|
+
end
|
73
|
+
|
74
|
+
# TODO specs
|
75
|
+
def self.delete(issue_id)
|
76
|
+
RemoteBranch.find(issue_id).delete
|
77
|
+
end
|
78
|
+
|
79
|
+
# TODO specs
|
80
|
+
def self.reviewed(issue_id)
|
81
|
+
issue = Issue.find(issue_id)
|
82
|
+
|
83
|
+
puts yellow("Merge #{issue_id} to master and push")
|
84
|
+
issue.local_branch.merge_to_master
|
85
|
+
|
86
|
+
puts yellow("Delete remote branch")
|
87
|
+
issue.remote_branch.delete
|
88
|
+
|
89
|
+
puts yellow("Delete hudson jobs")
|
90
|
+
issue.delete_hudson_jobs
|
91
|
+
|
92
|
+
puts yellow("Set Ticket status to 'reviewed'")
|
93
|
+
issue.update_status("reviewed")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
%w(config issue commit cli colors branch git hudson_job).each do |filename|
|
8
99
|
require File.dirname(__FILE__) + "/gitmine/#{filename}.rb"
|
9
100
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
class Gitmine
|
2
|
+
class Branch
|
3
|
+
class << self
|
4
|
+
def find(issue_id)
|
5
|
+
new(issue_id)
|
6
|
+
end
|
7
|
+
|
8
|
+
# TODO: specs
|
9
|
+
# Return local branch name for issue_id
|
10
|
+
def find_local(issue_id)
|
11
|
+
local_branches.select { |branch| branch[/^#{issue_id}-/] }.first
|
12
|
+
end
|
13
|
+
|
14
|
+
# TODO: specs
|
15
|
+
# Return remote branch name for issue_id
|
16
|
+
def find_remote(issue_id)
|
17
|
+
remote_branch = remote_branches.select { |branch| branch[/^#{issue_id}-/] }.first
|
18
|
+
unless remote_branch
|
19
|
+
# Fetch and retry
|
20
|
+
Git.fetch
|
21
|
+
clear_memoized_remote_branches!
|
22
|
+
remote_branch = remote_branches.select { |branch| branch[/^#{issue_id}-/] }.first
|
23
|
+
end
|
24
|
+
|
25
|
+
remote_branch
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# Return an array of local branches starting with digits
|
30
|
+
# Example
|
31
|
+
# ['123-my-branch', '1234-your-branch']
|
32
|
+
# TODO specs
|
33
|
+
def local_branches
|
34
|
+
return @@local_branches if defined?(@@local_branches) && @@local_branches
|
35
|
+
branches = []
|
36
|
+
Git.local_branches.each_line do |line|
|
37
|
+
if match = line[/\d+.*$/]
|
38
|
+
branches << match
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
@@local_branches = branches
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return an array of remote branches
|
46
|
+
# TODO specs
|
47
|
+
def remote_branches
|
48
|
+
return @@remote_branches if defined?(@@remote_branches) && @@remote_branches
|
49
|
+
branches = []
|
50
|
+
Git.remote_branches.each_line do |line|
|
51
|
+
if match = line.match(/origin\/(\d+.*)/)
|
52
|
+
branches << match[1]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
@@remote_branches = branches
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
def clear_memoized_remote_branches!
|
62
|
+
@@remote_branches = nil
|
63
|
+
end
|
64
|
+
end # class methods
|
65
|
+
|
66
|
+
attr_accessor :issue_id
|
67
|
+
|
68
|
+
def initialize(issue_id)
|
69
|
+
@issue_id = issue_id
|
70
|
+
end
|
71
|
+
|
72
|
+
def local
|
73
|
+
LocalBranch.new(issue_id)
|
74
|
+
end
|
75
|
+
|
76
|
+
def remote
|
77
|
+
RemoteBranch.new(issue_id)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class LocalBranch < Branch
|
82
|
+
def name
|
83
|
+
@name ||= Branch.find_local(issue_id)
|
84
|
+
end
|
85
|
+
|
86
|
+
def merge_to_master
|
87
|
+
Git.checkout("master")
|
88
|
+
Git.merge(self.name)
|
89
|
+
Git.push
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class RemoteBranch < Branch
|
94
|
+
def name
|
95
|
+
@name ||= Branch.find_remote(issue_id)
|
96
|
+
end
|
97
|
+
|
98
|
+
def delete
|
99
|
+
Git.delete_remote_branch(self.name)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/gitmine/cli.rb
CHANGED
@@ -1,50 +1,54 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
gitmine checkout ISSUE_ID
|
21
|
-
Checkout remote/local branch starting with ISSUE_ID
|
22
|
-
Example: gitmine checkout 1234
|
23
|
-
|
24
|
-
gitmine delete ISSUE_ID
|
25
|
-
Delete remote branch starting with ISSUE_ID
|
26
|
-
Example: gitmine delete 1234
|
27
|
-
|
28
|
-
gitmine log
|
29
|
-
Displays latest 10 commits and the status of their associated Redmine tickets
|
30
|
-
EOS
|
31
|
-
end
|
32
|
-
end
|
1
|
+
class Gitmine::CLI
|
2
|
+
def self.run
|
3
|
+
case ARGV[0]
|
4
|
+
when "log"
|
5
|
+
list
|
6
|
+
when "branch", "br"
|
7
|
+
branch
|
8
|
+
when "checkout", "co"
|
9
|
+
checkout
|
10
|
+
when "delete", "del"
|
11
|
+
delete
|
12
|
+
when "for_deploy", "reviewed"
|
13
|
+
reviewed
|
14
|
+
else
|
15
|
+
puts <<-EOS
|
16
|
+
Usage:
|
17
|
+
gitmine branch BRANCH_NAME
|
18
|
+
Create a new branch, push to origin, add github links to gitmine ticket
|
19
|
+
Example: gitmine branch 1234-my-branch
|
33
20
|
|
34
|
-
|
35
|
-
|
36
|
-
|
21
|
+
gitmine checkout ISSUE_ID
|
22
|
+
Checkout remote/local branch starting with ISSUE_ID
|
23
|
+
Example: gitmine checkout 1234
|
37
24
|
|
38
|
-
|
39
|
-
|
40
|
-
|
25
|
+
gitmine delete ISSUE_ID
|
26
|
+
Delete remote branch starting with ISSUE_ID
|
27
|
+
Example: gitmine delete 1234
|
41
28
|
|
42
|
-
|
43
|
-
|
29
|
+
gitmine log
|
30
|
+
Displays latest 10 commits and the status of their associated Redmine tickets
|
31
|
+
EOS
|
44
32
|
end
|
33
|
+
end
|
45
34
|
|
46
|
-
|
47
|
-
|
48
|
-
|
35
|
+
def self.list
|
36
|
+
Gitmine.list
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.branch
|
40
|
+
Gitmine.branch(ARGV[1])
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.checkout
|
44
|
+
Gitmine.checkout(ARGV[1])
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.delete
|
48
|
+
Gitmine.delete(ARGV[1])
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.reviewed
|
52
|
+
Gitmine.reviewed(ARGV[1])
|
49
53
|
end
|
50
54
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#module Gitmine::Colors
|
2
|
+
# Display the command, run it and raise if it fails.
|
3
|
+
def run_cmd(cmd)
|
4
|
+
puts blue(cmd)
|
5
|
+
raise unless system cmd
|
6
|
+
end
|
7
|
+
|
8
|
+
# ### COLORS ###
|
9
|
+
# Display colored text in console
|
10
|
+
def color(text, color_code)
|
11
|
+
"#{color_code}#{text}\e[0m"
|
12
|
+
end
|
13
|
+
|
14
|
+
def bold(text)
|
15
|
+
color(text, "\e[1m")
|
16
|
+
end
|
17
|
+
|
18
|
+
def white(text)
|
19
|
+
color(text, "\e[37m")
|
20
|
+
end
|
21
|
+
|
22
|
+
def green(text)
|
23
|
+
color(text, "\e[32m")
|
24
|
+
end
|
25
|
+
|
26
|
+
def red(text)
|
27
|
+
color(text, "\e[31m")
|
28
|
+
end
|
29
|
+
|
30
|
+
def magenta(text)
|
31
|
+
color(text, "\e[35m")
|
32
|
+
end
|
33
|
+
|
34
|
+
def yellow(text)
|
35
|
+
color(text, "\e[33m")
|
36
|
+
end
|
37
|
+
|
38
|
+
def blue(text)
|
39
|
+
color(text, "\e[34m")
|
40
|
+
end
|
41
|
+
|
42
|
+
def grey(text)
|
43
|
+
color(text, "\e[90m")
|
44
|
+
end
|
45
|
+
#end
|
data/lib/gitmine/commit.rb
CHANGED
@@ -1,28 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
attr_reader :grit_commit
|
1
|
+
class Gitmine::Commit
|
2
|
+
attr_reader :grit_commit
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# Issue associated with this commit
|
11
|
-
# Return nil if their is no associated issue
|
12
|
-
def issue
|
13
|
-
@issue ||= Issue.get_for_commit(message)
|
14
|
-
end
|
4
|
+
# Initialize a new Commit objects that delegates methods to the Grit::Commit object passed in
|
5
|
+
def initialize(grit_commit)
|
6
|
+
@grit_commit = grit_commit
|
7
|
+
end
|
15
8
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
9
|
+
# Issue associated with this commit
|
10
|
+
# Return nil if their is no associated issue
|
11
|
+
def issue
|
12
|
+
@issue ||= Gitmine::Issue.get_for_commit(message)
|
13
|
+
end
|
20
14
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
return @grit_commit.send(m, args, block) if @grit_commit.respond_to? m
|
25
|
-
super
|
26
|
-
end
|
15
|
+
# Delegate #id to Grit::Commit
|
16
|
+
def id
|
17
|
+
@grit_commit.id
|
27
18
|
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
# Delegate methods to Grit::Commit
|
22
|
+
def method_missing(m, *args, &block)
|
23
|
+
return @grit_commit.send(m, args, block) if @grit_commit.respond_to? m
|
24
|
+
super
|
25
|
+
end
|
28
26
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class Gitmine
|
2
|
+
class Config
|
3
|
+
CONFIG_FILE = './.gitmine.yml'
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def config
|
7
|
+
@@config ||= new
|
8
|
+
end
|
9
|
+
|
10
|
+
def redmine_host
|
11
|
+
config['host']
|
12
|
+
end
|
13
|
+
|
14
|
+
def redmine_api_key
|
15
|
+
config['api_key']
|
16
|
+
end
|
17
|
+
|
18
|
+
def github
|
19
|
+
config['github']
|
20
|
+
end
|
21
|
+
|
22
|
+
def hudson_host
|
23
|
+
config['hudson']['host']
|
24
|
+
end
|
25
|
+
|
26
|
+
def hudson_username
|
27
|
+
config['hudson']['username']
|
28
|
+
end
|
29
|
+
|
30
|
+
def hudson_password
|
31
|
+
config['hudson']['password']
|
32
|
+
end
|
33
|
+
|
34
|
+
def statuses
|
35
|
+
config['statuses']
|
36
|
+
end
|
37
|
+
|
38
|
+
def status_reviewed
|
39
|
+
config['statuses']['reviewed']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
path = CONFIG_FILE
|
45
|
+
@config = YAML.load_file(path)
|
46
|
+
end
|
47
|
+
|
48
|
+
def [](key)
|
49
|
+
@config[key]
|
50
|
+
end
|
51
|
+
|
52
|
+
end # Class Config
|
53
|
+
end
|
data/lib/gitmine/git.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
class Gitmine::Git
|
2
|
+
class << self
|
3
|
+
# Return output of 'git branch'
|
4
|
+
def local_branches
|
5
|
+
`git branch`
|
6
|
+
end
|
7
|
+
|
8
|
+
# Return output of 'git branch -r'
|
9
|
+
def remote_branches
|
10
|
+
`git branch -r`
|
11
|
+
end
|
12
|
+
|
13
|
+
# Run 'git fetch'
|
14
|
+
def fetch
|
15
|
+
run_cmd("git fetch")
|
16
|
+
end
|
17
|
+
|
18
|
+
def checkout(branch)
|
19
|
+
run_cmd("git checkout #{branch}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def merge(branch)
|
23
|
+
run_cmd("git merge #{branch}")
|
24
|
+
end
|
25
|
+
|
26
|
+
def push
|
27
|
+
run_cmd("git push")
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete_remote_branch(branch)
|
31
|
+
run_cmd("git push origin :#{branch}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class Gitmine
|
2
|
+
class HudsonJob
|
3
|
+
class Http
|
4
|
+
include HTTParty
|
5
|
+
|
6
|
+
base_uri Config.hudson_host
|
7
|
+
basic_auth(Config.hudson_username, Config.hudson_password) if Config.hudson_username
|
8
|
+
headers 'Content-type' => 'text/xml'
|
9
|
+
# I get timeout errors on heroku but not on local env. Is that because of REE-1.8.7 ?
|
10
|
+
# Workaround: Set the timeout to 10 seconds and rescue timeout errors.
|
11
|
+
default_timeout 8
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.all_by_name_including(pattern)
|
15
|
+
HudsonJob.all.select { |projects| projects.name[pattern] }
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.all
|
19
|
+
r = Http.get('/api/xml')
|
20
|
+
return [] unless r.code == 200
|
21
|
+
r.parsed_response["hudson"]["job"].map do |jobs_data|
|
22
|
+
new(jobs_data["name"])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_accessor :name
|
27
|
+
|
28
|
+
def initialize(name)
|
29
|
+
@name = name
|
30
|
+
end
|
31
|
+
|
32
|
+
def delete!
|
33
|
+
r = Http.post("/job/#{name}/doDelete")
|
34
|
+
raise "Failed to delete job #{name}" unless r.code == 200
|
35
|
+
puts green(" - #{name} deleted!")
|
36
|
+
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
data/lib/gitmine/issue.rb
CHANGED
@@ -1,14 +1,7 @@
|
|
1
|
-
|
1
|
+
class Gitmine
|
2
2
|
class Issue
|
3
|
-
CONFIG_FILE = './.gitmine.yml'
|
4
|
-
|
5
3
|
attr_reader :id, :subject, :status
|
6
4
|
|
7
|
-
# Config from .gitmine.yml
|
8
|
-
def self.config
|
9
|
-
@@config ||= YAML.load_file(CONFIG_FILE)
|
10
|
-
end
|
11
|
-
|
12
5
|
# Extract the issue_id from a commit message.
|
13
6
|
# Examples:
|
14
7
|
# CommitMsgToIssueId.parse("Message for Issue #123.")
|
@@ -34,28 +27,49 @@ module Gitmine
|
|
34
27
|
}
|
35
28
|
end
|
36
29
|
|
30
|
+
def local_branch
|
31
|
+
LocalBranch.find(self.id)
|
32
|
+
end
|
33
|
+
|
34
|
+
def remote_branch
|
35
|
+
RemoteBranch.find(self.id)
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete_hudson_jobs
|
39
|
+
hudson_jobs.map(&:delete!)
|
40
|
+
end
|
41
|
+
|
37
42
|
# Get attributes from redmine and set them all
|
38
43
|
def build_via_issue_id(issue_id)
|
39
44
|
@id = issue_id
|
40
45
|
data = http_get(issue_id).parsed_response['issue']
|
41
|
-
|
42
|
-
|
46
|
+
if data
|
47
|
+
@subject = data['subject']
|
48
|
+
@status = data['status']['name']
|
49
|
+
end
|
43
50
|
end
|
44
51
|
|
45
52
|
# Add a note to the Issue
|
46
53
|
def add_note(note)
|
47
54
|
response = self.class.put(url(self.id), :query => {:notes => note}, :body => "") # nginx reject requests without body
|
55
|
+
raise response.response.to_s unless response.code == 200
|
48
56
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
57
|
+
puts green("Note added to Issue ##{self.id}: #{note}")
|
58
|
+
end
|
59
|
+
|
60
|
+
def update_status(st)
|
61
|
+
status_id = Gitmine::Config.statuses[st]
|
62
|
+
raise "Please specify status_id in .gitmine.yml for #{st}" unless status_id
|
63
|
+
|
64
|
+
response = self.class.put(url(self.id), :query => {:issue => {:status_id => status_id }}, :body => "")
|
65
|
+
raise response.response.to_s unless response.code == 200
|
66
|
+
|
67
|
+
puts green("Issue ##{self.id} -> #{st}")
|
54
68
|
end
|
55
69
|
|
56
70
|
include HTTParty
|
57
|
-
base_uri "#{
|
58
|
-
basic_auth
|
71
|
+
base_uri "#{Gitmine::Config.redmine_host}/issues/"
|
72
|
+
basic_auth Gitmine::Config.redmine_api_key, '' # username is api_key, password is empty
|
59
73
|
headers 'Content-type' => 'text/xml' # by-pass rails authenticity token mechanism
|
60
74
|
|
61
75
|
protected
|
@@ -68,5 +82,10 @@ module Gitmine
|
|
68
82
|
def http_get(issue_id)
|
69
83
|
self.class.get(url(issue_id))
|
70
84
|
end
|
85
|
+
|
86
|
+
def hudson_jobs
|
87
|
+
HudsonJob.all_by_name_including(self.id)
|
88
|
+
end
|
89
|
+
|
71
90
|
end
|
72
91
|
end
|
data/spec/gitmine_spec.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Gitmine
|
3
|
+
describe Gitmine do
|
4
4
|
before do
|
5
5
|
File.stub!(:read) { "ref: refs/heads/wip" }
|
6
6
|
end
|
7
7
|
|
8
|
-
let(:gitmine) { Gitmine
|
8
|
+
let(:gitmine) { Gitmine.new }
|
9
9
|
|
10
10
|
let(:commit_1) do
|
11
11
|
mock(
|
@@ -41,7 +41,7 @@ describe Gitmine::Gitmine do
|
|
41
41
|
|
42
42
|
it "should check out to the current branch" do
|
43
43
|
Grit::Repo.should_receive(:new).with(ENV['PWD']) { grit_repo }
|
44
|
-
Gitmine
|
44
|
+
Gitmine.new
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
data/spec/issue_spec.rb
CHANGED
@@ -8,12 +8,6 @@ describe Gitmine::Issue do
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
describe "#config" do
|
12
|
-
it "should load the config from config.yml" do
|
13
|
-
Gitmine::Issue.config.should == {"host"=>"http://redmine-gitmine.heroku.com", "github" => "pcreux/gitmine"}
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
11
|
describe "#get_for_commit" do
|
18
12
|
it "should parse the commit message to find a commit_id and call #get" do
|
19
13
|
commit_msg = 'A commit msg Issue #123'
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Gitmine::Branch do
|
4
|
+
before do
|
5
|
+
Gitmine::Git.stub!(:local_branches) { <<-GIT_OUTPUT
|
6
|
+
2632-invoice-should-accept-date
|
7
|
+
2675
|
8
|
+
* 2869-BUG-accepted-by-not-set
|
9
|
+
master
|
10
|
+
production
|
11
|
+
GIT_OUTPUT
|
12
|
+
}
|
13
|
+
|
14
|
+
Gitmine::Git.stub!(:remote_branches) { <<-GIT_OUTPUT
|
15
|
+
origin/2890-email-aliases
|
16
|
+
origin/2915-sanitize-eft-fields
|
17
|
+
origin/HEAD -> origin/master
|
18
|
+
origin/master
|
19
|
+
origin/production
|
20
|
+
GIT_OUTPUT
|
21
|
+
}
|
22
|
+
|
23
|
+
Gitmine::Git.stub!(:fetch)
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#local_branches" do
|
27
|
+
it "should return an array of branches starting with digits only" do
|
28
|
+
Gitmine::Branch.local_branches.
|
29
|
+
should == %w(2632-invoice-should-accept-date 2675 2869-BUG-accepted-by-not-set)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#remote_branches" do
|
34
|
+
it "should return an array of branches starting with digits only" do
|
35
|
+
Gitmine::Branch.remote_branches.
|
36
|
+
should == %w( 2890-email-aliases 2915-sanitize-eft-fields )
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "find_local(issue_id)" do
|
41
|
+
context "when the branch exists" do
|
42
|
+
it "should return the branch name" do
|
43
|
+
Gitmine::Branch.find_local('2632').
|
44
|
+
should == '2632-invoice-should-accept-date'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when the does not exists" do
|
49
|
+
it "should return nil" do
|
50
|
+
Gitmine::Branch.find_local('9999').
|
51
|
+
should == nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "find_remote(issue_id)" do
|
57
|
+
context "when the branch exists" do
|
58
|
+
it "should return the branch name" do
|
59
|
+
Gitmine::Branch.find_remote('2890').
|
60
|
+
should == '2890-email-aliases'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when the does not exists" do
|
65
|
+
it "should return nil" do
|
66
|
+
Gitmine::Branch.find_remote('9999').
|
67
|
+
should == nil
|
68
|
+
end
|
69
|
+
it "should fetch and retry" do
|
70
|
+
Gitmine::Git.should_receive(:fetch)
|
71
|
+
Gitmine::Branch.should_receive(:clear_memoized_remote_branches!)
|
72
|
+
# can't test the recursive call
|
73
|
+
Gitmine::Branch.find_remote('9999')
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitmine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 6
|
10
|
+
version: 0.1.6
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Philippe Creux
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-12-10 00:00:00 -08:00
|
19
19
|
default_executable: gitmine
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -85,15 +85,21 @@ files:
|
|
85
85
|
- bin/gitmine
|
86
86
|
- gitmine.gemspec
|
87
87
|
- lib/gitmine.rb
|
88
|
+
- lib/gitmine/branch.rb
|
88
89
|
- lib/gitmine/cli.rb
|
90
|
+
- lib/gitmine/colors.rb
|
89
91
|
- lib/gitmine/commit.rb
|
90
|
-
- lib/gitmine/
|
92
|
+
- lib/gitmine/config.rb
|
93
|
+
- lib/gitmine/git.rb
|
94
|
+
- lib/gitmine/hudson_job.rb
|
91
95
|
- lib/gitmine/issue.rb
|
92
96
|
- spec/commit_msg_to_issue_id_spec.rb
|
93
97
|
- spec/commit_spec.rb
|
94
98
|
- spec/config.yml
|
95
99
|
- spec/gitmine_spec.rb
|
96
100
|
- spec/issue_spec.rb
|
101
|
+
- spec/lib/branch_spec.rb
|
102
|
+
- spec/lib/config_spec.rb
|
97
103
|
- spec/spec_helper.rb
|
98
104
|
has_rdoc: true
|
99
105
|
homepage: http://github.com/pcreux/gitmine
|
@@ -134,4 +140,6 @@ test_files:
|
|
134
140
|
- spec/commit_spec.rb
|
135
141
|
- spec/gitmine_spec.rb
|
136
142
|
- spec/issue_spec.rb
|
143
|
+
- spec/lib/branch_spec.rb
|
144
|
+
- spec/lib/config_spec.rb
|
137
145
|
- spec/spec_helper.rb
|
data/lib/gitmine/gitmine.rb
DELETED
@@ -1,105 +0,0 @@
|
|
1
|
-
module Gitmine
|
2
|
-
class Gitmine
|
3
|
-
def self.list
|
4
|
-
gm = Gitmine.new
|
5
|
-
gm.commits.each do |commit|
|
6
|
-
status = commit.issue ? commit.issue.status : 'N/A'
|
7
|
-
puts "#{commit.id[0..6]} #{status.ljust(12)} #{commit.committer.name.ljust(15)} #{commit.message[0..50].gsub("\n", '')}"
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def initialize
|
12
|
-
@repo = Grit::Repo.new(ENV['PWD'])
|
13
|
-
@branch = File.read('./.git/HEAD').match(/^ref: refs\/heads\/(.+)/)[1]
|
14
|
-
end
|
15
|
-
|
16
|
-
def commits
|
17
|
-
@repo.commits(@branch).map do |c|
|
18
|
-
Commit.new(c)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
|
23
|
-
# TODO specs
|
24
|
-
def self.branch(branch_name)
|
25
|
-
issue_id = branch_name[/^\d+/]
|
26
|
-
original_branch = File.read('./.git/HEAD').match(/^ref: refs\/heads\/(.+)/)[1]
|
27
|
-
config = Issue.config
|
28
|
-
|
29
|
-
raise "Invalid branch name. It should start with the issue number" unless issue_id
|
30
|
-
|
31
|
-
puts "Create the branch #{branch_name}"
|
32
|
-
run_cmd("git checkout -b #{branch_name}")
|
33
|
-
|
34
|
-
puts "Push it to origin"
|
35
|
-
run_cmd("git push origin #{branch_name}")
|
36
|
-
|
37
|
-
puts "Make the local branch tracking the remote"
|
38
|
-
run_cmd("git branch --set-upstream #{branch_name} origin/#{branch_name}")
|
39
|
-
|
40
|
-
puts "Adding a note to the Issue ##{issue_id}"
|
41
|
-
note = "Branch *#{branch_name}* created from #{original_branch}"
|
42
|
-
if config['github']
|
43
|
-
note << %{ - "See on Github":https://github.com/#{config['github']}/tree/#{branch_name}}
|
44
|
-
note << %{ - "Compare on Github":https://github.com/#{config['github']}/compare/#{original_branch}...#{branch_name}}
|
45
|
-
end
|
46
|
-
|
47
|
-
puts 'Done!' if Issue.find(issue_id).add_note(note)
|
48
|
-
end
|
49
|
-
|
50
|
-
# TODO specs
|
51
|
-
def self.checkout(issue_id)
|
52
|
-
if local_branch = local_branches.select { |branch| branch[/^#{issue_id}-/] }.first
|
53
|
-
run_cmd("git checkout #{local_branch}")
|
54
|
-
return
|
55
|
-
end
|
56
|
-
|
57
|
-
if remote_branch = remote_branches.select { |branch| branch[/^#{issue_id}-/] }.first
|
58
|
-
run_cmd("git checkout -b #{remote_branch} origin/#{remote_branch}")
|
59
|
-
return
|
60
|
-
end
|
61
|
-
|
62
|
-
raise "Can't find branch starting with #{issue_id}"
|
63
|
-
end
|
64
|
-
|
65
|
-
# TODO specs
|
66
|
-
def self.delete(issue_id)
|
67
|
-
if remote_branch = remote_branches.select { |branch| branch[/^#{issue_id}-/] }.first
|
68
|
-
run_cmd("git push origin :#{remote_branch}")
|
69
|
-
else
|
70
|
-
raise "Can't find branch starting with #{issue_id}"
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def self.run_cmd(cmd)
|
75
|
-
puts cmd
|
76
|
-
exit! unless system(cmd)
|
77
|
-
end
|
78
|
-
|
79
|
-
# TODO specs
|
80
|
-
def self.local_branches
|
81
|
-
branches = []
|
82
|
-
`git branch`.each_line do |line|
|
83
|
-
if match = line[/\d+.*$/]
|
84
|
-
branches << match
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
branches
|
89
|
-
end
|
90
|
-
|
91
|
-
# TODO specs
|
92
|
-
def self.remote_branches
|
93
|
-
run_cmd("git fetch")
|
94
|
-
|
95
|
-
branches = []
|
96
|
-
`git branch -r`.each_line do |line|
|
97
|
-
if match = line.match(/origin\/(\d+.*)/)
|
98
|
-
branches << match[1]
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
branches
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|