gitk: Add a menu item to show where a given line comes from
This adds a menu item to the pop-up menu for the diff display window which makes gitk find which commit added the line (via git blame) and show that commit, with the line highlighted with a light-blue background. Signed-off-by: Paul Mackerras <paulus@samba.org>
Paul Mackerras committed
Oct 27, 2008 at 21:36 UTC
8a8977425e2697029414c3bcf4b627b074934bbc
1 file changed
+153
-7
gitk
+153
-7
@@ -2296,6 +2296,7 @@ proc makewindow {} {
2296
global diff_menu
2297
set diff_menu .diffctxmenu
2298
makemenu $diff_menu {
2299
+ {mc "Show origin of this line" command show_line_source}
2300
{mc "Run git gui blame on this line" command {external_blame_diff}}
2301
}
2302
$diff_menu configure -tearoff 0
@@ -2830,9 +2831,15 @@ proc treeclick {w x y} {
2831
}
2832
2833
proc setfilelist {id} {
2833
- global treefilelist cflist
2834
+ global treefilelist cflist jump_to_here
2835
2836
treeview $cflist $treefilelist($id) 0
2837
+ if {$jump_to_here ne {}} {
2838
+ set f [lindex $jump_to_here 0]
2839
+ if {[lsearch -exact $treefilelist($id) $f] >= 0} {
2840
+ showfile $f
2841
+ }
2842
+ }
2843
}
2844
2845
image create bitmap tri-rt -background black -foreground blue -data {
@@ -3256,6 +3263,91 @@ proc external_blame {parent_idx {line {}}} {
3263
}
3264
}
3265
3266
+proc show_line_source {} {
3267
+ global cmitmode currentid parents curview blamestuff blameinst
3268
+ global diff_menu_line diff_menu_filebase flist_menu_file
3269
+
3270
+ if {$cmitmode eq "tree"} {
3271
+ set id $currentid
3272
+ set line [expr {$diff_menu_line - $diff_menu_filebase}]
3273
+ } else {
3274
+ set h [find_hunk_blamespec $diff_menu_filebase $diff_menu_line]
3275
+ if {$h eq {}} return
3276
+ set pi [lindex $h 0]
3277
+ if {$pi == 0} {
3278
+ mark_ctext_line $diff_menu_line
3279
+ return
3280
+ }
3281
+ set id [lindex $parents($curview,$currentid) [expr {$pi - 1}]]
3282
+ set line [lindex $h 1]
3283
+ }
3284
+ if {[catch {
3285
+ set f [open [list | git blame -p -L$line,+1 $id -- $flist_menu_file] r]
3286
+ } err]} {
3287
+ error_popup [mc "Couldn't start git blame: %s" $err]
3288
+ return
3289
+ }
3290
+ fconfigure $f -blocking 0
3291
+ set i [reg_instance $f]
3292
+ set blamestuff($i) {}
3293
+ set blameinst $i
3294
+ filerun $f [list read_line_source $f $i]
3295
+}
3296
+
3297
+proc stopblaming {} {
3298
+ global blameinst
3299
+
3300
+ if {[info exists blameinst]} {
3301
+ stop_instance $blameinst
3302
+ unset blameinst
3303
+ }
3304
+}
3305
+
3306
+proc read_line_source {fd inst} {
3307
+ global blamestuff curview commfd blameinst
3308
+
3309
+ while {[gets $fd line] >= 0} {
3310
+ lappend blamestuff($inst) $line
3311
+ }
3312
+ if {![eof $fd]} {
3313
+ return 1
3314
+ }
3315
+ unset commfd($inst)
3316
+ unset blameinst
3317
+ fconfigure $fd -blocking 1
3318
+ if {[catch {close $fd} err]} {
3319
+ error_popup [mc "Error running git blame: %s" $err]
3320
+ return 0
3321
+ }
3322
+
3323
+ set fname {}
3324
+ set line [split [lindex $blamestuff($inst) 0] " "]
3325
+ set id [lindex $line 0]
3326
+ set lnum [lindex $line 1]
3327
+ if {[string length $id] == 40 && [string is xdigit $id] &&
3328
+ [string is digit -strict $lnum]} {
3329
+ # look for "filename" line
3330
+ foreach l $blamestuff($inst) {
3331
+ if {[string match "filename *" $l]} {
3332
+ set fname [string range $l 9 end]
3333
+ break
3334
+ }
3335
+ }
3336
+ }
3337
+ if {$fname ne {}} {
3338
+ # all looks good, select it
3339
+ if {[commitinview $id $curview]} {
3340
+ selectline [rowofcommit $id] 1 [list $fname $lnum]
3341
+ } else {
3342
+ error_popup [mc "That line comes from commit %s, \
3343
+ which is not in this view" [shortids $id]]
3344
+ }
3345
+ } else {
3346
+ puts "oops couldn't parse git blame output"
3347
+ }
3348
+ return 0
3349
+}
3350
+
3351
# delete $dir when we see eof on $f (presumably because the child has exited)
3352
proc delete_at_eof {f dir} {
3353
while {[gets $f line] >= 0} {}
@@ -5748,6 +5840,7 @@ proc stopfinding {} {
5840
set fprogcoord 0
5841
adjustprogress
5842
}
5843
+ stopblaming
5844
}
5845
5846
proc findmore {} {
@@ -6152,7 +6245,7 @@ proc make_secsel {l} {
6245
$canv3 lower $t
6246
}
6247
6155
-proc selectline {l isnew} {
6248
+proc selectline {l isnew {desired_loc {}}} {
6249
global canv ctext commitinfo selectedline
6250
global canvy0 linespc parents children curview
6251
global currentid sha1entry
@@ -6160,7 +6253,7 @@ proc selectline {l isnew} {
6253
global mergemax numcommits pending_select
6254
global cmitmode showneartags allcommits
6255
global targetrow targetid lastscrollrows
6163
- global autoselect
6256
+ global autoselect jump_to_here
6257
6258
catch {unset pending_select}
6259
$canv delete hover
@@ -6299,6 +6392,7 @@ proc selectline {l isnew} {
6392
$ctext conf -state disabled
6393
set commentend [$ctext index "end - 1c"]
6394
6395
+ set jump_to_here $desired_loc
6396
init_flist [mc "Comments"]
6397
if {$cmitmode eq "tree"} {
6398
gettree $id
@@ -6546,15 +6640,32 @@ proc getblobline {bf id} {
6640
$ctext insert end "$line\n"
6641
}
6642
if {[eof $bf]} {
6643
+ global jump_to_here ctext_file_names commentend
6644
+
6645
# delete last newline
6646
$ctext delete "end - 2c" "end - 1c"
6647
close $bf
6648
+ if {$jump_to_here ne {} &&
6649
+ [lindex $jump_to_here 0] eq [lindex $ctext_file_names 0]} {
6650
+ set lnum [expr {[lindex $jump_to_here 1] +
6651
+ [lindex [split $commentend .] 0]}]
6652
+ mark_ctext_line $lnum
6653
+ }
6654
return 0
6655
}
6656
$ctext config -state disabled
6657
return [expr {$nl >= 1000? 2: 1}]
6658
}
6659
6660
+proc mark_ctext_line {lnum} {
6661
+ global ctext
6662
+
6663
+ $ctext tag delete omark
6664
+ $ctext tag add omark $lnum.0 "$lnum.0 + 1 line"
6665
+ $ctext tag conf omark -background "#e0e0ff"
6666
+ $ctext see $lnum.0
6667
+}
6668
+
6669
proc mergediff {id} {
6670
global diffmergeid mdifffd
6671
global diffids treediffs
@@ -6562,10 +6673,12 @@ proc mergediff {id} {
6673
global diffcontext
6674
global diffencoding
6675
global limitdiffs vfilelimit curview
6676
+ global targetline
6677
6678
set diffmergeid $id
6679
set diffids $id
6680
set treediffs($id) {}
6681
+ set targetline {}
6682
# this doesn't seem to actually affect anything...
6683
set cmd [concat | git diff-tree --no-commit-id --cc -U$diffcontext $id]
6684
if {$limitdiffs && $vfilelimit($curview) ne {}} {
@@ -6587,7 +6700,7 @@ proc getmergediffline {mdf id np} {
6700
global diffmergeid ctext cflist mergemax
6701
global difffilestart mdifffd treediffs
6702
global ctext_file_names ctext_file_lines
6590
- global diffencoding
6703
+ global diffencoding jump_to_here targetline diffline
6704
6705
$ctext conf -state normal
6706
set nr 0
@@ -6611,9 +6724,17 @@ proc getmergediffline {mdf id np} {
6724
set l [expr {(78 - [string length $fname]) / 2}]
6725
set pad [string range "----------------------------------------" 1 $l]
6726
$ctext insert end "$pad $fname $pad\n" filesep
6727
+ set targetline {}
6728
+ if {$jump_to_here ne {} && [lindex $jump_to_here 0] eq $fname} {
6729
+ set targetline [lindex $jump_to_here 1]
6730
+ }
6731
+ set diffline 0
6732
} elseif {[regexp {^@@} $line]} {
6733
set line [encoding convertfrom $diffencoding $line]
6734
$ctext insert end "$line\n" hunksep
6735
+ if {[regexp { \+(\d+),\d+ @@} $line m nl]} {
6736
+ set diffline $nl
6737
+ }
6738
} elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
6739
# do nothing
6740
} else {
@@ -6653,6 +6774,15 @@ proc getmergediffline {mdf id np} {
6774
lappend tags m$num
6775
}
6776
$ctext insert end "$line\n" $tags
6777
+ if {$targetline ne {} && $minuses eq {}} {
6778
+ if {$diffline == $targetline} {
6779
+ set here [$ctext index "end - 1 line"]
6780
+ mark_ctext_line [lindex [split $here .] 0]
6781
+ set targetline {}
6782
+ } else {
6783
+ incr diffline
6784
+ }
6785
+ }
6786
}
6787
}
6788
$ctext conf -state disabled
@@ -6840,7 +6970,7 @@ proc getblobdiffs {ids} {
6970
global diffcontext
6971
global ignorespace
6972
global limitdiffs vfilelimit curview
6843
- global diffencoding
6973
+ global diffencoding targetline
6974
6975
set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
6976
if {$ignorespace} {
@@ -6853,6 +6983,7 @@ proc getblobdiffs {ids} {
6983
puts "error getting diffs: $err"
6984
return
6985
}
6986
+ set targetline {}
6987
set diffinhdr 0
6988
set diffencoding [get_path_encoding {}]
6989
fconfigure $bdf -blocking 0 -encoding binary
@@ -6875,7 +7006,7 @@ proc setinlist {var i val} {
7006
7007
proc makediffhdr {fname ids} {
7008
global ctext curdiffstart treediffs
6878
- global ctext_file_names
7009
+ global ctext_file_names jump_to_here targetline diffline
7010
7011
set i [lsearch -exact $treediffs($ids) $fname]
7012
if {$i >= 0} {
@@ -6885,6 +7016,11 @@ proc makediffhdr {fname ids} {
7016
set l [expr {(78 - [string length $fname]) / 2}]
7017
set pad [string range "----------------------------------------" 1 $l]
7018
$ctext insert $curdiffstart "$pad $fname $pad" filesep
7019
+ set targetline {}
7020
+ if {$jump_to_here ne {} && [lindex $jump_to_here 0] eq $fname} {
7021
+ set targetline [lindex $jump_to_here 1]
7022
+ }
7023
+ set diffline 0
7024
}
7025
7026
proc getblobdiffline {bdf ids} {
@@ -6892,7 +7028,7 @@ proc getblobdiffline {bdf ids} {
7028
global diffnexthead diffnextnote difffilestart
7029
global ctext_file_names ctext_file_lines
7030
global diffinhdr treediffs
6895
- global diffencoding
7031
+ global diffencoding jump_to_here targetline diffline
7032
7033
set nr 0
7034
$ctext conf -state normal
@@ -6941,6 +7077,7 @@ proc getblobdiffline {bdf ids} {
7077
set line [encoding convertfrom $diffencoding $line]
7078
$ctext insert end "$line\n" hunksep
7079
set diffinhdr 0
7080
+ set diffline $f2l
7081
7082
} elseif {$diffinhdr} {
7083
if {![string compare -length 12 "rename from " $line]} {
@@ -6974,6 +7111,7 @@ proc getblobdiffline {bdf ids} {
7111
} else {
7112
set line [encoding convertfrom $diffencoding $line]
7113
set x [string range $line 0 0]
7114
+ set here [$ctext index "end - 1 chars"]
7115
if {$x == "-" || $x == "+"} {
7116
set tag [expr {$x == "+"}]
7117
$ctext insert end "$line\n" d$tag
@@ -6984,6 +7122,14 @@ proc getblobdiffline {bdf ids} {
7122
# or something else we don't recognize
7123
$ctext insert end "$line\n" hunksep
7124
}
7125
+ if {$targetline ne {} && ($x eq " " || $x eq "+")} {
7126
+ if {$diffline == $targetline} {
7127
+ mark_ctext_line [lindex [split $here .] 0]
7128
+ set targetline {}
7129
+ } else {
7130
+ incr diffline
7131
+ }
7132
+ }
7133
}
7134
}
7135
$ctext conf -state disabled