Subject: Re: bug#26345: [PATCH] Handle renamed files when cycling through revisions

From: Dmitry Gutov <[email-masked]>
Date: Tue, 29 Jul 2025 04:32:50 +0300

Hi all!

On 26/07/2025 23:14, Jakub Ječmínek via Bug reports for GNU Emacs, the 
Swiss army knife of text editors wrote:
> Sean Whitton<[email-masked]> writes:
> 
>>>> Seems like this needs to be investigated before we can proceed.
>>> I've tested various scenarios and haven't encountered any issues with the
>>> current implementation. It would be great if you could test the change as
>>> well, in case there are edge cases I missed.
>> I think that we need to see an argument in terms of the definition of
>> the file-name-changes backend function that demonstrates that your
>> approach will always work:
>>
>> ;;   Return the list of pairs with changes in file names in REV.  When
>> ;;   a file was added, it should be a cons with nil car.  When
>> ;;   deleted, a cons with nil cdr.  When copied or renamed, a cons
>> ;;   with the source name as car and destination name as cdr.
> My approach updates the FILE argument only if the file name has changed
> in the given revision; otherwise, it keeps the name unchanged. I’m not
> sure if it will **always** work, but it does fix the issue at hand. I'm
> sorry, but I can't provide any further arguments in favor of this
> change.

Looking over the bug report again, my previous question was off the 
mark: it was about longer jumps over revisions - but the 'j' and 'a' 
commands (which are probably the only examples of such in the tree) 
specify the target file name explicitly (getting it from the buffer 
contents), so they should be okay. For the 'n' and 'p' commands, it 
would usually be fine because the step is often 1 revision.

Anyway, that patch seems to fix stepping forward once across a name 
change, but not farther, nor stepping backward over such a change.

To reproduce, add steps like this to the original setup:

   $ echo "
morecontent" > newdir/file.txt
   $ git commit -m 'Edit after the move, adding second line'

And then this to the reproduction:

   5. Press 'n' again.
   6. Press 'p' twice.

It seems the fix is to move the change from 'vc-annotate' to 
'vc-annotate-warp-revision' (previously mentioned by Jakub), and to 
update the new function to handle both directions.

Please try out the the attached update, it seems handle both.

I did some quick testing with Mercurial too.

diff --git a/lisp/vc/vc-annotate.el b/lisp/vc/vc-annotate.el
index e68c5a79919..6bed36c99dc 100644
--- a/lisp/vc/vc-annotate.el
+++ b/lisp/vc/vc-annotate.el
@@ -667,6 +667,11 @@ vc-annotate-warp-revision
                                    (or file
                                        (caadr vc-buffer-overriding-fileset))
                                    newrev))
+            (setq file (vc-annotate--file-name-change
+                        (or file
+                            (caadr vc-buffer-overriding-fileset))
+                        newrev
+                        vc-annotate-backend))
             (setq revspec (1- revspec))))
 	(unless newrev
 	  (message "Cannot increment %d revisions from revision %s"
@@ -675,6 +680,12 @@ vc-annotate-warp-revision
 	(setq newrev vc-buffer-revision)
         (let ((vc-use-short-revision vc-annotate-use-short-revision))
 	  (while (and (< revspec 0) newrev)
+            (setq file (vc-annotate--file-name-change
+                        (or file
+                            (caadr vc-buffer-overriding-fileset))
+                        newrev
+                        vc-annotate-backend
+                        'backward))
             (setq newrev
                   (vc-call-backend vc-annotate-backend 'previous-revision
                                    (or file
@@ -804,6 +815,21 @@ vc-annotate-goto-line
       (message "Annotations were for revision %s; line numbers may be incorrect"
 	       rev)))))
 
+(defun vc-annotate--file-name-change (file rev backend &optional backward)
+  "Return the name of FILE at revision REV using BACKEND.
+If the file name has changed in the given revision, return the new name;
+otherwise, return FILE unchanged.
+If BACKWARD, return the name for FILE before REV."
+  (if (vc-find-backend-function backend 'file-name-changes)
+      (or
+       (if backward
+           (car (rassoc file
+                        (vc-call-backend backend 'file-name-changes rev)))
+         (cdr (assoc file
+                     (vc-call-backend backend 'file-name-changes rev))))
+       file)
+    file))
+
 (provide 'vc-annotate)
 
 ;;; vc-annotate.el ends here