git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v5] gitk: add external diff file rename detection

From
TGToBoMi via GitGitGadget <gitgitgadget@gmail.com>
Date
Jun 10, 2025, 08:29 UTC
Message-ID
<pull.1774.v5.git.1749544174590.gitgitgadget@gmail.com>
In-Reply-To
<pull.1774.v4.git.1745830037917.gitgitgadget@gmail.com>
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
Previous: tobias.boesch@miele.comNext: tobias.boesch@miele.com
Message 10 of 17 in “gitk: added external diff file rename detection”
  1. gitk: added external diff file rename detectionToBoMi via GitGitGadget, Aug 22, 2024
  2. gitk: added external diff file rename detectionToBoMi via GitGitGadget, Sep 6, 2024
  3. AW: [PATCH v2] gitk: added external diff file rename detectiontobias.boesch@miele.com, Oct 2, 2024
  4. gitk: added external diff file rename detectionToBoMi via GitGitGadget, Mar 4, 2025
  5. Johannes SixtMar 16, 2025
  6. AW: [PATCH v3] gitk: added external diff file rename detectiontobias.boesch@miele.com, Apr 28, 2025
  7. gitk: add external diff file rename detectionToBoMi via GitGitGadget, Apr 28, 2025
  8. Johannes SixtMay 6, 2025
  9. AW: [PATCH v4] gitk: add external diff file rename detectiontobias.boesch@miele.com, Jun 10, 2025
  10. gitk: add external diff file rename detectionToBoMi via GitGitGadget, Jun 10, 2025
  11. AW: [PATCH v5] gitk: add external diff file rename detectiontobias.boesch@miele.com, Jun 13, 2025
  12. gitk: add external diff file rename detectionToBoMi via GitGitGadget, Jun 24, 2025
  13. Johannes SixtJun 25, 2025
  14. gitk: add external diff file rename detectionToBoMi via GitGitGadget, Oct 31, 2025
  15. Johannes SixtNov 4, 2025
  16. gitk: add external diff file rename detectionToBoMi via GitGitGadget, Nov 6, 2025
  17. Johannes SixtNov 6, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.