AW: [PATCH v5] gitk: add external diff file rename detection
- From
tobias.boesch@miele.com <tobias.boesch@miele.com>
- Date
- Jun 13, 2025, 08:18 UTC
- Message-ID
- <AS2PR08MB8288190B1A6A04CAF89E1228E177A@AS2PR08MB8288.eurprd08.prod.outlook.com>
- In-Reply-To
- <pull.1774.v5.git.1749544174590.gitgitgadget@gmail.com>
Hi Hannes,
please ignore the V5 patch. I totally missed to check for the file of interest when checking for renames. I'll correct that and send an update.
Best wishes Tobias
Show 238 quoted lines
> -----Ursprüngliche Nachricht-----
> Von: ToBoMi via GitGitGadget <gitgitgadget@gmail.com>
> Gesendet: Dienstag, 10. Juni 2025 10:30
> An: git@vger.kernel.org
> Cc: Johannes Sixt <j6t@kdbg.org>; Boesch, Tobias
> <tobias.boesch@miele.com>; Boesch, Tobias <tobias.boesch@miele.com>;
> Boesch, Tobias <tobias.boesch@miele.com>
> Betreff: [PATCH v5] gitk: add external diff file rename detection
>
> From: Tobias Boesch <tobias.boesch@miele.com>
>
> 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.
> Since moved or renamed file are handled the same way do this also for moved
> files.
>
> Signed-off-by: Tobias Boesch <tobias.boesch@miele.com>
> ---
> gitk: add external diff file rename detection
>
> Changes since v1:
>
> * Commit message ident
> * Commit message line length
>
> Changes since v2:
>
> * Removed option for rename detection (Adding GUI options seems to be
> not desired - which is understandable)
> * Rebased on current master of git-for-windows
> * Renamed variables for a better understanding
> * Made rename detection also work when the renamed file is selected in
> gitk
>
> Changes since v3:
>
> * Changed message to use present tense, removed bullet points and
> described changes in imperative mood
>
> Changes sine v4:
>
> * Use a git command to gather the changed file paths rather than
> parsing the text from the diff window panel for efficiency and to
> avoid regex containing the filename as a variable.
> * Change != to ne in string comparison
> * removed extra set of parentheses around &&
> * shorter variable names
>
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-
> 1774%2FToBoMi%2Fdetect_renamed_files_when_opening_diff-v5
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-
> 1774/ToBoMi/detect_renamed_files_when_opening_diff-v5
> Pull-Request: https://github.com/gitgitgadget/git/pull/1774
>
> Range-diff vs v4:
>
> 1: 948b94bef5c ! 1: 0d28f189dc3 gitk: add external diff file rename
> detection
> @@ gitk-git/gitk: proc external_diff_get_one_file {diffid filename diffdir} {
> "revision $diffid"]
> }
>
> -+proc check_for_renames_in_diff {filepath} {
> -+ global ctext
> ++proc check_for_renames_in_diff {diffidfrom diffidto filepath} {
> ++ global nullid nullid2
> +
> -+ set renamed_filenames [list {}]
> -+ set filename [file tail $filepath]
> -+ set rename_from_text_identifier_length 12
> -+ set rename_to_text_identifier_length 10
> -+ set reg_expr_rename_from {^rename from (.*$filename)}
> -+ set reg_expr_rename_from [subst -nobackslashes -nocommands
> $reg_expr_rename_from]
> -+ set rename_from_text_index [$ctext search -elide -regexp --
> $reg_expr_rename_from 0.0]
> -+ if { ($rename_from_text_index != {})} {
> -+ set reg_expr_rename_to {^rename to (.*)}
> -+ set rename_to_text_index [$ctext search -elide -regexp --
> $reg_expr_rename_to $rename_from_text_index]
> -+ if { ($rename_from_text_index != {}) && ($rename_to_text_index !=
> {}) } {
> -+ lappend renamed_filenames [$ctext get
> "$rename_from_text_index + $rename_from_text_identifier_length chars"
> "$rename_from_text_index lineend"]
> -+ lappend renamed_filenames [$ctext get "$rename_to_text_index +
> $rename_to_text_identifier_length chars" "$rename_to_text_index lineend"]
> -+ }
> -+ return $renamed_filenames
> ++ if {$diffidfrom eq $nullid} {
> ++ set rev [list $diffidto -R]
> ++ } elseif {$diffidfrom eq $nullid2} {
> ++ set rev [list $diffidto --cached -R]
> ++ } elseif {$diffidto eq $nullid} {
> ++ set rev [list $diffidfrom]
> ++ } elseif {$diffidto eq $nullid2} {
> ++ set rev [list $diffidfrom --cached]
> ++ } else {
> ++ set rev [list $diffidfrom..$diffidto]
> + }
> -+ set reg_expr_rename_to {^rename to (.*$filename)}
> -+ set reg_expr_rename_to [subst -nobackslashes -nocommands
> $reg_expr_rename_to]
> -+ set rename_to_text_index [$ctext search -elide -regexp --
> $reg_expr_rename_to 0.0]
> -+ if { ($rename_to_text_index != {})} {
> -+ set reg_expr_rename_from {^rename from (.*)}
> -+ set rename_from_text_index [$ctext search -backwards -elide -regexp
> -- $reg_expr_rename_from $rename_to_text_index]
> -+ if { ($rename_to_text_index != {}) && ($rename_from_text_index !=
> {}) } {
> -+ lappend renamed_filenames [$ctext get
> "$rename_from_text_index + $rename_from_text_identifier_length chars"
> "$rename_from_text_index lineend"]
> -+ lappend renamed_filenames [$ctext get "$rename_to_text_index +
> $rename_to_text_identifier_length chars" "$rename_to_text_index lineend"]
> ++
> ++ set renames [list {}]
> ++ if {[catch {eval exec git diff $rev --find-renames --stat --raw --diff-
> filter=R} 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 regex_ren {\d+\s\d+\s\S+\s\S+\s\S+\s+(\S+)\s+(\S+)}
> ++ set regex_ren [subst -nobackslashes -nocommands $regex_ren]
> ++ if {[regexp -line -- $regex_ren $cmd_result whole_match ren_from
> ren_to]} {
> ++ if {$ren_from ne {} && $ren_to ne {}} {
> ++ lappend renames $ren_from
> ++ lappend renames $ren_to
> + }
> -+ return $renamed_filenames
> + }
> ++ return $renames
> +}
> +
> proc external_diff {} {
> @@ gitk-git/gitk: proc external_diff {} {
> # 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 renamed_filenames [check_for_renames_in_diff $flist_menu_file]
> ++ set renamed_filenames [check_for_renames_in_diff $diffidfrom
> $diffidto $flist_menu_file]
> + set rename_from_filename [lindex $renamed_filenames 1]
> + set rename_to_filename [lindex $renamed_filenames 2]
> + if { ($rename_from_filename != {}) && ($rename_to_filename != {}) } {
>
>
> gitk-git/gitk | 44 ++++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 42 insertions(+), 2 deletions(-)
>
> diff --git a/gitk-git/gitk b/gitk-git/gitk index 19689765cde..f97904f5fa2
> 100755
> --- a/gitk-git/gitk
> +++ b/gitk-git/gitk
> @@ -3775,6 +3775,38 @@ 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 -R]
> + } elseif {$diffidfrom eq $nullid2} {
> + set rev [list $diffidto --cached -R]
> + } elseif {$diffidto eq $nullid} {
> + set rev [list $diffidfrom]
> + } elseif {$diffidto eq $nullid2} {
> + set rev [list $diffidfrom --cached]
> + } else {
> + set rev [list $diffidfrom..$diffidto]
> + }
> +
> + set renames [list {}]
> + if {[catch {eval exec git diff $rev --find-renames --stat --raw --diff-filter=R}
> 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 regex_ren {\d+\s\d+\s\S+\s\S+\s\S+\s+(\S+)\s+(\S+)}
> + set regex_ren [subst -nobackslashes -nocommands $regex_ren]
> + if {[regexp -line -- $regex_ren $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 +3837,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 renamed_filenames [check_for_renames_in_diff $diffidfrom $diffidto
> $flist_menu_file]
> + set rename_from_filename [lindex $renamed_filenames 1]
> + set rename_to_filename [lindex $renamed_filenames 2]
> + if { ($rename_from_filename != {}) && ($rename_to_filename != {}) } {
> + set difffromfile [external_diff_get_one_file $diffidfrom
> $rename_from_filename $diffdir]
> + set difftofile [external_diff_get_one_file $diffidto $rename_to_filename
> $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]
>
> base-commit: 14de3eb34435db79c6e7edc8082c302a26a8330a
> --
> gitgitgadget------------------------------------------------------------------------------------------------- imperial-Werke oHG, Sitz Bünde, Registergericht Bad Oeynhausen - HRA 4825