git_helpers 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 89d50a9fc84a8451a75811aed19d8d5763114dbddda62abb44a2f32ee9784b60
4
+ data.tar.gz: 2cec1efd8ac009542d1cc883dc94c8f56b2f0326434cbbea8e59a916b03eb6a0
5
+ SHA512:
6
+ metadata.gz: f4a638373b1051726dab1a69543347cea204aa63b3241499d911485f3448b65da34c7f6a42b5934b7e63bdd80dfa06e1f949de007cefca932db57d679dfb05b9
7
+ data.tar.gz: 24577fa3a75b6a422bbb584a95519d9c269261161919e36c7e98f8d6b390541922d2775e00b796a3b0d7d99532052c598064fd7113c64c4730e4168cf9811f0b
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ /.bundle
2
+ /.yardoc/
3
+ /Gemfile.lock
4
+ /doc/
5
+ /pkg/
6
+ /vendor/cache/*.gem
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ ---
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.0
5
+ - 2.3.3
6
+ - 2.2.6
7
+ - 2.1.10
8
+ #- ruby-head
9
+ #- ruby-head-clang
10
+ script: bundle exec rake test
data/.yardopts ADDED
@@ -0,0 +1,6 @@
1
+ --markup markdown
2
+ --title "git_helpers Documentation"
3
+ --protected
4
+ -
5
+ ChangeLog.md
6
+ LICENSE.txt
data/ChangeLog.md ADDED
@@ -0,0 +1,4 @@
1
+ ### 0.1.0 / 2016-06-03
2
+
3
+ * Initial release:
4
+
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+ gem 'drain', :github => 'DamienRobert/drain'
5
+ gem 'shell_helpers', :github => 'DamienRobert/shell_helpers'
6
+
7
+ group :development do
8
+ gem 'kramdown'
9
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright © 2016–2017 Damien Robert
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # git_helpers
2
+
3
+ * [Homepage](https://github.com/DamienRobert/git_helpers#readme)
4
+ * [Issues](https://github.com/DamienRobert/git_helpers/issues)
5
+ * [Documentation](http://rubydoc.info/gems/git_helpers)
6
+ * [Email](mailto:Damien.Olivier.Robert+gems at gmail.com)
7
+
8
+ [![Gem Version](https://img.shields.io/gem/v/git_helpers.svg)](https://rubygems.org/gems/git_helpers)
9
+ [![Build Status](https://travis-ci.org/DamienRobert/git_helpers.svg?branch=master)](https://travis-ci.org/DamienRobert/git_helpers)
10
+
11
+ ## Description
12
+
13
+ - diff-fancy.rb: like [diff so fancy](https://github.com/so-fancy/diff-so-fancy) but in ruby and with more features
14
+ - gitsatus.rb: lie [zsh git prompt](https://github.com/olivierverdier/zsh-git-prompt) but in ruby and with more features too!
15
+
16
+ ## Diff Fancy
17
+
18
+ The output is very similar to diff-fancy.rb. With the following
19
+ differences:
20
+ - diff-fancy.rb implement a parser of git diff. It is then very easy to
21
+ tweak the output afterwards. The original diff-fancy relies on regexp,
22
+ which makes it harder to customize.
23
+ - support for submodules change in the diff
24
+ - support for octopus merge
25
+ - clean up 'No new line at end of file' for symlinks (which never have a new line)
26
+
27
+ TODO:
28
+ - support 'git log -p --graph'
29
+ - support git config to activate features on a repo basis
30
+
31
+ ## Examples
32
+
33
+ - `gitstatus.rb folders`
34
+ - `git diff | diff-fancy.rb`
35
+
36
+ Here is my .gitconfig using diff-fancy.rb:
37
+
38
+ ~~~
39
+ highlight = "!f() { [ \"$GIT_PREFIX\" != \"\" ] && cd \"$GIT_PREFIX\"; GIT_PAGER=\"diff-fancy.rb\" git $@; }; f"
40
+ di = "!f() { [ \"$GIT_PREFIX\" != \"\" ] && cd \"$GIT_PREFIX\"; GIT_PAGER=\"diff-fancy.rb\" git diff -B $@; }; f"
41
+ dc = "!f() { [ \"$GIT_PREFIX\" != \"\" ] && cd \"$GIT_PREFIX\"; GIT_PAGER=\"diff-fancy.rb\" git diff -B --staged $@; }; f"
42
+ dw = "!f() { [ \"$GIT_PREFIX\" != \"\" ] && cd \"$GIT_PREFIX\"; GIT_PAGER=\"diff-fancy.rb --no-highlight\" git diff -B --color-words $@; }; f"
43
+ dcw = "!f() { [ \"$GIT_PREFIX\" != \"\" ] && cd \"$GIT_PREFIX\"; GIT_PAGER=\"diff-fancy.rb --no-highlight\" git diff -B --staged --color-words $@; }; f"
44
+ ~~~
45
+
46
+ ## Install
47
+
48
+ #TODO: release the gem
49
+ $ gem install git_helpers
50
+
51
+ ## Copyright
52
+
53
+ Copyright © 2016–2017 Damien Robert
54
+
55
+ MIT License. See [LICENSE.txt](./LICENSE.txt) for details.
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'rubygems/tasks'
5
+ Gem::Tasks.new(sign: {checksum: true, pgp: true},
6
+ scm: {status: true}) do |tasks|
7
+ tasks.console.command = 'pry'
8
+ end
9
+ rescue LoadError => e
10
+ warn e.message
11
+ end
12
+
13
+ require 'rake/testtask'
14
+ Rake::TestTask.new do |test|
15
+ test.libs << 'test'
16
+ test.pattern = 'test/**/test_*.rb'
17
+ test.verbose = true
18
+ end
19
+
20
+ begin
21
+ require 'yard'
22
+ YARD::Rake::YardocTask.new
23
+ rescue LoadError => e
24
+ task :yard do
25
+ warn e.message
26
+ end
27
+ end
28
+ task :doc => :yard
29
+
data/bin/diff-fancy.rb ADDED
@@ -0,0 +1,699 @@
1
+ #!/usr/bin/env ruby
2
+ # Inspired by diff-so-fancy; wrapper around diff-highlight
3
+ # https://github.com/stevemao/diff-so-fancy
4
+ # [commit: 0ea7c129420c57ec0384a704325e27c41f8f450d,
5
+ # last commit checked: 3adf0114da99643ec53a16253a3d6f42390e4c19 (2017-04-04)]
6
+ #TODO: use git-config
7
+ #TODO: work with 'git log -p --graph'
8
+
9
+ require "simplecolor"
10
+ SimpleColor.mix_in_string
11
+ begin
12
+ require "shell_helpers"
13
+ rescue LoadError
14
+ end
15
+
16
+ class GitDiff
17
+ def self.output(gdiff, **opts)
18
+ if gdiff.respond_to?(:each_line)
19
+ enum=gdiff.each_line
20
+ else
21
+ enum=gdiff.each
22
+ end
23
+ self.new(enum, **opts).output
24
+ end
25
+
26
+ attr_reader :output
27
+ include Enumerable
28
+ NoNewLine="\\n"
29
+
30
+ def initialize(diff,**opts)
31
+ @diff=diff #Assume diff is a line iterator ['gitdiff'.each_line]
32
+ @current=0
33
+ @mode=:unknown
34
+ @opts=opts
35
+ @opts[:color]=@opts.fetch(:color,true)
36
+ #modes:
37
+ #- unknown (temp mode)
38
+ #- commit
39
+ #- meta
40
+ #- submodule_header
41
+ #- submodule
42
+ #- diff_header
43
+ #- hunk
44
+ @colors={meta: [:bold]}
45
+ end
46
+
47
+ def output_line(l)
48
+ @output << l.chomp+"\n"
49
+ end
50
+ def output_lines(lines)
51
+ lines.each {|l| output_line l}
52
+ end
53
+ def output
54
+ each {|l| puts l}
55
+ end
56
+
57
+ def next_mode(nmode)
58
+ @next_mode=nmode
59
+ end
60
+ def update_mode
61
+ @start_mode=false
62
+ @next_mode && change_mode(@next_mode)
63
+ @next_mode=nil
64
+ end
65
+ def change_mode(nmode)
66
+ @start_mode=true
67
+ send :"end_#{@mode}" unless @mode==:unknown
68
+ @mode=nmode
69
+ send :"new_#{@mode}" unless @mode==:unknown
70
+ end
71
+
72
+ def new_commit; @commit={}; end
73
+ def end_commit; end
74
+ def new_meta; end
75
+ def end_meta; end
76
+ def new_hunk; end
77
+ def end_hunk; end
78
+ def new_submodule_header; @submodule={}; end
79
+ def end_submodule_header; end
80
+ def new_submodule; end
81
+ def end_submodule; end
82
+ def new_diff_header; @file={mode: :modify} end
83
+ def end_diff_header; end
84
+
85
+ def detect_new_diff_header
86
+ @line =~ /^diff\s/
87
+ end
88
+ def detect_end_diff_header
89
+ @line =~ /^\+\+\+\s/
90
+ end
91
+
92
+ def detect_new_hunk
93
+ @line.match(/^@@+\s.*\s@@/)
94
+ end
95
+ def detect_end_hunk
96
+ @hunk[:lines_seen].each_with_index.all? { |v,i| v==@hunk[:lines][i].first }
97
+ end
98
+
99
+ def handle_meta
100
+ handle_line
101
+ end
102
+
103
+ def parse_hunk_header
104
+ m=@line.match(/^@@+\s(.*)\s@@\s*(.*)/)
105
+ hunks=m[1]
106
+ @hunk={lines: []}
107
+ @hunk[:header]=m[2]
108
+ filenumber=0
109
+ hunks.split.each do |hunk|
110
+ hunkmode=hunk[0]
111
+ hunk=hunk[1..-1]
112
+ line,length=hunk.split(',').map(&:to_i)
113
+ #handle hunks of the form @@ -1 +0,0 @@
114
+ length,line=line,length unless length
115
+ case hunkmode
116
+ when '-'
117
+ filenumber+=1
118
+ @hunk[:lines][filenumber]=[length,line]
119
+ when '+'
120
+ @hunk[:lines][0]=[length,line]
121
+ end
122
+ end
123
+ @hunk[:n]=@hunk[:lines].length
124
+ @hunk[:lines_seen]=Array.new(@hunk[:n],0)
125
+ end
126
+
127
+ def handle_hunk
128
+ if @start_mode
129
+ parse_hunk_header
130
+ else
131
+ #'The 'No new line at end of file' is sort of part of the hunk, but
132
+ #is not considerer in the hunkheader
133
+ unless @line == NoNewLine
134
+ #we need to wait for a NoNewLine to be sure we are at the end of the hunk
135
+ return reparse(:unknown) if detect_end_hunk
136
+ linemodes=@line[0...@hunk[:n]-1]
137
+ newline=true
138
+ #the line is on the new file unless there is a '-' somewhere
139
+ if linemodes=~/-/
140
+ newline=false
141
+ else
142
+ @hunk[:lines_seen][0]+=1
143
+ end
144
+ (1...@hunk[:n]).each do |i|
145
+ linemode=linemodes[i-1]
146
+ case linemode
147
+ when '-'
148
+ @hunk[:lines_seen][i]+=1
149
+ when ' '
150
+ @hunk[:lines_seen][i]+=1 if newline
151
+ end
152
+ end
153
+ end
154
+ end
155
+ handle_line
156
+ end
157
+
158
+ def get_file_name(file)
159
+ #remove prefix (todo handle the no-prefix option)
160
+ file.gsub(/^[abciow12]\//,'')
161
+ end
162
+
163
+ def detect_filename
164
+ if m=@line.match(/^---\s(.*)/)
165
+ @file[:old_name]=get_file_name(m[1])
166
+ return true
167
+ end
168
+ if m=@line.match(/^\+\+\+\s(.*)/)
169
+ @file[:name]=get_file_name(m[1])
170
+ return true
171
+ end
172
+ false
173
+ end
174
+
175
+ def detect_perm
176
+ if m=@line.match(/^old mode\s+(.*)/)
177
+ @file[:old_perm]=m[1]
178
+ return true
179
+ end
180
+ if m=@line.match(/^new mode\s+(.*)/)
181
+ @file[:new_perm]=m[1]
182
+ return true
183
+ end
184
+ false
185
+ end
186
+
187
+ def detect_index
188
+ if m=@line.match(/^index\s+(.*)\.\.(.*)/)
189
+ @file[:oldhash]=m[1].split(',')
190
+ @file[:hash],perm=m[2].split
191
+ @file[:perm]||=perm
192
+ return true
193
+ end
194
+ false
195
+ end
196
+
197
+ def detect_delete
198
+ if m=@line.match(/^deleted file mode\s+(.*)/)
199
+ @file[:old_perm]=m[1]
200
+ @file[:mode]=:delete
201
+ return true
202
+ end
203
+ false
204
+ end
205
+
206
+ def detect_newfile
207
+ if m=@line.match(/^new file mode\s+(.*)/)
208
+ @file[:new_perm]=m[1]
209
+ @file[:mode]=:new
210
+ return true
211
+ end
212
+ false
213
+ end
214
+
215
+ def detect_rename_copy
216
+ if m=@line.match(/^similarity index\s+(.*)/)
217
+ @file[:similarity]=m[1]
218
+ return true
219
+ end
220
+ if m=@line.match(/^dissimilarity index\s+(.*)/)
221
+ @file[:mode]=:rewrite
222
+ @file[:dissimilarity]=m[1]
223
+ return true
224
+ end
225
+ #if we have a rename with 100% similarity, there won't be any hunks so
226
+ #we need to detect the filenames there
227
+ if m=@line.match(/^(?:rename|copy) from\s+(.*)/)
228
+ @file[:old_name]=m[1]
229
+ end
230
+ if m=@line.match(/^(?:rename|copy) to\s+(.*)/)
231
+ @file[:name]=m[1]
232
+ end
233
+ if m=@line.match(/^rename\s+(.*)/)
234
+ @file[:mode]=:rename
235
+ return true
236
+ end
237
+ if m=@line.match(/^copy\s+(.*)/)
238
+ @file[:mode]=:copy
239
+ return true
240
+ end
241
+ false
242
+ end
243
+
244
+ def detect_diff_header
245
+ if @start_mode
246
+ if m=@line.chomp.match(/^diff\s--git\s(.*)\s(.*)/)
247
+ @file[:old_name]=get_file_name(m[1])
248
+ @file[:name]=get_file_name(m[2])
249
+ elsif
250
+ m=@line.match(/^diff\s--(?:cc|combined)\s(.*)/)
251
+ @file[:name]=get_file_name(m[1])
252
+ end
253
+ true
254
+ end
255
+ end
256
+
257
+ def handle_diff_header
258
+ if detect_diff_header
259
+ elsif detect_filename
260
+ elsif detect_perm
261
+ elsif detect_index
262
+ elsif detect_delete
263
+ elsif detect_newfile
264
+ elsif detect_rename_copy
265
+ else
266
+ return reparse(:unknown)
267
+ end
268
+ next_mode(:unknown) if detect_end_diff_header
269
+ handle_line
270
+ end
271
+
272
+ def detect_new_submodule_header
273
+ if m=@line.chomp.match(/^Submodule\s(.*)\s(.*)/)
274
+ subname=m[1];
275
+ return not(@submodule && @submodule[:name]==subname)
276
+ end
277
+ false
278
+ end
279
+
280
+ def handle_submodule_header
281
+ if m=@line.chomp.match(/^Submodule\s(\S*)\s(.*)/)
282
+ subname=m[1]
283
+ if @submodule[:name]
284
+ #we may be dealing with a new submodule
285
+ #require 'pry'; binding.pry
286
+ return reparse(:submodule_header) if subname != @submodule[:name]
287
+ else
288
+ @submodule[:name]=m[1]
289
+ end
290
+ subinfo=m[2]
291
+ if subinfo == "contains untracked content"
292
+ @submodule[:untracked]=true
293
+ elsif subinfo == "contains modified content"
294
+ @submodule[:modified]=true
295
+ else
296
+ (@submodule[:info]||="") << subinfo
297
+ next_mode(:submodule) if subinfo =~ /^.......\.\.\.?........*:$/
298
+ end
299
+ handle_line
300
+ else
301
+ return reparse(:unknown)
302
+ end
303
+ end
304
+
305
+ def submodule_line
306
+ @line=~/^ [><] /
307
+ end
308
+
309
+ def handle_submodule
310
+ #we have lines indicating new commits
311
+ #they always end by a new line except when followed by another submodule
312
+ return reparse(:unknown) if !submodule_line
313
+ handle_line
314
+ end
315
+
316
+ def detect_new_commit
317
+ @line=~/^commit\b/
318
+ end
319
+
320
+ def handle_commit
321
+ if m=@line.match(/^(\w+):\s(.*)/)
322
+ @commit[m[1]]=m[2]
323
+ handle_line
324
+ else
325
+ @start_mode ? handle_line : reparse(:unknown)
326
+ end
327
+ end
328
+
329
+ def reparse(nmode)
330
+ change_mode(nmode)
331
+ parse_line
332
+ end
333
+
334
+ def handle_line
335
+ end
336
+
337
+
338
+ def parse_line
339
+ case @mode
340
+ when :unknown, :meta
341
+ if detect_new_hunk
342
+ return reparse(:hunk)
343
+ elsif detect_new_diff_header
344
+ return reparse(:diff_header)
345
+ elsif detect_new_submodule_header
346
+ return reparse(:submodule_header)
347
+ elsif detect_new_commit
348
+ return reparse(:commit)
349
+ else
350
+ change_mode(:meta) if @mode==:unknown
351
+ handle_meta
352
+ end
353
+ when :commit
354
+ handle_commit
355
+ when :submodule_header
356
+ handle_submodule_header
357
+ when :submodule
358
+ handle_submodule
359
+ when :diff_header
360
+ handle_diff_header
361
+ #=> mode=unknown if we detect we are not a diff header anymore
362
+ when :hunk
363
+ handle_hunk
364
+ #=> mode=unknown at end of hunk
365
+ end
366
+ end
367
+
368
+ def prepare_new_line(line)
369
+ @orig_line=line
370
+ @line=@orig_line.uncolor
371
+ update_mode
372
+ end
373
+
374
+ def parse
375
+ Enumerator.new do |y|
376
+ @output=y
377
+ @diff.each do |line|
378
+ prepare_new_line(line)
379
+ parse_line
380
+ yield if block_given?
381
+ end
382
+ change_mode(:unknown) #to trigger the last end_* hook
383
+ end
384
+ end
385
+
386
+ def each(&b)
387
+ parse.each(&b)
388
+ end
389
+ end
390
+
391
+ class GitDiffDebug < GitDiff
392
+ def initialize(*args,&b)
393
+ super
394
+ @cols=`tput cols`.to_i
395
+ end
396
+
397
+ def center(msg)
398
+ msg.center(@cols,'─')
399
+ end
400
+
401
+ def handle_line
402
+ super
403
+ output_line "#{@mode}: #{@orig_line}"
404
+ #p @hunk if @mode==:hunk
405
+ end
406
+
407
+ %i(commit meta diff_header hunk submodule_header submodule).each do |meth|
408
+ define_method(:"new_#{meth}") do |*a,&b|
409
+ super(*a,&b)
410
+ output_line(center("New #{meth}"))
411
+ end
412
+ define_method(:"end_#{meth}") do |*a,&b|
413
+ super(*a,&b)
414
+ output_line(center("End #{meth}"))
415
+ end
416
+ end
417
+ end
418
+
419
+ #stolen from diff-highlight git contrib script
420
+ class GitDiffHighlight < GitDiff
421
+ def new_hunk
422
+ super
423
+ @accumulator=[[],[]]
424
+ end
425
+ def end_hunk
426
+ super
427
+ show_hunk
428
+ end
429
+
430
+ def highlight_pair(old,new)
431
+ oldc=SimpleColor.color_entities(old).each_with_index
432
+ newc=SimpleColor.color_entities(new).each_with_index
433
+ seen_pm=false
434
+ #find common prefix
435
+ loop do
436
+ a=oldc.grep {|c| ! SimpleColor.color?(c)}
437
+ b=newc.grep {|c| ! SimpleColor.color?(c)}
438
+ if !seen_pm and a=="-" and b=="+"
439
+ seen_pm=true
440
+ elsif a==b
441
+ else
442
+ last
443
+ end
444
+ #rescue StopIteration
445
+ end
446
+ end
447
+
448
+ def show_hunk
449
+ old,new=@accumulator
450
+ if old.length != new.length
451
+ output_lines(old+new)
452
+ else
453
+ newhunk=[]
454
+ (0...old.length).each do |i|
455
+ oldi,newi=highlight_pair(old[i],new[i])
456
+ output_line oldi
457
+ newhunk << newi
458
+ end
459
+ output_lines(newhunk)
460
+ end
461
+ end
462
+
463
+ def handle_line
464
+ if @mode == :hunk && @hunk[:n]==2
465
+ linemode=@line[0]
466
+ case linemode
467
+ when "-"
468
+ @accumulator[0] << @orig_line
469
+ when "+"
470
+ @accumulator[1] << @orig_line
471
+ else
472
+ show_hunk
473
+ @accumulator=[[],[]]
474
+ output_line @orig_line
475
+ end
476
+ else
477
+ output_line @orig_line
478
+ end
479
+ end
480
+ end
481
+
482
+ class GitFancyDiff < GitDiff
483
+
484
+ def initialize(*args,&b)
485
+ super
486
+ #when run inside a pager I get one more column so the line overflow
487
+ #I don't know why
488
+ cols=`tput cols`.to_i
489
+ cols==0 && cols=80 #if TERM is not defined `tput cols` returns ''
490
+ @cols=cols-1
491
+ end
492
+
493
+ def hline
494
+ '─'*@cols
495
+ end
496
+ def hhline
497
+ #'⬛'*@cols
498
+ #"━"*@cols
499
+ "═"*@cols
500
+ end
501
+
502
+ def short_perm_mode(m, prefix: '+')
503
+ case m
504
+ when "040000"
505
+ prefix+"d" #directory
506
+ when "100644"
507
+ "" #file
508
+ when "100755"
509
+ prefix+"x" #executable
510
+ when "120000"
511
+ prefix+"l" #symlink
512
+ when "160000"
513
+ prefix+"g" #gitlink
514
+ end
515
+ end
516
+ def perm_mode(m, prefix: ' ')
517
+ case m
518
+ when "040000"
519
+ prefix+"directory"
520
+ when "100644"
521
+ "" #file
522
+ when "100755"
523
+ prefix+"executable"
524
+ when "120000"
525
+ prefix+"symlink"
526
+ when "160000"
527
+ prefix+"gitlink"
528
+ end
529
+ end
530
+
531
+ def diff_header_summary
532
+ r=case @file[:mode]
533
+ when :modify
534
+ "modified: #{@file[:name]}"
535
+ when :rewrite
536
+ "rewrote: #{@file[:name]} (dissimilarity: #{@file[:dissimilarity]})"
537
+ when :new
538
+ "added#{perm_mode(@file[:new_perm])}: #{@file[:name]}"
539
+ when :delete
540
+ "deleted#{perm_mode(@file[:old_perm])}: #{@file[:old_name]}"
541
+ when :rename
542
+ "renamed: #{@file[:old_name]} to #{@file[:name]} (similarity: #{@file[:similarity]})"
543
+ when :copy
544
+ "copied: #{@file[:old_name]} to #{@file[:name]} (similarity: #{@file[:similarity]})"
545
+ end
546
+ r<<" [#{short_perm_mode(@file[:old_perm],prefix:'-')}#{short_perm_mode(@file[:new_perm])}]" if @file[:old_perm] && @file[:new_perm]
547
+ r
548
+ end
549
+
550
+ def meta_colorize(l)
551
+ if @opts[:color]
552
+ l.color(*@colors[:meta])
553
+ else
554
+ l
555
+ end
556
+ end
557
+
558
+ def new_diff_header
559
+ super
560
+ output_line meta_colorize(hline)
561
+ end
562
+
563
+ def end_diff_header
564
+ super
565
+ output_line meta_colorize(diff_header_summary)
566
+ output_line meta_colorize(hline)
567
+ end
568
+
569
+ def submodule_header_summary
570
+ r="Submodule #{@submodule[:name]}"
571
+ extra=[@submodule[:modified] && "modified", @submodule[:untracked] && "untracked"].compact.join("+")
572
+ r<<" [#{extra}]" unless extra.empty?
573
+ r << " #{@submodule[:info]}" if @submodule[:info]
574
+ r
575
+ end
576
+
577
+ def new_submodule_header
578
+ super
579
+ output_line meta_colorize(hline)
580
+ end
581
+
582
+ def end_submodule_header
583
+ super
584
+ output_line meta_colorize(submodule_header_summary)
585
+ output_line meta_colorize(hline)
586
+ end
587
+
588
+ def nonewline_clean
589
+ @mode==:hunk && @file && (@file[:perm]=="120000" or @file[:old_perm]=="120000" or @file[:new_perm]=="120000") && @line==NoNewLine
590
+ end
591
+
592
+ def new_commit
593
+ super
594
+ output_line meta_colorize(hhline)
595
+ end
596
+ def end_commit
597
+ super
598
+ output_line meta_colorize(hhline)
599
+ end
600
+
601
+ def clean_hunk_col
602
+ if @opts[:color] && @mode==:hunk && !@start_mode && @hunk[:n]==2
603
+ bcolor,ecolor,line=SimpleColor.current_colors(@orig_line)
604
+ m=line.scrub.match(/^([+-])?(.*)/)
605
+ mode=m[1]
606
+ cline=m[2]
607
+ if mode && cline !~ /[^[:space:]]/ #detect blank line
608
+ output_line SimpleColor.color(bcolor.to_s + (cline.empty? ? " ": cline)+ecolor.to_s,:inverse)
609
+ else
610
+ cline.sub!(/^\s/,'') unless mode #strip one blank character
611
+ output_line bcolor.to_s+cline+ecolor.to_s
612
+ end
613
+ true
614
+ end
615
+ end
616
+
617
+ def hunk_header
618
+ if @mode==:hunk && @start_mode
619
+ if @hunk[:lines][0][1] && @hunk[:lines][0][1] != 0
620
+ header="#{@file[:name]}:#{@hunk[:lines][0][1]}"
621
+ output_line @orig_line.sub(/(@@+\s)(.*)(\s@@+)/,"\\1#{header}\\3")
622
+ end
623
+ true
624
+ end
625
+ end
626
+
627
+ def binary_file_differ
628
+ @file and (@file[:mode]==:new && @line =~ %r{^Binary files /dev/null and ./#{@file[:name]} differ$} or
629
+ @file[:mode]==:delete && @line =~ %r{^Binary files ./#{@file[:old_name]} and /dev/null differ$})
630
+ end
631
+
632
+ def handle_line
633
+ super
634
+ #:diff_header and submodule_header are handled at end_*
635
+ case @mode
636
+ when :meta
637
+ if binary_file_differ
638
+ else output_line @orig_line
639
+ end
640
+ when :hunk
641
+ if hunk_header
642
+ elsif nonewline_clean
643
+ elsif clean_hunk_col
644
+ else output_line @orig_line
645
+ end
646
+ when :submodule,:commit
647
+ output_line @orig_line
648
+ end
649
+ end
650
+ end
651
+
652
+ if __FILE__ == $0
653
+ require 'optparse'
654
+
655
+ @opts={pager: true, diff_highlight: true, color: true, debug: false}
656
+ optparse = OptionParser.new do |opt|
657
+ opt.banner = "fancy git diff"
658
+ opt.on("--[no-]pager", "launch the pager [true]") do |v|
659
+ @opts[:pager]=v
660
+ end
661
+ opt.on("--[no-]highlight", "run the diff through diff-highlight [true]") do |v|
662
+ @opts[:diff_highlight]=v
663
+ end
664
+ opt.on("--[no-]color", "color output [true]") do |v|
665
+ @opts[:color]=v
666
+ end
667
+ opt.on("--raw", "Only parse diff headers") do |v|
668
+ @opts[:color]=false
669
+ @opts[:pager]=false
670
+ @opts[:diff_highlight]=false
671
+ end
672
+ opt.on("--[no-]debug", "Debug mode") do |v|
673
+ @opts[:debug]=v
674
+ end
675
+ end
676
+ optparse.parse!
677
+ @opts[:pager]=false unless Module.const_defined?('ShellHelpers')
678
+ @opts[:pager] && ShellHelpers.run_pager #("--pattern '^(Date|added|deleted|modified): '")
679
+
680
+ diff_highlight=ENV['DIFF_HIGHLIGHT']||"#{File.dirname(__FILE__)}/contrib/diff-highlight"
681
+
682
+ args=ARGF
683
+ if @opts[:debug]
684
+ GitDiffDebug.new(args,**@opts).output
685
+ elsif @opts[:diff_highlight]
686
+ IO.popen(diff_highlight,'r+') do |f|
687
+ Thread.new do
688
+ args.each_line do |l|
689
+ f.write(l)
690
+ end
691
+ f.close_write
692
+ end
693
+ GitFancyDiff.new(f,**@opts).output
694
+ end
695
+ else
696
+ #diff=GitDiffHighlight.new(args,**@opts).parse
697
+ GitFancyDiff.new(args,**@opts).output
698
+ end
699
+ end