gitmine 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|