unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: "Nicolás Ojeda Bär" <n.oje.bar@gmail.com>
To: 55871@debbugs.gnu.org
Subject: bug#55871: Acknowledgement (27.1; vc-git.el log view 'a', 'f', 'd' do not work when following renames)
Date: Fri, 10 Jun 2022 19:31:55 +0200	[thread overview]
Message-ID: <CAPunWhAX4_tj7h4OXR_qnNUT9XJN3718GjaBw+yEujsp5gAjMQ@mail.gmail.com> (raw)
In-Reply-To: <handler.55871.B.16547851264967.ack@debbugs.gnu.org>

[-- Attachment #1: Type: text/plain, Size: 829 bytes --]

Dear all,

Attached is a patch that solves some of the problems in this issue (not all).

Its main feature is that it should not modify anything if you are not
using `vc-git-print-log-follow`.

If you are, the patch makes it possible to use `a`, `f` and `d` from
inside the `*vc-change-log*` buffer.

The patch itself is very much based on the one proposed in
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=8756#53, with some
changes to make sure that nothing changes if `vc-git-print-log-follow`
is not being used. Additionally, a horrible HACK (see commit message)
is used to allow `vc-git.el` to discriminate between `d` and `D` in
the `*vc-change-log*` buffer, as these two functions require different
Git commands to be executed when following renames (this is also
discussed in the just linked bug report).

Cheers,
Nicolas

[-- Attachment #2: 0001-vc-git.el-better-support-for-follow-mode.patch --]
[-- Type: application/octet-stream, Size: 8087 bytes --]

From d52f16607fcca4f80f1720b863ab920d15d9c0bb Mon Sep 17 00:00:00 2001
From: nojebar <nicolas.ojeda.bar@lexifi.com>
Date: Fri, 10 Jun 2022 16:16:26 +0200
Subject: [PATCH] vc-git.el: better support for "follow" mode

* lisp/vc/vc-git.el (vc-print-log): generate map between SHA-1's and
filenames when printing the log of a single file in "follow" mode.
(vc-git-find-revision): use mapping to find the correct filename to
checkout when pressing 'f' in '*vc-change-log*'.
(vc-git-diff): use mapping to find the correct filenames to diff when
computing diffs between two versions of a file from the
'*vc-change-log*' buffer.
(vc-git-annotate-command): use mapping to pass the right
filename when pressing 'a' in '*vc-change-log*'.
(vc-git-previous-revision): use mapping to find previous revision of a
file that has been renamed. Additionally a horrible hack (see next
point) is done so that this function can differentiate between 'd' and
'D' (whole changeset diffing) since these two operations require
different Git commands to be executed. This makes it possible to use
the 'd' command in '*vc-change-log*' after renaming a file.

* lisp/vc/log-view.el (log-view-diff-common): set a buffer-local
variable 'git-log-view-diff-whole-changeset' to let vc-git.el
differentiate between 'd' and 'D'.
---
 lisp/vc/log-view.el |   1 +
 lisp/vc/vc-git.el   | 101 ++++++++++++++++++++++++++++++++++++++------
 2 files changed, 89 insertions(+), 13 deletions(-)

diff --git a/lisp/vc/log-view.el b/lisp/vc/log-view.el
index 415b1564ed..9685883504 100644
--- a/lisp/vc/log-view.el
+++ b/lisp/vc/log-view.el
@@ -601,6 +601,7 @@ log-view-diff-changeset
     (log-view-diff-common beg end t)))
 
 (defun log-view-diff-common (beg end &optional whole-changeset)
+  (setq-local git-log-view-diff-whole-changeset whole-changeset)
   (let* ((to (log-view-current-tag beg))
          (fr-entry (log-view-current-entry end))
          (fr (cadr fr-entry)))
diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index 8937454d11..8634785a94 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -969,18 +969,77 @@ vc-git-checkin
 		    (if only (list "--only" "--") '("-a")))))
     (if (and msg-file (file-exists-p msg-file)) (delete-file msg-file))))
 
+;;; '--follow' HANDLING
+
+(defvar vc-git--shalist-raw nil)
+(defvar vc-git--shalist nil)
+
+(defun vc-git--make-shalist (buffer files start-revision limit)
+  "Store newline-separated list of revision hashes and file names
+in vc-git--shalist-raw buffer-local variable."
+  (setq-local vc-git--shalist-raw nil)
+  (setq-local vc-git--shalist nil)
+  (with-temp-buffer
+    (set-process-filter
+     (apply #'vc-git-command nil
+            'async files
+            (append
+             '("log"
+               "--follow"
+               "--name-only"
+               "--pretty=tformat:%H"
+               "--no-color")
+             ;; Tail revision must now its parent
+             (when limit (list "-n" (format "%s" (1+ limit))))
+             (when start-revision (list start-revision))
+             '("--")))
+     (lambda (_p s)
+       (with-current-buffer buffer
+         (setq-local
+          vc-git--shalist-raw
+          (replace-regexp-in-string
+           "\n\n" "\n"
+           (concat vc-git--shalist-raw s))))))))
+
+(defun vc-git--shalist ()
+  "Return alternating list of SHA1 hashes and file names.
+The list contains commit hashes and historical names for a file
+in the current change log buffer."
+  (let ((vc-change-log (get-buffer "*vc-change-log*")))
+    (when vc-change-log
+      (with-current-buffer vc-change-log
+        (cond
+         (vc-git--shalist vc-git--shalist)
+         (vc-git--shalist-raw
+          (setq-local vc-git--shalist
+                      (split-string vc-git--shalist-raw "\n"))))))))
+
+(defun vc-git--rev-to-filename (rev)
+  "Return a historical file name for the file in REV."
+  (when rev
+    (setq rev (vc-git--rev-parse rev))
+    (cadr (member rev (vc-git--shalist)))))
+
+(defun vc-git--rev-to-previous-rev (rev)
+  "Return the revision before REV according to historical file
+name data."
+  (when rev
+    (setq rev (vc-git--rev-parse rev))
+    (car (cddr (member rev (vc-git--shalist))))))
+
 (defun vc-git-find-revision (file rev buffer)
   (let* (process-file-side-effects
 	 (coding-system-for-read 'binary)
 	 (coding-system-for-write 'binary)
 	 (fullname
-	  (let ((fn (vc-git--run-command-string
-		     file "ls-files" "-z" "--full-name" "--")))
-	    ;; ls-files does not return anything when looking for a
-	    ;; revision of a file that has been renamed or removed.
-	    (if (string= fn "")
-		(file-relative-name file (vc-git-root default-directory))
-	      (substring fn 0 -1)))))
+          (or (vc-git--rev-to-filename rev)
+	      (let ((fn (vc-git--run-command-string
+		         file "ls-files" "-z" "--full-name" "--")))
+	        ;; ls-files does not return anything when looking for a
+	        ;; revision of a file that has been renamed or removed.
+	        (if (string= fn "")
+	            (file-relative-name file (vc-git-root default-directory))
+	          (substring fn 0 -1))))))
     (vc-git-command
      buffer 0
      nil
@@ -1182,6 +1241,7 @@ vc-git-print-log
                   ;; "--follow" on directories or multiple files is broken
                   ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=8756
                   ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=16422
+                  (vc-git--make-shalist buffer files start-revision limit)
                   (list "--follow"))
 		(when shortlog
 		  `("--graph" "--decorate" "--date=short"
@@ -1423,6 +1483,13 @@ vc-git-diff
         (unless rev1 (setq rev1 "4b825dc642cb6eb9a060e54bf8d69288fbee4904"))
       (setq command "diff-index")
       (unless rev1 (setq rev1 "HEAD")))
+    (let ((file1 (vc-git--rev-to-filename rev1))
+          (file2 (vc-git--rev-to-filename rev2)))
+      (when (and file1 file2)
+        ;; Run diff from the repository root because our file names are
+        ;; relative to it
+        (setq default-directory (vc-git-root default-directory)
+              files (list file1 file2))))
     (if vc-git-diff-switches
         (apply #'vc-git-command (or buffer "*vc-diff*")
 	       1 ; bug#21969
@@ -1459,7 +1526,9 @@ vc-git-revision-completion-table
 
 (defun vc-git-annotate-command (file buf &optional rev)
   (vc-git--asciify-coding-system)
-  (let ((name (file-relative-name file)))
+  (let ((name (vc-git--rev-to-filename rev)))
+    (if name (setq default-directory (vc-git-root default-directory))
+      (setq name (file-relative-name file)))
     (apply #'vc-git-command buf 'async nil "blame" "--date=short"
 	   (append (vc-switches 'git 'annotate)
 		   (list rev "--" name)))))
@@ -1505,6 +1574,10 @@ vc-git-retrieve-tag
 
 ;;; MISCELLANEOUS
 
+;; HACK: let log-view.el inform vc-git.el whether we are doing a
+;; "whole changeset" diff or not.
+(defvar git-log-view-diff-whole-changeset nil)
+
 (defun vc-git-previous-revision (file rev)
   "Git-specific version of `vc-previous-revision'."
   (if file
@@ -1520,11 +1593,13 @@ vc-git-previous-revision
                            (point)
                            (1- (point-max)))))))
         (or (vc-git-symbolic-commit prev-rev) prev-rev))
-    ;; We used to use "^" here, but that fails on MS-Windows if git is
-    ;; invoked via a batch file, in which case cmd.exe strips the "^"
-    ;; because it is a special character for cmd which process-file
-    ;; does not (and cannot) quote.
-    (vc-git--rev-parse (concat rev "~1"))))
+    ;; Use historical data for the file if possible.
+    (or (and (not git-log-view-diff-whole-changeset) (vc-git--rev-to-previous-rev rev))
+        ;; We used to use "^" here, but that fails on MS-Windows if git is
+        ;; invoked via a batch file, in which case cmd.exe strips the "^"
+        ;; because it is a special character for cmd which process-file
+        ;; does not (and cannot) quote.
+        (vc-git--rev-parse (concat rev "~1")))))
 
 (defun vc-git--rev-parse (rev)
   (with-temp-buffer
-- 
2.17.1


  parent reply	other threads:[~2022-06-10 17:31 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-06-09  9:54 bug#55871: 27.1; vc-git.el log view 'a', 'f', 'd' do not work when following renames Nicolás Ojeda Bär
     [not found] ` <handler.55871.B.16547851264967.ack@debbugs.gnu.org>
2022-06-10 17:31   ` Nicolás Ojeda Bär [this message]
2022-08-18  2:10     ` bug#55871: Acknowledgement (27.1; vc-git.el log view 'a', 'f', 'd' do not work when following renames) Dmitry Gutov
2022-09-06 10:56       ` bug#55871: 27.1; vc-git.el log view 'a', 'f', 'd' do not work when following renames Lars Ingebrigtsen
2022-09-06 12:12         ` Nicolás Ojeda Bär
2022-09-06 12:13           ` Lars Ingebrigtsen
2022-12-03  2:02           ` Dmitry Gutov
2022-12-11 23:02       ` bug#55871: Acknowledgement (27.1; vc-git.el log view 'a', 'f', 'd' do not work when following renames) Dmitry Gutov
2022-12-12 16:44         ` Nicolás Ojeda Bär
2022-12-13  1:23           ` Dmitry Gutov
2023-12-14  0:52             ` Dmitry Gutov
2023-12-14  1:23               ` Dmitry Gutov
2023-12-15  2:01                 ` Dmitry Gutov
2023-12-15 13:05                   ` Eli Zaretskii
2023-12-15 14:39                     ` Dmitry Gutov
2023-12-15 15:10                       ` Eli Zaretskii
2023-12-15 20:45                         ` Dmitry Gutov
2023-12-16  7:21                           ` Eli Zaretskii

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

  List information: https://www.gnu.org/software/emacs/

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

  git send-email \
    --in-reply-to=CAPunWhAX4_tj7h4OXR_qnNUT9XJN3718GjaBw+yEujsp5gAjMQ@mail.gmail.com \
    --to=n.oje.bar@gmail.com \
    --cc=55871@debbugs.gnu.org \
    /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.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

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 read-only IMAP folder(s) and NNTP newsgroup(s).