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 <angavrilov@gmail.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
Alexander Gavrilov committed
Oct 24, 2008 at 12:13 UTC
7cdc3556d1cec11582ae2a018d4052767c02e1ef
1 file changed
+146
-2
gitk
+146
-2
@@ -2249,6 +2249,7 @@ proc makewindow {} {
2249
bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
2250
global ctxbut
2251
bind $cflist $ctxbut {pop_flist_menu %W %X %Y %x %y}
2252
+ bind $ctext $ctxbut {pop_diff_menu %W %X %Y %x %y}
2253
2254
set maincursor [. cget -cursor]
2255
set textcursor [$ctext cget -cursor]
@@ -2291,6 +2292,13 @@ proc makewindow {} {
2292
{mc "Blame parent commit" command {external_blame 1}}
2293
}
2294
$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
2302
}
2303
2304
# 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} {
3001
tk_popup $flist_menu $X $Y
3002
}
3003
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
+
3032
proc flist_hl {only} {
3033
global flist_menu_file findstring gdttype
3034
@@ -3099,7 +3135,96 @@ proc external_diff {} {
3135
}
3136
}
3137
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 {}}} {
3228
global flist_menu_file
3229
global nullid nullid2
3230
global parentlist selectedline currentid
@@ -3115,7 +3240,12 @@ proc external_blame {parent_idx} {
3240
return
3241
}
3242
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]} {
3249
error_popup "[mc "git gui blame: command failed:"] $err"
3250
}
3251
}
@@ -6364,6 +6494,7 @@ proc gettreeline {gtf id} {
6494
6495
proc showfile {f} {
6496
global treefilelist treeidlist diffids nullid nullid2
6497
+ global ctext_file_names ctext_file_lines
6498
global ctext commentend
6499
6500
set i [lsearch -exact $treefilelist($diffids) $f]
@@ -6387,6 +6518,8 @@ proc showfile {f} {
6518
filerun $bf [list getblobline $bf $diffids]
6519
$ctext config -state normal
6520
clear_ctext $commentend
6521
+ lappend ctext_file_names $f
6522
+ lappend ctext_file_lines [lindex [split $commentend "."] 0]
6523
$ctext insert end "\n"
6524
$ctext insert end "$f\n" filesep
6525
$ctext config -state disabled
@@ -6447,6 +6580,7 @@ proc mergediff {id} {
6580
proc getmergediffline {mdf id np} {
6581
global diffmergeid ctext cflist mergemax
6582
global difffilestart mdifffd treediffs
6583
+ global ctext_file_names ctext_file_lines
6584
global diffencoding
6585
6586
$ctext conf -state normal
@@ -6465,6 +6599,8 @@ proc getmergediffline {mdf id np} {
6599
lappend difffilestart $here
6600
lappend treediffs($id) $fname
6601
add_flist [list $fname]
6602
+ lappend ctext_file_names $fname
6603
+ lappend ctext_file_lines [lindex [split $here "."] 0]
6604
set diffencoding [get_path_encoding $fname]
6605
set l [expr {(78 - [string length $fname]) / 2}]
6606
set pad [string range "----------------------------------------" 1 $l]
@@ -6733,11 +6869,13 @@ proc setinlist {var i val} {
6869
6870
proc makediffhdr {fname ids} {
6871
global ctext curdiffstart treediffs
6872
+ global ctext_file_names
6873
6874
set i [lsearch -exact $treediffs($ids) $fname]
6875
if {$i >= 0} {
6876
setinlist difffilestart $i $curdiffstart
6877
}
6878
+ set ctext_file_names [lreplace $ctext_file_names end end $fname]
6879
set l [expr {(78 - [string length $fname]) / 2}]
6880
set pad [string range "----------------------------------------" 1 $l]
6881
$ctext insert $curdiffstart "$pad $fname $pad" filesep
@@ -6746,6 +6884,7 @@ proc makediffhdr {fname ids} {
6884
proc getblobdiffline {bdf ids} {
6885
global diffids blobdifffd ctext curdiffstart
6886
global diffnexthead diffnextnote difffilestart
6887
+ global ctext_file_names ctext_file_lines
6888
global diffinhdr treediffs
6889
global diffencoding
6890
@@ -6763,6 +6902,8 @@ proc getblobdiffline {bdf ids} {
6902
# start of a new file
6903
$ctext insert end "\n"
6904
set curdiffstart [$ctext index "end - 1c"]
6905
+ lappend ctext_file_names ""
6906
+ lappend ctext_file_lines [lindex [split $curdiffstart "."] 0]
6907
$ctext insert end "\n" filesep
6908
# If the name hasn't changed the length will be odd,
6909
# the middle char will be a space, and the two bits either
@@ -6899,6 +7040,7 @@ proc nextfile {} {
7040
7041
proc clear_ctext {{first 1.0}} {
7042
global ctext smarktop smarkbot
7043
+ global ctext_file_names ctext_file_lines
7044
global pendinglinks
7045
7046
set l [lindex [split $first .] 0]
@@ -6912,6 +7054,8 @@ proc clear_ctext {{first 1.0}} {
7054
if {$first eq "1.0"} {
7055
catch {unset pendinglinks}
7056
}
7057
+ set ctext_file_names {}
7058
+ set ctext_file_lines {}
7059
}
7060
7061
proc settabs {{firstab {}}} {