git-conflict-blame 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a86c8e97cedc6efef883350c97235a1022bdcd5e
4
- data.tar.gz: fce9caad34072ce10ccfb6fcf3409a8f3cc996f5
3
+ metadata.gz: 0546d98781bc0da7566751e77e34d01ec9b00d3b
4
+ data.tar.gz: 9d75e270618d36a51db1d23879e075602dab13a4
5
5
  SHA512:
6
- metadata.gz: 0c2046509bf36923cd590742157f28da0ee731e48cb437fc791be52aebd14a6ee88ca2ca264b6db3fb76689b33b0a57a8d68eadeb97b19d502a04a4a351e1562
7
- data.tar.gz: aab146a0103c4eee5d0b2e49bca2e3bc89b5215a6f621a7faf2ba6b87b7bfd5afd3650a1063a678eb8fed925462928585683ba1686cd7e98088ebf038e9c271b
6
+ metadata.gz: d5cffc2bfaf1b0d5fd72814a806fb12ab664ee62d7f0ad310ec94410a9234043af3b7cf0471592486555dce7419a095c4c0447d25bc8a753657cc753369c7195
7
+ data.tar.gz: 07f0af656c3e8f4bd333c1b2f22128ba84d0ad892d26a5a99761a56fe0b54a43586e9437bc91f96b904751987270a09a1d967518d0a86963a3005197cc97f7ee
@@ -9,13 +9,14 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
9
  copies of the Software, and to permit persons to whom the Software is
10
10
  furnished to do so, subject to the following conditions:
11
11
 
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
14
 
15
15
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
16
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
17
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md CHANGED
@@ -1,41 +1,46 @@
1
- # Git::Conflict::Blame
1
+ # Git Conflict Blame
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/git/conflict/blame`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ Git command that shows the blame on the lines that are in conflict. This should be ran
4
+ after a `git merge` command has been ran and there are files that are in conflict.
6
5
 
7
6
  ## Installation
8
7
 
9
- Add this line to your application's Gemfile:
8
+ This gem depends on [Rugged](http://www.rubydoc.info/gems/rugged), which requires
9
+ certain dependencies installed. Make sure you have `cmake`, `make`, or `gmake` installed
10
+ on your system.
10
11
 
11
- ```ruby
12
- gem 'git-conflict-blame'
12
+ ```bash
13
+ gem install git-conflict-blame
13
14
  ```
14
15
 
15
- And then execute:
16
-
17
- $ bundle
16
+ ## Usage
18
17
 
19
- Or install it yourself as:
18
+ Run this inside a directory of your git repository. If there are no conflicts,
19
+ nothing will be displayed.
20
20
 
21
- $ gem install git-conflict-blame
21
+ To output in a colorized pretty format:
22
22
 
23
- ## Usage
23
+ ```bash
24
+ git conflict-blame
25
+ ```
24
26
 
25
- TODO: Write usage instructions here
27
+ To output machine-readable data:
26
28
 
27
- ## Development
29
+ ```bash
30
+ git conflict-blame --json
31
+ ```
28
32
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
33
+ To output pretty machine-readable data:
30
34
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
35
+ ```bash
36
+ git conflict-blame --json --pretty
37
+ ```
32
38
 
33
39
  ## Contributing
34
40
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/git-conflict-blame.
41
+ Bug reports and pull requests are welcome on GitHub at https://github.com/eterry1388/git-conflict-blame.
36
42
 
37
43
 
38
44
  ## License
39
45
 
40
46
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
-
@@ -1,72 +1,34 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # This needs a lot of work. It was VERY quickly slapped together!
3
+ require 'git-conflict-blame'
4
4
 
5
- require 'git'
6
- require 'colorize'
7
-
8
- begin
9
- git = Git.open( '.' )
10
- status = git.status
11
- changed = status.changed
12
- unmerged = changed.select { |filename, data| data.stage.to_i == 3 }
13
- rescue Exception => e
14
- puts 'Could not find valid GIT repo!'
15
- exit 1
5
+ def show_version
6
+ require 'git-conflict-blame/version'
7
+ puts "git-conflict-blame #{GitConflictBlame::VERSION}"
16
8
  end
17
9
 
18
- data = {}
19
- unmerged.keys.each do |filename|
20
- raw = `git blame --show-email -c --date short #{filename}`
21
-
22
- start_indexes = []
23
- end_indexes = []
24
- lines = raw.split( "\n" )
25
- lines.each do |line|
26
- start_indexes << lines.index( line ) if line.include?( '<<<<<<<' )
27
- end_indexes << lines.index( line ) if line.include?( '>>>>>>>' )
28
- end
29
-
30
- index = 0
31
- all_lines = []
32
- start_indexes.count.times do
33
- start_index = start_indexes[index]
34
- end_index = end_indexes[index]
35
- all_lines << lines[start_index..end_index]
36
- index += 1
37
- end
38
-
39
- data[filename] = all_lines
10
+ show_help = ARGV.include?( '--help' ) || ARGV.include?( '-h' )
11
+ if show_help
12
+ show_version
13
+ puts
14
+ puts 'Git command that shows the blame on the lines that are in conflict.'
15
+ puts 'This should be ran after a "git merge" command has been ran and there'
16
+ puts 'are files that are in conflict.'
17
+ puts
18
+ puts 'Usage: git conflict-blame [options]'
19
+ puts "\t -j, --json \t Output machine-readable data"
20
+ puts "\t -p, --pretty \t Output in pretty format (only valid if using '--json')"
21
+ puts "\t -v, --version \t Show the version of this tool"
22
+ puts "\t -h, --help \t Show this message"
23
+ exit 0
40
24
  end
41
25
 
42
- data.delete_if { |_, all_lines| all_lines.nil? || all_lines.empty? }
43
-
44
- data.each do |filename, conflicts|
45
- puts filename.green
46
- conflicts.each do |lines|
47
- lines.each do |line|
48
- line_array = line.split( "\t" )
49
-
50
- commit_id = line_array[0]
51
- email = line_array[1].delete( "(" )[0..30]
52
- date = line_array[2]
26
+ if ARGV.include?( '--version' ) || ARGV.include?( '-v' )
27
+ show_version
28
+ exit 0
29
+ end
53
30
 
54
- git_info = [commit_id, email, date]
55
- raw_line = line_array[3..999].join( "\t" )
56
- raw_line_array = raw_line.split( ")" )
57
- line_number = raw_line_array.first
58
- raw_line_string = raw_line_array[1..999].join( ")" )
31
+ json = ARGV.include?( '--json' ) || ARGV.include?( '-j' )
32
+ pretty_json = ARGV.include?( '--pretty' ) || ARGV.include?( '-p' )
59
33
 
60
- formatted_git_info = "\t%-10s %-30s %-13s" % git_info
61
- if raw_line.include?( '<<<<<<<' ) || raw_line.include?( '>>>>>>>' )
62
- raw_line_string = raw_line_string.red
63
- elsif raw_line.include?( '=======' )
64
- raw_line_string = raw_line_string.yellow
65
- else
66
- raw_line_string = raw_line_string.light_blue
67
- end
68
- puts "%s [%-22s] %-s" % [formatted_git_info, line_number.bold, raw_line_string]
69
- puts if raw_line.include?( '>>>>>>>' )
70
- end
71
- end
72
- end
34
+ GitConflictBlame.run( json: json, pretty_json: pretty_json )
@@ -20,8 +20,9 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency 'bundler', '~> 1.10'
22
22
  spec.add_development_dependency 'rake', '~> 10.0'
23
- spec.add_development_dependency 'rspec'
23
+ spec.add_development_dependency 'rspec', '~> 3.3'
24
24
 
25
- spec.add_dependency 'git'
26
- spec.add_dependency 'colorize'
25
+ spec.add_dependency 'rugged', '~> 0.23'
26
+ spec.add_dependency 'colorize', '~> 0.7'
27
+ spec.add_dependency 'nesty', '~> 1.0'
27
28
  end
@@ -0,0 +1,4 @@
1
+ require 'nesty'
2
+
3
+ GitError = Class.new( Nesty::NestedStandardError )
4
+ CmdError = Class.new( Nesty::NestedStandardError )
@@ -1,3 +1,3 @@
1
- module GitConflictBlame
2
- VERSION = '0.1.0'
1
+ class GitConflictBlame
2
+ VERSION = '0.2.0'
3
3
  end
@@ -1,4 +1,227 @@
1
- require 'git-conflict-blame/version'
1
+ require 'git-conflict-blame/exceptions'
2
+ require 'rugged'
3
+ require 'colorize'
4
+ require 'open3'
5
+ require 'json'
2
6
 
3
- module GitConflictBlame
7
+ # Git command that shows the blame on the lines that are in conflict. This should be ran
8
+ # after a "git merge" command has been ran and there are files that are in conflict.
9
+ class GitConflictBlame
10
+
11
+ # @param json [Boolean] Whether or not to output in JSON format
12
+ # @param pretty_json [Boolean] Whether or not to output in pretty JSON format
13
+ def initialize( json: false, pretty_json: false )
14
+ @json = json
15
+ @pretty_json = pretty_json
16
+ @current_dir = Dir.pwd
17
+ @repo = Rugged::Repository.discover( @current_dir )
18
+ end
19
+
20
+ # Main public method to run on the class
21
+ #
22
+ # @param options [Hash] Run options
23
+ def self.run( options )
24
+ new( options ).run!
25
+ end
26
+
27
+ # Actually performs the conflict blame
28
+ def run!
29
+ Dir.chdir( @repo.workdir )
30
+ raise GitError, 'No conflicts found' unless conflicts?
31
+ log "#{conflicts.count} conflicts found!".red
32
+ log "Parsing files to find out who is to blame..."
33
+ data = find_conflict_blames
34
+ if @json
35
+ json_data = {
36
+ exception: false,
37
+ count: conflicts.count,
38
+ data: data
39
+ }
40
+ output_json( json_data )
41
+ else
42
+ display_results( data )
43
+ end
44
+ rescue GitError => e
45
+ log_error( e )
46
+ exit 1
47
+ rescue CmdError => e
48
+ message = "This is probably a bug. Please log it here:\n"
49
+ message << "https://github.com/eterry1388/git-conflict-blame/issues"
50
+ log_error( e, message: message )
51
+ exit 1
52
+ rescue Exception => e
53
+ message = "#{e.backtrace.join( "\n" )}\n"
54
+ message << "Unhandled exception! This is probably a bug. Please log it here:\n"
55
+ message << "https://github.com/eterry1388/git-conflict-blame/issues"
56
+ log_error( e, message: message )
57
+ exit 1
58
+ ensure
59
+ Dir.chdir( @current_dir )
60
+ end
61
+
62
+ private
63
+
64
+ # Outputs a message
65
+ #
66
+ # @note This will not output anything if @json is set!
67
+ # @param message [String]
68
+ def log( message = '' )
69
+ return if @json
70
+ puts message
71
+ end
72
+
73
+ # Outputs an error message
74
+ #
75
+ # @param e [Exception]
76
+ # @param message [String] Error message
77
+ def log_error( e, message: )
78
+ if @json
79
+ message = "#{e.message}\n#{message}"
80
+ error_hash = {
81
+ exception: true,
82
+ type: e.class,
83
+ message: message
84
+ }
85
+ output_json( error_hash )
86
+ else
87
+ puts "#{e.class}: #{e.message}"
88
+ puts message if message
89
+ end
90
+ end
91
+
92
+ # Run a command
93
+ #
94
+ # @param command [String] the command to run
95
+ # @raise [CmdError] if command is not successful
96
+ # @return [String] the output (stdout) of running the command
97
+ def cmd( command )
98
+ stdout, stderr, status = Open3.capture3( command )
99
+ unless status.success?
100
+ raise CmdError, "Command: '#{command}', Stdout: '#{stdout}', Stderr: '#{stderr}'"
101
+ end
102
+ stdout
103
+ rescue Errno::ENOENT => e
104
+ raise CmdError, "Command: '#{command}', Error: '#{e}'"
105
+ end
106
+
107
+ # Figures out if there are any conflicts in the git repo
108
+ #
109
+ # @return [Boolean]
110
+ def conflicts?
111
+ @repo.index.conflicts?
112
+ end
113
+
114
+ # Gets all the filenames that are in conflict
115
+ #
116
+ # @return [Array<String>]
117
+ def conflicts
118
+ @conflicts ||= @repo.index.conflicts.map { |conflict| conflict[:ours][:path] }
119
+ end
120
+
121
+ # Runs the "git blame" command
122
+ #
123
+ # @param filename [String]
124
+ # @return [String] Raw results of the "git blame" command
125
+ def raw_blame( filename )
126
+ cmd( "git blame --show-email -c --date short #{filename}" )
127
+ end
128
+
129
+ # Parses through the conflicted files and finds the blame on each line
130
+ #
131
+ # @return [Hash]
132
+ def find_conflict_blames
133
+ data = {}
134
+ conflicts.each do |conflict|
135
+ raw = raw_blame( conflict )
136
+ start_indexes = []
137
+ end_indexes = []
138
+ lines = raw.split( "\n" )
139
+ lines.each do |line|
140
+ start_indexes << lines.index( line ) if line.include?( '<<<<<<<' )
141
+ end_indexes << lines.index( line ) if line.include?( '>>>>>>>' )
142
+ end
143
+
144
+ index = 0
145
+ all_line_sets = []
146
+ start_indexes.count.times do
147
+ start_index = start_indexes[index]
148
+ end_index = end_indexes[index]
149
+ line_set = lines[start_index..end_index]
150
+ all_line_sets << parse_lines( line_set )
151
+ index += 1
152
+ end
153
+ data[conflict] = all_line_sets
154
+ end
155
+ data.delete_if { |_, all_line_sets| all_line_sets.nil? || all_line_sets.empty? }
156
+ data
157
+ end
158
+
159
+ # Iterates through a line set and parses them
160
+ #
161
+ # @param lines [Array<String>]
162
+ # @return [Array<Hash>]
163
+ def parse_lines( lines )
164
+ lines.map { |line| parse_line( line ) }
165
+ end
166
+
167
+ # Parses a line from the "git blame" command
168
+ #
169
+ # @param line [String]
170
+ # @return [Hash]
171
+ def parse_line( line )
172
+ line_array = line.split( "\t" )
173
+ commit_id = line_array[0]
174
+ email = line_array[1].delete( '(' ).delete( '<' ).delete( '>' )
175
+ date = line_array[2]
176
+ raw_line = line_array[3..999].join( "\t" )
177
+ raw_line_array = raw_line.split( ')' )
178
+ line_number = raw_line_array[0].to_i
179
+ line_content = raw_line_array[1..999].join( ')' )
180
+
181
+ {
182
+ commit_id: commit_id,
183
+ email: email,
184
+ date: date,
185
+ line_number: line_number,
186
+ line_content: line_content
187
+ }
188
+ end
189
+
190
+ # Outputs a Hash in JSON format
191
+ #
192
+ # @param hash [Hash]
193
+ def output_json( hash )
194
+ if @pretty_json
195
+ puts JSON.pretty_generate( hash )
196
+ else
197
+ puts hash.to_json
198
+ end
199
+ end
200
+
201
+ # Outputs the results of {#find_conflict_blames} to the console
202
+ #
203
+ # @param data [Hash]
204
+ def display_results( data )
205
+ data.each do |filename, conflicts|
206
+ log filename.green
207
+ conflicts.each do |lines|
208
+ lines.each do |line|
209
+ git_info = [line[:commit_id], line[:email][0..30], line[:date]]
210
+ formatted_git_info = "\t%-10s %-30s %-13s" % git_info
211
+
212
+ if line[:line_content].include?( '<<<<<<<' ) || line[:line_content].include?( '>>>>>>>' )
213
+ line_color = :red
214
+ elsif line[:line_content].include?( '=======' )
215
+ line_color = :yellow
216
+ else
217
+ line_color = :light_blue
218
+ end
219
+
220
+ line_data = [formatted_git_info, line[:line_number].to_s.bold, line[:line_content].colorize( line_color )]
221
+ log "%s [%-20s] %-s" % line_data
222
+ end
223
+ log # New line
224
+ end
225
+ end
226
+ end
4
227
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-conflict-blame
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Terry
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-14 00:00:00.000000000 Z
11
+ date: 2015-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -42,44 +42,58 @@ dependencies:
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '3.3'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '3.3'
55
55
  - !ruby/object:Gem::Dependency
56
- name: git
56
+ name: rugged
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '0.23'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '0.23'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: colorize
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: '0.7'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: nesty
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: '0'
96
+ version: '1.0'
83
97
  description: Git command that shows the blame on the lines that are in conflict
84
98
  email:
85
99
  - eterry1388@aol.com
@@ -93,7 +107,7 @@ files:
93
107
  - ".gitignore"
94
108
  - ".rspec"
95
109
  - Gemfile
96
- - LICENSE.txt
110
+ - LICENSE
97
111
  - README.md
98
112
  - Rakefile
99
113
  - bin/console
@@ -101,6 +115,7 @@ files:
101
115
  - bin/setup
102
116
  - git-conflict-blame.gemspec
103
117
  - lib/git-conflict-blame.rb
118
+ - lib/git-conflict-blame/exceptions.rb
104
119
  - lib/git-conflict-blame/version.rb
105
120
  homepage: https://github.com/eterry1388/git-conflict-blame
106
121
  licenses: