From 11a1aaf342fa6b1259b8ef7d53aff767d82e4c2e Mon Sep 17 00:00:00 2001 From: Tobias Boesch Date: Thu, 5 Sep 2024 10:03:25 +0200 Subject: [PATCH] gitk: add external diff file rename detection If a file is renamed between commits and an external diff is started through gitk on the original or the renamed file name, gitk is unable to open the renamed file in the external diff editor. It fails to fetch the renamed file from git, because it fetches it using its original path in contrast to using the renamed path of the file. Detect the rename and open the external diff with the original and the renamed file instead of no file (fetch the renamed file path and name from git) no matter if the original or the renamed file is selected in gitk. Signed-off-by: Tobias Boesch --- gitk-git/gitk | 59 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/gitk-git/gitk b/gitk-git/gitk index 19689765cde531..457949e14d17aa 100755 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -3775,6 +3775,53 @@ proc external_diff_get_one_file {diffid filename diffdir} { "revision $diffid"] } +proc check_for_renames_in_diff {diffidfrom diffidto filepath} { + global nullid nullid2 + + if {$diffidfrom eq $nullid} { + set rev [list $diffidto] + set cmd [list diff-index -R] + } elseif {$diffidfrom eq $nullid2} { + set rev [list $diffidto] + set cmd [list diff-index --cached -R] + } elseif {$diffidto eq $nullid} { + set rev [list $diffidfrom] + set cmd [list diff-index] + } elseif {$diffidto eq $nullid2} { + set rev [list $diffidfrom] + set cmd [list diff-index --cached] + } else { + set rev [list $diffidfrom..$diffidto] + set cmd [list diff-tree] + } + + set renames [list {}] + if {[catch {eval exec git $cmd --find-renames --stat --raw --diff-filter=R $rev} cmd_result]} { + error_popup "[mc "Error getting file rename info for file \"%s\" from commit %s to %s." \ + $filepath $diffidfrom $diffidto] $cmd_result.\n\n" + } + set filename [file tail $filepath] + set esc_chars {\\ | ? ^ * . $ \[ \] + \( \) \{ \}} + foreach char $esc_chars { + set filename [string map [list $char \\$char] $filename] + } + set regex_base {\d+\s\d+\s\S+\s\S+\s\S+\s+} + set regex_ren_from $regex_base[subst -nobackslashes -nocommands {(\S+$filename)\s+(\S+)}] + set regex_ren_to $regex_base[subst -nobackslashes -nocommands {(\S+)\s+(\S+$filename)}] + if {[regexp -line -- $regex_ren_from $cmd_result whole_match ren_from ren_to]} { + if {$ren_from ne {} && $ren_to ne {}} { + lappend renames $ren_from + lappend renames $ren_to + } + } elseif {[regexp -line -- $regex_ren_to $cmd_result whole_match ren_from ren_to]} { + if {$ren_from ne {} && $ren_to ne {}} { + lappend renames $ren_from + lappend renames $ren_to + } + } + return $renames +} + proc external_diff {} { global nullid nullid2 global flist_menu_file @@ -3805,8 +3852,16 @@ proc external_diff {} { if {$diffdir eq {}} return # gather files to diff - set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir] - set difftofile [external_diff_get_one_file $diffidto $flist_menu_file $diffdir] + set renames [check_for_renames_in_diff $diffidfrom $diffidto $flist_menu_file] + set renamefrom [lindex $renames 1] + set renameto [lindex $renames 2] + if { ($renamefrom != {}) && ($renameto != {}) } { + set difffromfile [external_diff_get_one_file $diffidfrom $renamefrom $diffdir] + set difftofile [external_diff_get_one_file $diffidto $renameto $diffdir] + } else { + set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir] + set difftofile [external_diff_get_one_file $diffidto $flist_menu_file $diffdir] + } if {$difffromfile ne {} && $difftofile ne {}} { set cmd [list [shellsplit $extdifftool] $difffromfile $difftofile]