Skip to content

Commit 8a89774

Browse files
committed
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 <[email protected]>
1 parent 190ec52 commit 8a89774

File tree

1 file changed

+153
-7
lines changed

1 file changed

+153
-7
lines changed

gitk

Lines changed: 153 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2296,6 +2296,7 @@ proc makewindow {} {
22962296
global diff_menu
22972297
set diff_menu .diffctxmenu
22982298
makemenu $diff_menu {
2299+
{mc "Show origin of this line" command show_line_source}
22992300
{mc "Run git gui blame on this line" command {external_blame_diff}}
23002301
}
23012302
$diff_menu configure -tearoff 0
@@ -2830,9 +2831,15 @@ proc treeclick {w x y} {
28302831
}
28312832

28322833
proc setfilelist {id} {
2833-
global treefilelist cflist
2834+
global treefilelist cflist jump_to_here
28342835

28352836
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+
}
28362843
}
28372844

28382845
image create bitmap tri-rt -background black -foreground blue -data {
@@ -3256,6 +3263,91 @@ proc external_blame {parent_idx {line {}}} {
32563263
}
32573264
}
32583265

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+
32593351
# delete $dir when we see eof on $f (presumably because the child has exited)
32603352
proc delete_at_eof {f dir} {
32613353
while {[gets $f line] >= 0} {}
@@ -5748,6 +5840,7 @@ proc stopfinding {} {
57485840
set fprogcoord 0
57495841
adjustprogress
57505842
}
5843+
stopblaming
57515844
}
57525845

57535846
proc findmore {} {
@@ -6152,15 +6245,15 @@ proc make_secsel {l} {
61526245
$canv3 lower $t
61536246
}
61546247

6155-
proc selectline {l isnew} {
6248+
proc selectline {l isnew {desired_loc {}}} {
61566249
global canv ctext commitinfo selectedline
61576250
global canvy0 linespc parents children curview
61586251
global currentid sha1entry
61596252
global commentend idtags linknum
61606253
global mergemax numcommits pending_select
61616254
global cmitmode showneartags allcommits
61626255
global targetrow targetid lastscrollrows
6163-
global autoselect
6256+
global autoselect jump_to_here
61646257

61656258
catch {unset pending_select}
61666259
$canv delete hover
@@ -6299,6 +6392,7 @@ proc selectline {l isnew} {
62996392
$ctext conf -state disabled
63006393
set commentend [$ctext index "end - 1c"]
63016394

6395+
set jump_to_here $desired_loc
63026396
init_flist [mc "Comments"]
63036397
if {$cmitmode eq "tree"} {
63046398
gettree $id
@@ -6546,26 +6640,45 @@ proc getblobline {bf id} {
65466640
$ctext insert end "$line\n"
65476641
}
65486642
if {[eof $bf]} {
6643+
global jump_to_here ctext_file_names commentend
6644+
65496645
# delete last newline
65506646
$ctext delete "end - 2c" "end - 1c"
65516647
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+
}
65526654
return 0
65536655
}
65546656
$ctext config -state disabled
65556657
return [expr {$nl >= 1000? 2: 1}]
65566658
}
65576659

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+
65586669
proc mergediff {id} {
65596670
global diffmergeid mdifffd
65606671
global diffids treediffs
65616672
global parents
65626673
global diffcontext
65636674
global diffencoding
65646675
global limitdiffs vfilelimit curview
6676+
global targetline
65656677

65666678
set diffmergeid $id
65676679
set diffids $id
65686680
set treediffs($id) {}
6681+
set targetline {}
65696682
# this doesn't seem to actually affect anything...
65706683
set cmd [concat | git diff-tree --no-commit-id --cc -U$diffcontext $id]
65716684
if {$limitdiffs && $vfilelimit($curview) ne {}} {
@@ -6587,7 +6700,7 @@ proc getmergediffline {mdf id np} {
65876700
global diffmergeid ctext cflist mergemax
65886701
global difffilestart mdifffd treediffs
65896702
global ctext_file_names ctext_file_lines
6590-
global diffencoding
6703+
global diffencoding jump_to_here targetline diffline
65916704

65926705
$ctext conf -state normal
65936706
set nr 0
@@ -6611,9 +6724,17 @@ proc getmergediffline {mdf id np} {
66116724
set l [expr {(78 - [string length $fname]) / 2}]
66126725
set pad [string range "----------------------------------------" 1 $l]
66136726
$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
66146732
} elseif {[regexp {^@@} $line]} {
66156733
set line [encoding convertfrom $diffencoding $line]
66166734
$ctext insert end "$line\n" hunksep
6735+
if {[regexp { \+(\d+),\d+ @@} $line m nl]} {
6736+
set diffline $nl
6737+
}
66176738
} elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
66186739
# do nothing
66196740
} else {
@@ -6653,6 +6774,15 @@ proc getmergediffline {mdf id np} {
66536774
lappend tags m$num
66546775
}
66556776
$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+
}
66566786
}
66576787
}
66586788
$ctext conf -state disabled
@@ -6840,7 +6970,7 @@ proc getblobdiffs {ids} {
68406970
global diffcontext
68416971
global ignorespace
68426972
global limitdiffs vfilelimit curview
6843-
global diffencoding
6973+
global diffencoding targetline
68446974

68456975
set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
68466976
if {$ignorespace} {
@@ -6853,6 +6983,7 @@ proc getblobdiffs {ids} {
68536983
puts "error getting diffs: $err"
68546984
return
68556985
}
6986+
set targetline {}
68566987
set diffinhdr 0
68576988
set diffencoding [get_path_encoding {}]
68586989
fconfigure $bdf -blocking 0 -encoding binary
@@ -6875,7 +7006,7 @@ proc setinlist {var i val} {
68757006

68767007
proc makediffhdr {fname ids} {
68777008
global ctext curdiffstart treediffs
6878-
global ctext_file_names
7009+
global ctext_file_names jump_to_here targetline diffline
68797010

68807011
set i [lsearch -exact $treediffs($ids) $fname]
68817012
if {$i >= 0} {
@@ -6885,14 +7016,19 @@ proc makediffhdr {fname ids} {
68857016
set l [expr {(78 - [string length $fname]) / 2}]
68867017
set pad [string range "----------------------------------------" 1 $l]
68877018
$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
68887024
}
68897025

68907026
proc getblobdiffline {bdf ids} {
68917027
global diffids blobdifffd ctext curdiffstart
68927028
global diffnexthead diffnextnote difffilestart
68937029
global ctext_file_names ctext_file_lines
68947030
global diffinhdr treediffs
6895-
global diffencoding
7031+
global diffencoding jump_to_here targetline diffline
68967032

68977033
set nr 0
68987034
$ctext conf -state normal
@@ -6941,6 +7077,7 @@ proc getblobdiffline {bdf ids} {
69417077
set line [encoding convertfrom $diffencoding $line]
69427078
$ctext insert end "$line\n" hunksep
69437079
set diffinhdr 0
7080+
set diffline $f2l
69447081

69457082
} elseif {$diffinhdr} {
69467083
if {![string compare -length 12 "rename from " $line]} {
@@ -6974,6 +7111,7 @@ proc getblobdiffline {bdf ids} {
69747111
} else {
69757112
set line [encoding convertfrom $diffencoding $line]
69767113
set x [string range $line 0 0]
7114+
set here [$ctext index "end - 1 chars"]
69777115
if {$x == "-" || $x == "+"} {
69787116
set tag [expr {$x == "+"}]
69797117
$ctext insert end "$line\n" d$tag
@@ -6984,6 +7122,14 @@ proc getblobdiffline {bdf ids} {
69847122
# or something else we don't recognize
69857123
$ctext insert end "$line\n" hunksep
69867124
}
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+
}
69877133
}
69887134
}
69897135
$ctext conf -state disabled

0 commit comments

Comments
 (0)