hg-blame-game 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 27d059d640c539503f7c4c313667b765448ccb83
4
+ data.tar.gz: 5065d0e44ca382eb496b20aaa2c113f932218c44
5
+ SHA512:
6
+ metadata.gz: ff9791c8f81b7ea30ffe7737e1d53b389e9756df097f8be450b1a4261421c5fefc599a4f6840de2683932a7d48a71cafbf44ba3910406c3725e6b15f67eae297
7
+ data.tar.gz: c6da93c80c0d8e1e0814cef85e5be5d12835cd01ca0b64f315556cf80d698b2675ffc21e1cadfd6ae80fd883fb6f3f600b440e987ebe9ee6fb5f205c5ee76042
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .idea
6
+ test/fixtures/sample_hg_repo
7
+ .DS_Store
@@ -0,0 +1 @@
1
+ ruby-2.0.0
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'rake'
4
+
5
+ # Specify your gem's dependencies in hg-blame-game.gemspec
6
+ gemspec
@@ -0,0 +1,20 @@
1
+ # hg-blame-game
2
+
3
+ [![Build Status](https://travis-ci.org/charleseff/hg-blame-game.png)](https://travis-ci.org/charleseff/hg-blame-game)
4
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/charleseff/hg-blame-game)
5
+
6
+ hg-blame-game is an interactive command-line tool that allows you to chain multiple 'hg blame' calls together in order to drill down to the actual responsible commit. When one `hg blame` is not enough.
7
+
8
+ ## Installation:
9
+
10
+ gem install hg-blame-game
11
+
12
+ ## Usage:
13
+
14
+ hg-blame-game --help
15
+
16
+ ## Examples:
17
+
18
+ [Happy Path](https://github.com/charleseff/hg-blame-game/blob/master/features/happy_path.feature)
19
+
20
+ [Other Scenarios](https://github.com/charleseff/hg-blame-game/blob/master/features/other_scenarios.feature)
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rubygems'
4
+ require 'cucumber'
5
+ require 'cucumber/rake/task'
6
+ require 'rspec/core/rake_task'
7
+
8
+ Cucumber::Rake::Task.new(:features)
9
+ RSpec::Core::RakeTask.new(:spec)
10
+
11
+ task :default => [:spec, :features]
data/TODO ADDED
@@ -0,0 +1,6 @@
1
+ - add special highlighting for in the 'hg show' for the line of code (or also the file) that has been chosen to chain into
2
+ - add screencast
3
+ - add ability to go back a step if you made a mistake
4
+ - (maybe) add support for one-file mode (maybe make it default too?)
5
+ - add pull request for aruba?
6
+ - add pull request for colorize (decolorize)
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + '/../lib') unless $:.include?(File.dirname(__FILE__) + '/../lib')
3
+ require 'hg-blame-game'
4
+
5
+ aliases = %w(blame praise annotate ann)
6
+
7
+ options = {}
8
+ OptionParser.new do |opts|
9
+ opts.banner = <<-END.gsub(/^[ \t]+/m, '')
10
+ Usage: hg-blame-game [options] path/to/filename
11
+ END
12
+
13
+ opts.separator ""
14
+ opts.separator "Options:"
15
+
16
+ opts.on("-r", "--rev", "--REV [REV]", String, "Set initial revision (defaults to tip)") do |rev|
17
+ options[:changeset_id] = rev
18
+ end
19
+
20
+ opts.on_tail("-h", "--help", "Show this message") do
21
+ puts opts
22
+ puts "\nAliases: " + aliases.map{|a| ["hg-#{a}-game", "hg #{a}-game"]}.flatten.join(", ")
23
+ exit
24
+ end
25
+
26
+ end.parse!
27
+
28
+ path_to_file = ARGV[0]
29
+ raise OptionParser::MissingArgument.new("You must specify a path to a file ") if not path_to_file
30
+
31
+ HgBlameGame.new(path_to_file, options).run
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + '/../lib') unless $:.include?(File.dirname(__FILE__) + '/../lib')
3
+ require 'hg-blame-game'
4
+
5
+ aliases = %w(blame praise annotate ann)
6
+
7
+ options = {}
8
+ OptionParser.new do |opts|
9
+ opts.banner = <<-END.gsub(/^[ \t]+/m, '')
10
+ Usage: hg-blame-game [options] path/to/filename
11
+ END
12
+
13
+ opts.separator ""
14
+ opts.separator "Options:"
15
+
16
+ opts.on("-r", "--rev", "--REV [REV]", String, "Set initial revision (defaults to tip)") do |rev|
17
+ options[:changeset_id] = rev
18
+ end
19
+
20
+ opts.on_tail("-h", "--help", "Show this message") do
21
+ puts opts
22
+ puts "\nAliases: " + aliases.map{|a| ["hg-#{a}-game", "hg #{a}-game"]}.flatten.join(", ")
23
+ exit
24
+ end
25
+
26
+ end.parse!
27
+
28
+ path_to_file = ARGV[0]
29
+ raise OptionParser::MissingArgument.new("You must specify a path to a file ") if not path_to_file
30
+
31
+ HgBlameGame.new(path_to_file, options).run
@@ -0,0 +1,147 @@
1
+ Feature: The happy path
2
+
3
+ Scenario: Happy path
4
+ Given I cd to "test/fixtures/sample_hg_repo"
5
+ When I run `../../../bin/hg-blame-game add.rb` interactively
6
+ Then I should see:
7
+ """
8
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:1: module Add
9
+ Danny Dover <developers+danny@foo.com> acb762f5f681:2: def add_4(y)
10
+ Danny Dover <developers+danny@foo.com> acb762f5f681:3: y + 5
11
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:4: end
12
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:5: end
13
+
14
+ (h for help) >
15
+ """
16
+ When I type "3"
17
+ Then I should see:
18
+ """
19
+ # HG changeset patch
20
+ # User Danny Dover <developers+danny@foo.com>
21
+ # Date 1326581406 28800
22
+ # Sat Jan 14 14:50:06 2012 -0800
23
+ # Node ID acb762f5f681d31b710f49f563ba8d1562b9e4c2
24
+ # Parent c8769d6446bc9adf5c0ad0394d87c31594f0cd2b
25
+ I like y's better
26
+
27
+ committer: Charles Finkel <charles.finkel@gmail.com>
28
+
29
+ diff -r c8769d6446bc -r acb762f5f681 add.rb
30
+ --- a/add.rb Sat Jan 14 14:49:00 2012 -0800
31
+ +++ b/add.rb Sat Jan 14 14:50:06 2012 -0800
32
+ @@ -1,5 +1,5 @@
33
+ module Add
34
+ - def add_4(x)
35
+ - x + 5
36
+ + def add_4(y)
37
+ + y + 5
38
+ end
39
+ end
40
+
41
+
42
+ 1) add.rb (or 's' for same)
43
+
44
+ (h for help) >
45
+ """
46
+ When I type "1"
47
+ Then I should see:
48
+ """
49
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:1: module Add
50
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:2: def add_4(x)
51
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:3: x + 5
52
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:4: end
53
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:5: end
54
+
55
+ (h for help) >
56
+ """
57
+ When I type "3"
58
+ Then I should see:
59
+ """
60
+ # HG changeset patch
61
+ # User Carmen Cummings <developers+carmen@foo.com>
62
+ # Date 1326581340 28800
63
+ # Sat Jan 14 14:49:00 2012 -0800
64
+ # Node ID c8769d6446bc9adf5c0ad0394d87c31594f0cd2b
65
+ # Parent c043b110cc46389037e207f814d251f4c7e00a7b
66
+ moving add_4 to module
67
+
68
+ committer: Charles Finkel <charles.finkel@gmail.com>
69
+
70
+ diff -r c043b110cc46 -r c8769d6446bc add.rb
71
+ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
72
+ +++ b/add.rb Sat Jan 14 14:49:00 2012 -0800
73
+ @@ -0,0 +1,5 @@
74
+ +module Add
75
+ + def add_4(x)
76
+ + x + 5
77
+ + end
78
+ +end
79
+
80
+ diff -r c043b110cc46 -r c8769d6446bc blah.rb
81
+ --- a/blah.rb Sat Jan 14 14:46:53 2012 -0800
82
+ +++ b/blah.rb Sat Jan 14 14:49:00 2012 -0800
83
+ @@ -1,5 +1,5 @@
84
+ -def add_4(x)
85
+ - x + 5
86
+ -end
87
+ +$:.unshift(File.dirname(__FILE__))
88
+ +require 'add'
89
+ +include Add
90
+
91
+ puts add_4(9) # should be 13
92
+
93
+
94
+ 1) blah.rb
95
+ 2) add.rb (or 's' for same)
96
+ """
97
+ When I type "1"
98
+ Then I should see:
99
+ """
100
+ Alice Amos <developers+alice@foo.com> a37def2620e7:1: def add_4(x)
101
+ Bob Barker <developers+bob@foo.com> c043b110cc46:2: x + 5
102
+ Alice Amos <developers+alice@foo.com> a37def2620e7:3: end
103
+ Alice Amos <developers+alice@foo.com> a37def2620e7:4:
104
+ Alice Amos <developers+alice@foo.com> a37def2620e7:5: puts add_4(9) # should be 13
105
+
106
+ (h for help) >
107
+ """
108
+ When I type "2"
109
+ Then I should see:
110
+ """
111
+ # HG changeset patch
112
+ # User Bob Barker <developers+bob@foo.com>
113
+ # Date 1326581213 28800
114
+ # Sat Jan 14 14:46:53 2012 -0800
115
+ # Node ID c043b110cc46389037e207f814d251f4c7e00a7b
116
+ # Parent a37def2620e7853b1c44aafc2b7ae528340c37aa
117
+ being bad
118
+
119
+ committer: Charles Finkel <charles.finkel@gmail.com>
120
+
121
+ diff -r a37def2620e7 -r c043b110cc46 blah.rb
122
+ --- a/blah.rb Sat Jan 14 14:46:18 2012 -0800
123
+ +++ b/blah.rb Sat Jan 14 14:46:53 2012 -0800
124
+ @@ -1,5 +1,5 @@
125
+ def add_4(x)
126
+ - x + 4
127
+ + x + 5
128
+ end
129
+
130
+ puts add_4(9) # should be 13
131
+
132
+
133
+ 1) blah.rb (or 's' for same)
134
+
135
+ (h for help) >
136
+ """
137
+ When I type "q"
138
+ Then I should see:
139
+ """
140
+ The responsible commit is:
141
+
142
+ changeset: 1:c043b110cc46
143
+ user: Bob Barker <developers+bob@foo.com>
144
+ date: Sat Jan 14 14:46:53 2012 -0800
145
+ summary: being bad
146
+ """
147
+
@@ -0,0 +1,253 @@
1
+ Feature: Other scenarios
2
+
3
+ Scenario: Getting help
4
+ When I run `bin/hg-blame-game --help`
5
+ Then it should pass with:
6
+ """
7
+ Usage: hg-blame-game [options] path/to/filename
8
+ """
9
+
10
+ Scenario: Without a filepath:
11
+ When I run `bin/hg-blame-game`
12
+ Then it should fail with:
13
+ """
14
+ missing argument: You must specify a path to a file
15
+ """
16
+
17
+ Scenario: Specifying a rep that doesn't exist:
18
+ When I run `bin/hg-blame-game file/that/doesnt/exist.rb`
19
+ Then it should fail with:
20
+ """
21
+ abort: no repository found in
22
+ """
23
+
24
+ Scenario: Invalid input on hg blame view:
25
+ Given I cd to "test/fixtures/sample_hg_repo"
26
+ When I run `../../../bin/hg-blame-game add.rb` interactively
27
+ And I type "foobar"
28
+ Then I should see:
29
+ """
30
+ Invalid input. Enter:
31
+ - the line number from the above list (from 1 to 5) you are hg blaming.
32
+ - the changeset_id to chain into.
33
+ - 'r' to re-view the hg blame
34
+
35
+ (h for help) >
36
+ """
37
+
38
+ Scenario: Invalid input on hg export view:
39
+ Given I cd to "test/fixtures/sample_hg_repo"
40
+ When I run `../../../bin/hg-blame-game add.rb` interactively
41
+ And I type "3"
42
+ And I type "blah"
43
+ Then I should see:
44
+ """
45
+ Invalid input. Enter:
46
+ - 'q' to quit, if you have found the offending commit
47
+ - the number from the above list (from 1 to 1) of the file to chain into.
48
+ - the filepath to chain into.
49
+ - 's' to chain into the 'same' file as before
50
+ - 'r' to re-view the hg export
51
+
52
+ (h for help) >
53
+ """
54
+
55
+ Scenario: With a SHA:
56
+ Given I cd to "test/fixtures/sample_hg_repo"
57
+ When I run `../../../bin/hg-blame-game blah.rb --rev=c043b110cc46` interactively
58
+ Then I should see:
59
+ """
60
+ Alice Amos <developers+alice@foo.com> a37def2620e7:1: def add_4(x)
61
+ Bob Barker <developers+bob@foo.com> c043b110cc46:2: x + 5
62
+ Alice Amos <developers+alice@foo.com> a37def2620e7:3: end
63
+ Alice Amos <developers+alice@foo.com> a37def2620e7:4:
64
+ Alice Amos <developers+alice@foo.com> a37def2620e7:5: puts add_4(9) # should be 13
65
+ """
66
+
67
+ Scenario: Entering the SHA instead of the number
68
+ Given I cd to "test/fixtures/sample_hg_repo"
69
+ When I run `../../../bin/hg-blame-game add.rb` interactively
70
+ When I type "acb762f5f681"
71
+ Then I should see:
72
+ """
73
+ # HG changeset patch
74
+ # User Danny Dover <developers+danny@foo.com>
75
+ # Date 1326581406 28800
76
+ # Sat Jan 14 14:50:06 2012 -0800
77
+ # Node ID acb762f5f681d31b710f49f563ba8d1562b9e4c2
78
+ # Parent c8769d6446bc9adf5c0ad0394d87c31594f0cd2b
79
+ I like y's better
80
+
81
+ committer: Charles Finkel <charles.finkel@gmail.com>
82
+
83
+ diff -r c8769d6446bc -r acb762f5f681 add.rb
84
+ --- a/add.rb Sat Jan 14 14:49:00 2012 -0800
85
+ +++ b/add.rb Sat Jan 14 14:50:06 2012 -0800
86
+ @@ -1,5 +1,5 @@
87
+ module Add
88
+ - def add_4(x)
89
+ - x + 5
90
+ + def add_4(y)
91
+ + y + 5
92
+ end
93
+ end
94
+
95
+ """
96
+
97
+ Scenario: Entering 's' for the same file to hg blame into
98
+ Given I cd to "test/fixtures/sample_hg_repo"
99
+ When I run `../../../bin/hg-blame-game add.rb` interactively
100
+ When I type "3"
101
+ Then I should see:
102
+ """
103
+ 1) add.rb
104
+ """
105
+ When I type "s"
106
+ Then I should see:
107
+ """
108
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:1: module Add
109
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:2: def add_4(x)
110
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:3: x + 5
111
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:4: end
112
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:5: end
113
+ """
114
+
115
+ Scenario: Entering the filename for the file to hg blame into:
116
+ Given I cd to "test/fixtures/sample_hg_repo"
117
+ When I run `../../../bin/hg-blame-game add.rb` interactively
118
+ When I type "3"
119
+ Then I should see:
120
+ """
121
+ 1) add.rb (or 's' for same)
122
+ """
123
+ When I type "add.rb"
124
+ Then I should see:
125
+ """
126
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:1: module Add
127
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:2: def add_4(x)
128
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:3: x + 5
129
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:4: end
130
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:5: end
131
+ """
132
+
133
+ Scenario: Re-viewing a hg blame:
134
+ Given I cd to "test/fixtures/sample_hg_repo"
135
+ When I run `../../../bin/hg-blame-game add.rb` interactively
136
+ Then I should see:
137
+ """
138
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:1: module Add
139
+ Danny Dover <developers+danny@foo.com> acb762f5f681:2: def add_4(y)
140
+ Danny Dover <developers+danny@foo.com> acb762f5f681:3: y + 5
141
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:4: end
142
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:5: end
143
+ """
144
+ When I type "r"
145
+ Then I should see:
146
+ """
147
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:1: module Add
148
+ Danny Dover <developers+danny@foo.com> acb762f5f681:2: def add_4(y)
149
+ Danny Dover <developers+danny@foo.com> acb762f5f681:3: y + 5
150
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:4: end
151
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:5: end
152
+ """
153
+
154
+ Scenario: Re-viewing a hg export:
155
+ Given I cd to "test/fixtures/sample_hg_repo"
156
+ When I run `../../../bin/hg-blame-game add.rb` interactively
157
+ And I type "3"
158
+ Then I should see:
159
+ """
160
+ HG changeset patch
161
+ # User Danny Dover <developers+danny@foo.com>
162
+ # Date 1326581406 28800
163
+ # Sat Jan 14 14:50:06 2012 -0800
164
+ # Node ID acb762f5f681d31b710f49f563ba8d1562b9e4c2
165
+ # Parent c8769d6446bc9adf5c0ad0394d87c31594f0cd2b
166
+ I like y's better
167
+
168
+ committer: Charles Finkel <charles.finkel@gmail.com>
169
+
170
+ diff -r c8769d6446bc -r acb762f5f681 add.rb
171
+ --- a/add.rb Sat Jan 14 14:49:00 2012 -0800
172
+ +++ b/add.rb Sat Jan 14 14:50:06 2012 -0800
173
+ @@ -1,5 +1,5 @@
174
+ module Add
175
+ - def add_4(x)
176
+ - x + 5
177
+ + def add_4(y)
178
+ + y + 5
179
+ end
180
+ end
181
+
182
+
183
+ 1) add.rb (or 's' for same)
184
+ """
185
+ When I type "r"
186
+ Then I should see:
187
+ """
188
+ HG changeset patch
189
+ # User Danny Dover <developers+danny@foo.com>
190
+ # Date 1326581406 28800
191
+ # Sat Jan 14 14:50:06 2012 -0800
192
+ # Node ID acb762f5f681d31b710f49f563ba8d1562b9e4c2
193
+ # Parent c8769d6446bc9adf5c0ad0394d87c31594f0cd2b
194
+ I like y's better
195
+
196
+ committer: Charles Finkel <charles.finkel@gmail.com>
197
+
198
+ diff -r c8769d6446bc -r acb762f5f681 add.rb
199
+ --- a/add.rb Sat Jan 14 14:49:00 2012 -0800
200
+ +++ b/add.rb Sat Jan 14 14:50:06 2012 -0800
201
+ @@ -1,5 +1,5 @@
202
+ module Add
203
+ - def add_4(x)
204
+ - x + 5
205
+ + def add_4(y)
206
+ + y + 5
207
+ end
208
+ end
209
+
210
+
211
+ 1) add.rb (or 's' for same)
212
+ """
213
+
214
+ Scenario: Getting help interactively for git blame:
215
+ Given I cd to "test/fixtures/sample_hg_repo"
216
+ When I run `../../../bin/hg-blame-game add.rb` interactively
217
+ Then I should see:
218
+ """
219
+ (h for help) >
220
+ """
221
+ When I type "h"
222
+ Then I should see:
223
+ """
224
+ Enter:
225
+ - the line number from the above list (from 1 to 5) you are hg blaming.
226
+ - the changeset_id to chain into.
227
+ - 'r' to re-view the hg blame
228
+
229
+ (h for help) >
230
+ """
231
+
232
+ Scenario: Getting help interactively for hg export:
233
+ Given I cd to "test/fixtures/sample_hg_repo"
234
+ When I run `../../../bin/hg-blame-game add.rb` interactively
235
+ And I type "3"
236
+ Then I should see:
237
+ """
238
+ (h for help) >
239
+ """
240
+ When I type "h"
241
+ Then I should see:
242
+ """
243
+ Enter:
244
+ - 'q' to quit, if you have found the offending commit
245
+ - the number from the above list (from 1 to 1) of the file to chain into.
246
+ - the filepath to chain into.
247
+ - 's' to chain into the 'same' file as before
248
+ - 'r' to re-view the hg export
249
+
250
+ (h for help) >
251
+ """
252
+
253
+
@@ -0,0 +1,26 @@
1
+ World(ArubaExt)
2
+
3
+ Then /^the output should contain, ignoring spaces:$/ do |expected|
4
+ assert_partial_output(expected.gsub("\s", ''), all_output.gsub("\s", ''))
5
+ end
6
+
7
+ Then /^I should see:$/ do |expected|
8
+ assert_seen(expected)
9
+ end
10
+
11
+ Then /^the next bit of output should contain, ignoring spaces:$/ do |expected|
12
+ assert_seen(expected)
13
+ end
14
+
15
+ def assert_seen(expected)
16
+ @seen_output ||= ''
17
+ expected = unescape(expected.gsub("\s", ''))
18
+ wait_until_expectation do
19
+ @all_output = only_processes[0].output
20
+
21
+ actual = unescape(@all_output[@seen_output.size..-1].gsub("\s", ''))
22
+
23
+ actual.should include(expected)
24
+ end
25
+ @seen_output = @all_output
26
+ end
@@ -0,0 +1,41 @@
1
+ # extension methods for Aruba
2
+ module ArubaExt
3
+
4
+ def wait_until(seconds = 5)
5
+ timeout(seconds) { yield }
6
+ end
7
+
8
+ def timeout(seconds = 50, &block)
9
+ start_time = Time.now
10
+
11
+ result = nil
12
+
13
+ until result
14
+ return result if result = yield
15
+
16
+ delay = seconds - (Time.now - start_time)
17
+ if delay <= 0
18
+ raise TimeoutError, "timed out"
19
+ end
20
+
21
+ sleep(0.05)
22
+ end
23
+ end
24
+
25
+ def wait_until_expectation
26
+ begin
27
+ exception = nil
28
+ wait_until do
29
+ begin
30
+ yield
31
+ true
32
+ rescue RSpec::Expectations::ExpectationNotMetError => e
33
+ exception = e
34
+ false
35
+ end
36
+ end
37
+ rescue TimeoutError
38
+ raise exception
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ require 'aruba/cucumber'
2
+
3
+ def unzip_repo_if_needed!
4
+ fixtures_dir = File.expand_path(File.dirname(__FILE__)) + '/../../test/fixtures'
5
+ unless File.directory? fixtures_dir + '/sample_hg_repo'
6
+ `unzip #{fixtures_dir}/sample_hg_repo.zip -d #{fixtures_dir}`
7
+ end
8
+ end
9
+
10
+ # for some reason Aruba sets the default dir to 'tmp/aruba'. Override this:
11
+ def set_relative_dir_for_aruba!
12
+ root_dir = File.expand_path("../../../", __FILE__)
13
+ self.instance_variable_set('@dirs', [root_dir])
14
+ end
15
+
16
+ Before do
17
+ unzip_repo_if_needed!
18
+ set_relative_dir_for_aruba!
19
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "hg-blame-game/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "hg-blame-game"
7
+ s.version = Hg::Blame::Game::VERSION
8
+ s.authors = ["Charles Finkel"]
9
+ s.email = ["charles.finkel@gmail.com"]
10
+ s.homepage = "https://github.com/charleseff/hg-blame-game-ruby"
11
+ s.summary = %q{hg-blame-game is an interactive command for chaining 'hg blame' calls to get to the real culprit for the line of code you care about, when one `hg blame` does not tell the whole story.}
12
+ s.description = %q{When one `hg blame` is not enough}
13
+
14
+ s.rubyforge_project = "hg-blame-game"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency 'aruba'
22
+ s.add_development_dependency 'rspec'
23
+ s.add_runtime_dependency 'colorize'
24
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'colorize'
3
+ require 'optparse'
4
+ require 'hg-blame-game/hg_blame_game'
@@ -0,0 +1,146 @@
1
+ class HgBlameGame
2
+ def initialize(path_to_file, opts={})
3
+ @path_to_file = path_to_file
4
+ @changeset_id = !opts[:changeset_id].nil? ? opts[:changeset_id] : 'tip'
5
+ end
6
+
7
+ def run
8
+ loop do
9
+ p_flush("\n")
10
+
11
+ changeset_id_to_show = show_hg_blame_and_prompt_for_changeset_id
12
+
13
+ p_flush("\n")
14
+
15
+ files_changed = sys("hg status --change #{changeset_id_to_show}").split("\n").map { |g| g.split(' ').last }
16
+
17
+ @path_to_file = prompt_for_file(files_changed, changeset_id_to_show)
18
+ @changeset_id = "#{changeset_id_to_show}^"
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def sys(cmd)
25
+ puts "Executing: #{cmd}" if ENV['DEBUG']
26
+ `#{cmd}`
27
+ end
28
+
29
+ def show_hg_blame_and_prompt_for_changeset_id
30
+ hg_blame_out = sys hg_blame_cmd
31
+ exit $?.exitstatus unless $?.success?
32
+ changeset_id_list = get_changeset_id_list(hg_blame_out)
33
+ print_hg_blame_and_prompt
34
+ prompt_for_changeset_id(changeset_id_list)
35
+ end
36
+
37
+ def prompt_for_changeset_id(changeset_ids)
38
+ loop do
39
+ input = $stdin.gets.strip
40
+ # changeset_id was entered, return it:
41
+ return input if changeset_ids.include? input
42
+
43
+ if input =~ /\A\d+\Z/
44
+ input = input.to_i
45
+ return changeset_ids[input - 1] if input <= changeset_ids.count && input >= 1
46
+ end
47
+
48
+ if input == 'r'
49
+ print_hg_blame_and_prompt
50
+ elsif input == 'h'
51
+ p_flush prompt_for_changeset_id_message(changeset_ids.count)
52
+ else
53
+ p_flush "\nInvalid input. " + prompt_for_changeset_id_message(changeset_ids.count)
54
+ end
55
+ end
56
+ end
57
+
58
+ def print_hg_blame_and_prompt
59
+ system hg_blame_cmd
60
+ p_flush "\n" + simple_prompt
61
+ end
62
+
63
+ def hg_blame_cmd
64
+ "hg blame --rev #{@changeset_id} --verbose --user --line-number --changeset #{@path_to_file}"
65
+ end
66
+
67
+ HG_BLAME_REGEX = / ([^\: ]+):/
68
+
69
+ def get_changeset_id_list(hg_blame_out)
70
+ hg_blame_out.strip.split("\n").map { |line| line[HG_BLAME_REGEX, 1] }
71
+ end
72
+
73
+ def prompt_for_file(files_changed, changeset_id)
74
+ print_file_prompt(files_changed, changeset_id)
75
+
76
+ loop do
77
+ input = $stdin.gets.strip
78
+ if input == 'q'
79
+ p_flush "\n" + color("The responsible commit is:") + "\n\n"
80
+ system "hg log --rev #{changeset_id}"
81
+ exit 0
82
+ end
83
+ return @path_to_file if input == 's'
84
+ return input if files_changed.include? input
85
+
86
+ if input =~ /\A\d+\Z/
87
+ input = input.to_i
88
+ return files_changed[input-1] if input >= 1 && input <= files_changed.count
89
+ end
90
+
91
+ if input == 'r'
92
+ print_file_prompt(files_changed, changeset_id)
93
+ elsif input == 'h'
94
+ p_flush prompt_for_file_message(files_changed.count)
95
+ else
96
+ p_flush "\nInvalid input. " + prompt_for_file_message(files_changed.count)
97
+ end
98
+ end
99
+ end
100
+
101
+ def print_file_prompt(files, changeset_id)
102
+ system "hg export #{changeset_id}"
103
+ print("\n")
104
+ files.each_with_index do |file, index|
105
+ line = sprintf("%3d) #{file}", index+1)
106
+ line += ' ' + orange_color("(or 's' for same)") if file == @path_to_file
107
+ print line + "\n"
108
+ end
109
+ p_flush "\n" + simple_prompt
110
+ end
111
+
112
+ def prompt_for_file_message(count)
113
+ "Enter:\n" +
114
+ " - 'q' to quit, if you have found the offending commit\n" +
115
+ " - the number from the above list (from 1 to #{count}) of the file to chain into.\n" +
116
+ " - the filepath to chain into.\n" +
117
+ " - 's' to chain into the 'same' file as before\n" +
118
+ " - 'r' to re-view the hg export\n\n" +
119
+ simple_prompt
120
+ end
121
+
122
+ def simple_prompt
123
+ color("(h for help) >") + ' '
124
+ end
125
+
126
+ def prompt_for_changeset_id_message(count)
127
+ "Enter:\n" +
128
+ " - the line number from the above list (from 1 to #{count}) you are hg blaming.\n" +
129
+ " - the changeset_id to chain into.\n" +
130
+ " - 'r' to re-view the hg blame\n\n" + simple_prompt
131
+ end
132
+
133
+ def p_flush(str)
134
+ $stdout.print str
135
+ $stdout.flush
136
+ end
137
+
138
+ def color(s)
139
+ s.colorize(:color => :light_white, :background => :magenta)
140
+ end
141
+
142
+ def orange_color(s)
143
+ s.colorize(:color => :yellow, :background => :black)
144
+ end
145
+
146
+ end
@@ -0,0 +1,7 @@
1
+ module Hg
2
+ module Blame
3
+ module Game
4
+ VERSION = "0.1.1"
5
+ end
6
+ end
7
+ end
Binary file
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe HgBlameGame do
4
+
5
+ let(:hg_blame_game) { HgBlameGame.new('some_file') }
6
+ let(:changeset_id_list) { %w(c8769d6446bc acb762f5f681 acb762f5f681 c8769d6446bc c8769d6446bc) }
7
+
8
+ before do
9
+ $stdout.stub(:print)
10
+ end
11
+
12
+ describe "#get_changeset_id_list" do
13
+ let(:hg_blame_out) {
14
+ <<-END.gsub(/^[ \t]+/m, '')
15
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:1: module Add
16
+ Danny Dover <developers+danny@foo.com> acb762f5f681:2: def add_4(y)
17
+ Danny Dover <developers+danny@foo.com> acb762f5f681:3: y + 5
18
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:4: end
19
+ Carmen Cummings <developers+carmen@foo.com> c8769d6446bc:5: end
20
+ END
21
+ }
22
+
23
+ it "should return a list of changeset_ids" do
24
+ hg_blame_game.send(:get_changeset_id_list,hg_blame_out).should == changeset_id_list
25
+ end
26
+ end
27
+
28
+ describe "prompt_for_changeset_id" do
29
+ before do
30
+ $stdin.should_receive(:gets).and_return(input)
31
+ end
32
+ context "when user enters a correct changeset_id" do
33
+ let(:input) { 'c8769d6446bc' }
34
+ it "should return the correct changeset_id" do
35
+ hg_blame_game.send(:prompt_for_changeset_id, changeset_id_list).should == 'c8769d6446bc'
36
+ end
37
+ end
38
+ context "when user enters a correct number" do
39
+ let(:input) { '1' }
40
+ it "should return the correct changeset_id" do
41
+ hg_blame_game.send(:prompt_for_changeset_id, changeset_id_list).should == 'c8769d6446bc'
42
+ end
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1 @@
1
+ require 'hg-blame-game'
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hg-blame-game
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Charles Finkel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aruba
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: colorize
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: When one `hg blame` is not enough
56
+ email:
57
+ - charles.finkel@gmail.com
58
+ executables:
59
+ - hg-annotate-game
60
+ - hg-blame-game
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - .gitignore
65
+ - .ruby-version
66
+ - .travis.yml
67
+ - Gemfile
68
+ - README.md
69
+ - Rakefile
70
+ - TODO
71
+ - bin/hg-annotate-game
72
+ - bin/hg-blame-game
73
+ - features/happy_path.feature
74
+ - features/other_scenarios.feature
75
+ - features/step_definitions/gbc_steps.rb
76
+ - features/support/aruba_ext.rb
77
+ - features/support/env.rb
78
+ - hg-blame-game.gemspec
79
+ - lib/hg-blame-game.rb
80
+ - lib/hg-blame-game/hg_blame_game.rb
81
+ - lib/hg-blame-game/version.rb
82
+ - public/pensive-kanye.png
83
+ - spec/hg-blame-game/hg_blame_game_spec.rb
84
+ - spec/spec_helper.rb
85
+ - test/fixtures/sample_hg_repo.zip
86
+ homepage: https://github.com/charleseff/hg-blame-game-ruby
87
+ licenses: []
88
+ metadata: {}
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubyforge_project: hg-blame-game
105
+ rubygems_version: 2.0.5
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: hg-blame-game is an interactive command for chaining 'hg blame' calls to
109
+ get to the real culprit for the line of code you care about, when one `hg blame`
110
+ does not tell the whole story.
111
+ test_files:
112
+ - features/happy_path.feature
113
+ - features/other_scenarios.feature
114
+ - features/step_definitions/gbc_steps.rb
115
+ - features/support/aruba_ext.rb
116
+ - features/support/env.rb
117
+ - spec/hg-blame-game/hg_blame_game_spec.rb
118
+ - spec/spec_helper.rb
119
+ - test/fixtures/sample_hg_repo.zip