@matheusbsbs / matheus-bagliano / commits / 8a8977425e
Fork 0

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