git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "ToBoMi via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Johannes Sixt <j6t@kdbg.org>,
	tobias.boesch@miele.com, ToBoMi <tobias.boesch@miele.com>,
	Tobias Boesch <tobias.boesch@miele.com>
Subject: [PATCH v7] gitk: add external diff file rename detection
Date: Fri, 31 Oct 2025 10:09:31 +0000	[thread overview]
Message-ID: <pull.1774.v7.git.1761905371163.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.1774.v6.git.1750755954011.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.

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
    
    Changes sine v5:
    
     * Include filename in rename check. Find only the file and its renamed
       version that is selected in the GUI.
     * Escape special characters in the filename to prevent that they are
       intepreted as part of a regular expression
    
    Changes since v6:
    
     * Don't extra mention moved files in commit message
     * Create empty list properly (avoid creting a list containing an empty
       string)
     * Avoid snake case in variable names
     * Change logic that checks the renames and gathers the file paths to
       use the difffilestart variable for efficient file path extraction
     * Removed underscores in file names

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1774%2FToBoMi%2Fdetect_renamed_files_when_opening_diff-v7
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1774/ToBoMi/detect_renamed_files_when_opening_diff-v7
Pull-Request: https://github.com/gitgitgadget/git/pull/1774

Range-diff vs v6:

 1:  f1c71e5632 ! 1:  7c09a28951 gitk: add external diff file rename detection
     @@ Commit message
          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-git/gitk: proc external_diff_get_one_file {diffid filename diffdir} {
                      "revision $diffid"]
       }
       
     -+proc check_for_renames_in_diff {diffidfrom diffidto filepath} {
     -+    global nullid nullid2
     ++proc check_for_renames_in_diff {filepath} { # renames
     ++    global difffilestart ctext
      +
     -+    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 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
     ++    set renames {}
     ++    
     ++    foreach loc $difffilestart {
     ++        set loclineend [string map {.0 .end} $loc]
     ++        set ctextline [$ctext get $loc $loclineend]
     ++        if {[string first $filename $ctextline] != -1} {
     ++            set fromlineloc "$loc + 2 lines"
     ++            set tolineloc "$loc + 3 lines"
     ++            set renfromline [$ctext get $fromlineloc [string map {.0 .end} $fromlineloc]]
     ++            set rentoline [$ctext get $tolineloc [string map {.0 .end} $tolineloc]]
     ++            if {[string match "rename from *" $renfromline]
     ++                && [string match "rename to *" $rentoline]} {
     ++                set renfrom [string range $renfromline 12 end]
     ++                set rento [string range $rentoline 10 end]
     ++                lappend renames $renfrom
     ++                lappend renames $rento
     ++                break
     ++            }
      +        }
      +    }
     ++    
      +    return $renames
      +}
      +
     @@ 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 $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]
     ++    set renames [check_for_renames_in_diff $flist_menu_file]
     ++    set renamefrom [lindex $renames 0]
     ++    set renameto [lindex $renames 1]
     ++    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]


 gitk-git/gitk | 40 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 38 insertions(+), 2 deletions(-)

diff --git a/gitk-git/gitk b/gitk-git/gitk
index c02db0194d..8f8f1cb9ab 100755
--- a/gitk-git/gitk
+++ b/gitk-git/gitk
@@ -3788,6 +3788,34 @@ proc external_diff_get_one_file {diffid filename diffdir} {
                "revision $diffid"]
 }
 
+proc check_for_renames_in_diff {filepath} { # renames
+    global difffilestart ctext
+
+    set filename [file tail $filepath]
+    set renames {}
+    
+    foreach loc $difffilestart {
+        set loclineend [string map {.0 .end} $loc]
+        set ctextline [$ctext get $loc $loclineend]
+        if {[string first $filename $ctextline] != -1} {
+            set fromlineloc "$loc + 2 lines"
+            set tolineloc "$loc + 3 lines"
+            set renfromline [$ctext get $fromlineloc [string map {.0 .end} $fromlineloc]]
+            set rentoline [$ctext get $tolineloc [string map {.0 .end} $tolineloc]]
+            if {[string match "rename from *" $renfromline]
+                && [string match "rename to *" $rentoline]} {
+                set renfrom [string range $renfromline 12 end]
+                set rento [string range $rentoline 10 end]
+                lappend renames $renfrom
+                lappend renames $rento
+                break
+            }
+        }
+    }
+    
+    return $renames
+}
+
 proc external_diff {} {
     global nullid nullid2
     global flist_menu_file
@@ -3818,8 +3846,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 $flist_menu_file]
+    set renamefrom [lindex $renames 0]
+    set renameto [lindex $renames 1]
+    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]

base-commit: 57da342c786f59eaeb436c18635cc1c7597733d9
-- 
gitgitgadget

  parent reply	other threads:[~2025-10-31 10:09 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-08-22  9:27 [PATCH] gitk: added external diff file rename detection ToBoMi via GitGitGadget
2024-09-06  7:28 ` [PATCH v2] " ToBoMi via GitGitGadget
2024-10-02  9:47   ` AW: " tobias.boesch
2025-03-04 13:01   ` [PATCH v3] " ToBoMi via GitGitGadget
2025-03-16 16:21     ` Johannes Sixt
2025-04-28  8:52       ` AW: " tobias.boesch
2025-04-28  8:47     ` [PATCH v4] gitk: add " ToBoMi via GitGitGadget
2025-05-06 19:39       ` Johannes Sixt
2025-06-10  8:28         ` AW: " tobias.boesch
2025-06-10  8:29       ` [PATCH v5] " ToBoMi via GitGitGadget
2025-06-13  8:18         ` AW: " tobias.boesch
2025-06-24  9:05         ` [PATCH v6] " ToBoMi via GitGitGadget
2025-06-25  6:23           ` Johannes Sixt
2025-10-31 10:09           ` ToBoMi via GitGitGadget [this message]
2025-11-04 18:04             ` [PATCH v7] " Johannes Sixt
2025-11-06 14:42             ` [PATCH v8] " ToBoMi via GitGitGadget
2025-11-06 18:16               ` Johannes Sixt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=pull.1774.v7.git.1761905371163.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=j6t@kdbg.org \
    --cc=tobias.boesch@miele.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).