hg-blame-game 0.1.1
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.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +6 -0
- data/README.md +20 -0
- data/Rakefile +11 -0
- data/TODO +6 -0
- data/bin/hg-annotate-game +31 -0
- data/bin/hg-blame-game +31 -0
- data/features/happy_path.feature +147 -0
- data/features/other_scenarios.feature +253 -0
- data/features/step_definitions/gbc_steps.rb +26 -0
- data/features/support/aruba_ext.rb +41 -0
- data/features/support/env.rb +19 -0
- data/hg-blame-game.gemspec +24 -0
- data/lib/hg-blame-game.rb +4 -0
- data/lib/hg-blame-game/hg_blame_game.rb +146 -0
- data/lib/hg-blame-game/version.rb +7 -0
- data/public/pensive-kanye.png +0 -0
- data/spec/hg-blame-game/hg_blame_game_spec.rb +46 -0
- data/spec/spec_helper.rb +1 -0
- data/test/fixtures/sample_hg_repo.zip +0 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.0.0
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# hg-blame-game
|
2
|
+
|
3
|
+
[](https://travis-ci.org/charleseff/hg-blame-game)
|
4
|
+
[](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)
|
data/Rakefile
ADDED
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
|
data/bin/hg-blame-game
ADDED
@@ -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,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
|
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
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'hg-blame-game'
|
Binary file
|
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
|