Skip to content

Commit 7cdc355

Browse files
angavrilovpaulusmack
authored andcommitted
gitk: Allow starting gui blame for a specific line
This adds a context menu item to the diff viewer pane that calls git gui blame, focusing it on the clicked line. In case of combined diffs, it also automatically deduces which parent is to be blamed. Lines added by the diff are blamed on the current commit itself. The context menu itself is added by this patch. It would be possible to add the commands from the flist menu to it. Signed-off-by: Alexander Gavrilov <[email protected]> Signed-off-by: Paul Mackerras <[email protected]>
1 parent 2df6442 commit 7cdc355

File tree

1 file changed

+146
-2
lines changed

1 file changed

+146
-2
lines changed

gitk

Lines changed: 146 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2249,6 +2249,7 @@ proc makewindow {} {
22492249
bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
22502250
global ctxbut
22512251
bind $cflist $ctxbut {pop_flist_menu %W %X %Y %x %y}
2252+
bind $ctext $ctxbut {pop_diff_menu %W %X %Y %x %y}
22522253

22532254
set maincursor [. cget -cursor]
22542255
set textcursor [$ctext cget -cursor]
@@ -2291,6 +2292,13 @@ proc makewindow {} {
22912292
{mc "Blame parent commit" command {external_blame 1}}
22922293
}
22932294
$flist_menu configure -tearoff 0
2295+
2296+
global diff_menu
2297+
set diff_menu .diffctxmenu
2298+
makemenu $diff_menu {
2299+
{mc "Run git gui blame on this line" command {external_blame_diff}}
2300+
}
2301+
$diff_menu configure -tearoff 0
22942302
}
22952303

22962304
# Windows sends all mouse wheel events to the current focused window, not
@@ -2993,6 +3001,34 @@ proc pop_flist_menu {w X Y x y} {
29933001
tk_popup $flist_menu $X $Y
29943002
}
29953003

3004+
proc find_ctext_fileinfo {line} {
3005+
global ctext_file_names ctext_file_lines
3006+
3007+
set ok [bsearch $ctext_file_lines $line]
3008+
set tline [lindex $ctext_file_lines $ok]
3009+
3010+
if {$ok >= [llength $ctext_file_lines] || $line < $tline} {
3011+
return {}
3012+
} else {
3013+
return [list [lindex $ctext_file_names $ok] $tline]
3014+
}
3015+
}
3016+
3017+
proc pop_diff_menu {w X Y x y} {
3018+
global ctext diff_menu flist_menu_file
3019+
global diff_menu_txtpos diff_menu_line
3020+
global diff_menu_filebase
3021+
3022+
stopfinding
3023+
set diff_menu_txtpos [split [$w index "@$x,$y"] "."]
3024+
set diff_menu_line [lindex $diff_menu_txtpos 0]
3025+
set f [find_ctext_fileinfo $diff_menu_line]
3026+
if {$f eq {}} return
3027+
set flist_menu_file [lindex $f 0]
3028+
set diff_menu_filebase [lindex $f 1]
3029+
tk_popup $diff_menu $X $Y
3030+
}
3031+
29963032
proc flist_hl {only} {
29973033
global flist_menu_file findstring gdttype
29983034

@@ -3099,7 +3135,96 @@ proc external_diff {} {
30993135
}
31003136
}
31013137

3102-
proc external_blame {parent_idx} {
3138+
proc find_hunk_blamespec {base line} {
3139+
global ctext
3140+
3141+
# Find and parse the hunk header
3142+
set s_lix [$ctext search -backwards -regexp ^@@ "$line.0 lineend" $base.0]
3143+
if {$s_lix eq {}} return
3144+
3145+
set s_line [$ctext get $s_lix "$s_lix + 1 lines"]
3146+
if {![regexp {^@@@*(( -\d+(,\d+)?)+) \+(\d+)(,\d+)? @@} $s_line \
3147+
s_line old_specs osz osz1 new_line nsz]} {
3148+
return
3149+
}
3150+
3151+
# base lines for the parents
3152+
set base_lines [list $new_line]
3153+
foreach old_spec [lrange [split $old_specs " "] 1 end] {
3154+
if {![regexp -- {-(\d+)(,\d+)?} $old_spec \
3155+
old_spec old_line osz]} {
3156+
return
3157+
}
3158+
lappend base_lines $old_line
3159+
}
3160+
3161+
# Now scan the lines to determine offset within the hunk
3162+
set parent {}
3163+
set max_parent [expr {[llength $base_lines]-2}]
3164+
set dline 0
3165+
set s_lno [lindex [split $s_lix "."] 0]
3166+
3167+
for {set i $line} {$i > $s_lno} {incr i -1} {
3168+
set c_line [$ctext get $i.0 "$i.0 + 1 lines"]
3169+
# Determine if the line is removed
3170+
set chunk [string range $c_line 0 $max_parent]
3171+
set removed_idx [string first "-" $chunk]
3172+
# Choose a parent index
3173+
if {$parent eq {}} {
3174+
if {$removed_idx >= 0} {
3175+
set parent $removed_idx
3176+
} else {
3177+
set unchanged_idx [string first " " $chunk]
3178+
if {$unchanged_idx >= 0} {
3179+
set parent $unchanged_idx
3180+
} else {
3181+
# blame the current commit
3182+
set parent -1
3183+
}
3184+
}
3185+
}
3186+
# then count other lines that belong to it
3187+
if {$parent >= 0} {
3188+
set code [string index $c_line $parent]
3189+
if {$code eq "-" || ($removed_idx < 0 && $code ne "+")} {
3190+
incr dline
3191+
}
3192+
} else {
3193+
if {$removed_idx < 0} {
3194+
incr dline
3195+
}
3196+
}
3197+
}
3198+
3199+
if {$parent eq {}} { set parent -1 }
3200+
incr parent
3201+
incr dline [lindex $base_lines $parent]
3202+
return [list $parent $dline]
3203+
}
3204+
3205+
proc external_blame_diff {} {
3206+
global currentid diffmergeid cmitmode
3207+
global diff_menu_txtpos diff_menu_line
3208+
global diff_menu_filebase flist_menu_file
3209+
3210+
if {$cmitmode eq "tree"} {
3211+
set parent_idx 0
3212+
set line [expr {$diff_menu_line - $diff_menu_filebase - 1}]
3213+
} else {
3214+
set hinfo [find_hunk_blamespec $diff_menu_filebase $diff_menu_line]
3215+
if {$hinfo ne {}} {
3216+
set parent_idx [lindex $hinfo 0]
3217+
set line [lindex $hinfo 1]
3218+
} else {
3219+
set parent_idx 0
3220+
set line 0
3221+
}
3222+
}
3223+
3224+
external_blame $parent_idx $line
3225+
}
3226+
3227+
proc external_blame {parent_idx {line {}}} {
31033228
global flist_menu_file
31043229
global nullid nullid2
31053230
global parentlist selectedline currentid
@@ -3115,7 +3240,12 @@ proc external_blame {parent_idx} {
31153240
return
31163241
}
31173242

3118-
if {[catch {exec git gui blame $base_commit $flist_menu_file &} err]} {
3243+
set cmdline [list git gui blame]
3244+
if {$line ne {} && $line > 1} {
3245+
lappend cmdline "--line=$line"
3246+
}
3247+
lappend cmdline $base_commit $flist_menu_file
3248+
if {[catch {eval exec $cmdline &} err]} {
31193249
error_popup "[mc "git gui blame: command failed:"] $err"
31203250
}
31213251
}
@@ -6364,6 +6494,7 @@ proc gettreeline {gtf id} {
63646494

63656495
proc showfile {f} {
63666496
global treefilelist treeidlist diffids nullid nullid2
6497+
global ctext_file_names ctext_file_lines
63676498
global ctext commentend
63686499

63696500
set i [lsearch -exact $treefilelist($diffids) $f]
@@ -6387,6 +6518,8 @@ proc showfile {f} {
63876518
filerun $bf [list getblobline $bf $diffids]
63886519
$ctext config -state normal
63896520
clear_ctext $commentend
6521+
lappend ctext_file_names $f
6522+
lappend ctext_file_lines [lindex [split $commentend "."] 0]
63906523
$ctext insert end "\n"
63916524
$ctext insert end "$f\n" filesep
63926525
$ctext config -state disabled
@@ -6447,6 +6580,7 @@ proc mergediff {id} {
64476580
proc getmergediffline {mdf id np} {
64486581
global diffmergeid ctext cflist mergemax
64496582
global difffilestart mdifffd treediffs
6583+
global ctext_file_names ctext_file_lines
64506584
global diffencoding
64516585

64526586
$ctext conf -state normal
@@ -6465,6 +6599,8 @@ proc getmergediffline {mdf id np} {
64656599
lappend difffilestart $here
64666600
lappend treediffs($id) $fname
64676601
add_flist [list $fname]
6602+
lappend ctext_file_names $fname
6603+
lappend ctext_file_lines [lindex [split $here "."] 0]
64686604
set diffencoding [get_path_encoding $fname]
64696605
set l [expr {(78 - [string length $fname]) / 2}]
64706606
set pad [string range "----------------------------------------" 1 $l]
@@ -6733,11 +6869,13 @@ proc setinlist {var i val} {
67336869

67346870
proc makediffhdr {fname ids} {
67356871
global ctext curdiffstart treediffs
6872+
global ctext_file_names
67366873

67376874
set i [lsearch -exact $treediffs($ids) $fname]
67386875
if {$i >= 0} {
67396876
setinlist difffilestart $i $curdiffstart
67406877
}
6878+
set ctext_file_names [lreplace $ctext_file_names end end $fname]
67416879
set l [expr {(78 - [string length $fname]) / 2}]
67426880
set pad [string range "----------------------------------------" 1 $l]
67436881
$ctext insert $curdiffstart "$pad $fname $pad" filesep
@@ -6746,6 +6884,7 @@ proc makediffhdr {fname ids} {
67466884
proc getblobdiffline {bdf ids} {
67476885
global diffids blobdifffd ctext curdiffstart
67486886
global diffnexthead diffnextnote difffilestart
6887+
global ctext_file_names ctext_file_lines
67496888
global diffinhdr treediffs
67506889
global diffencoding
67516890

@@ -6763,6 +6902,8 @@ proc getblobdiffline {bdf ids} {
67636902
# start of a new file
67646903
$ctext insert end "\n"
67656904
set curdiffstart [$ctext index "end - 1c"]
6905+
lappend ctext_file_names ""
6906+
lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
67666907
$ctext insert end "\n" filesep
67676908
# If the name hasn't changed the length will be odd,
67686909
# the middle char will be a space, and the two bits either
@@ -6899,6 +7040,7 @@ proc nextfile {} {
68997040

69007041
proc clear_ctext {{first 1.0}} {
69017042
global ctext smarktop smarkbot
7043+
global ctext_file_names ctext_file_lines
69027044
global pendinglinks
69037045

69047046
set l [lindex [split $first .] 0]
@@ -6912,6 +7054,8 @@ proc clear_ctext {{first 1.0}} {
69127054
if {$first eq "1.0"} {
69137055
catch {unset pendinglinks}
69147056
}
7057+
set ctext_file_names {}
7058+
set ctext_file_lines {}
69157059
}
69167060

69177061
proc settabs {{firstab {}}} {

0 commit comments

Comments
 (0)