hg-blame-game 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![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)
|
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
|