From: Dmitry Gutov <dgutov@yandex.ru>
To: "Nicolás Ojeda Bär" <n.oje.bar@gmail.com>
Cc: 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: Thu, 14 Dec 2023 02:52:23 +0200 [thread overview]
Message-ID: <e8d60bed-9ac4-8569-49ef-cf3aa74f32a2@yandex.ru> (raw)
In-Reply-To: <abcdc8ac-e123-d434-e303-b57b8469bab3@yandex.ru>
[-- Attachment #1: Type: text/plain, Size: 1902 bytes --]
On 13/12/2022 03:23, Dmitry Gutov wrote:
>> I read your message, and I think immitating GitHub's UI is a great
>> idea, as it liberates us from having to do any kind of "pretreatment"
>> of the git log (which can be expensive for large repos).
>
> Indeed. I suppose we'll lose out in some more complex cases (e.g. if
> --follow tracks copies, it might track the cases when several files had
> been copied into one, including when that action spanned several
> commits; and thus --follow could show the history of each such file),
> but we should win in the most common cases (single-file copies and
> renames) OOtB, which we don't have any good support for still.
>
>> We should focus in this direction to fix this issue. If I get some
>> spare time over the holidays I will try to take a look (sadly, I
>> cannot promise that will be the case...).
>
> We won't be able to put the improvement into Emacs 29 anyway (the
> release branch has been cut, it's now bugfix-only), so there is no hurry.
>
> Let's see who gets to this first. If you wanted to finish up your patch
> instead, I'm not going to say no either. But GitHub's approach seems
> like it should require less (and less complex) code.
Attached is the implementation for this alternative approach.
It's Git-only, but otherwise seems to function well (with potential for
future additions). The look of the message and the button could use some
work, but this is the best I came up thus far.
As far as testing, it allowed following all files in Emacs's repo (that
I have tried) to their original creation. Including etc/NEWS.29, and
most other NEWS.*, with NEWS.28 being an exception (apparently because
on that occasion NEWS was truncated before NEWS.28 was created).
Also important: set vc-git-print-log-follow to nil. Otherwise logs don't
end on renames, and this feature doesn't get a chance to work.
Cheers,
Dmitry.
[-- Attachment #2: vc-git-file-name-changes.diff --]
[-- Type: text/x-patch, Size: 7123 bytes --]
diff --git a/etc/NEWS b/etc/NEWS
index 1ff2f8a149f..4dd11c99927 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -457,6 +457,14 @@ With this value only the revision number is displayed on the mode-line.
*** Obsolete command 'vc-switch-backend' re-added as 'vc-change-backend'.
The command was previously obsoleted and unbound in Emacs 28.
+*** Support for viewing file change history across renames.
+When a fileset's VC change history ends at a rename, we now print the
+old name(s) and a button which jumps to their history. Only supported
+with Git at the moment.
+
+*** New option 'vc-git-file-name-changes-switches'.
+It allows tweaking the thresholds for rename and copy detection.
+
** Diff mode
+++
diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index 2e057ecfaa7..e2c2ed5c79c 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -89,6 +89,7 @@
;; - make-version-backups-p (file) NOT NEEDED
;; - previous-revision (file rev) OK
;; - next-revision (file rev) OK
+;; - file-name-changes (rev) OK
;; - check-headers () COULD BE SUPPORTED
;; - delete-file (file) OK
;; - rename-file (old new) OK
@@ -152,6 +153,20 @@ vc-git-shortlog-switches
(repeat :tag "Argument List" :value ("") string))
:version "30.1")
+;; XXX: (setq vc-git-log-switches '("--simplify-merges")) can also
+;; create fuller history when using this feature. Not sure why.
+(defcustom vc-git-file-name-changes-switches '("-M" "-C")
+ "String or list of string to pass to Git when finding previous names.
+
+This option should usually at least contain '-M'. You can adjust
+the flags to change the similarity thresholds (default 50%). Or
+add `--find-copies-harder' (slower in large projects, since it
+uses a full scan)."
+ :type '(choice (const :tag "None" nil)
+ (string :tag "Argument String")
+ (repeat :tag "Argument List" :value ("") string))
+ :version "30.1")
+
(defcustom vc-git-resolve-conflicts t
"When non-nil, mark conflicted file as resolved upon saving.
That is performed after all conflict markers in it have been
@@ -1239,6 +1254,30 @@ vc-git-find-revision
nil
"cat-file" "blob" (concat (if rev rev "HEAD") ":" fullname))))
+(defun vc-git-file-name-changes (rev)
+ (with-temp-buffer
+ (let ((root (vc-git-root default-directory)))
+ (apply #'vc-git-command (current-buffer) t nil
+ "diff"
+ "--name-status"
+ "--diff-filter=ADCR"
+ (concat rev "^") rev
+ (vc-switches 'git 'file-name-changes))
+ (let (res)
+ (goto-char (point-min))
+ (while (re-search-forward "^\\([CMR]\\)[0-9]*\t\\([^\n\t]+\\)\\(?:\t\\([^\n\t]+\\)\\)?" nil t)
+ (pcase (match-string 1)
+ ("A" (push (cons nil (match-string 2)) res))
+ ("D" (push (cons (match-string 2) nil) res))
+ ((or "C" "R") (push (cons (match-string 2) (match-string 3)) res))
+ ;; ("M" (push (cons (match-string 1) (match-string 1)) res))
+ ))
+ (mapc (lambda (c)
+ (if (car c) (setcar c (expand-file-name (car c) root)))
+ (if (cdr c) (setcdr c (expand-file-name (cdr c) root))))
+ res)
+ (nreverse res)))))
+
(defun vc-git-find-ignore-file (file)
"Return the git ignore file that controls FILE."
(expand-file-name ".gitignore"
@@ -1416,7 +1455,15 @@ vc-git-clone
;; Long explanation here:
;; https://stackoverflow.com/questions/46487476/git-log-follow-graph-skips-commits
(defcustom vc-git-print-log-follow nil
- "If true, follow renames in Git logs for a single file."
+ "If true, use the flag `--follow' when producing single file logs.
+
+It will make the printed log automatically follow the renames.
+The downsides is that the log produced this way may omit
+certain (merge) commits, and that `log-view-diff' fails on
+commits that used the previous name, in that log buffer.
+
+When this variable is nil, and the log ends with a rename, we
+print a button that shows the log for the previous name."
:type 'boolean
:version "26.1")
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index 958929fe4c6..e626d72d59a 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -517,6 +517,13 @@
;; Return the revision number that precedes REV for FILE, or nil if no such
;; revision exists.
;;
+;; - file-name-changes (rev)
+;;
+;; 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.
+;;
;; - next-revision (file rev)
;;
;; Return the revision number that follows REV for FILE, or nil if no such
@@ -2695,9 +2702,42 @@ vc-print-log-setup-buttons
(goto-char (point-min))
(while (re-search-forward log-view-message-re nil t)
(cl-incf entries))
- ;; If we got fewer entries than we asked for, then displaying
- ;; the "more" buttons isn't useful.
- (when (>= entries limit)
+ (if (< entries limit)
+ ;; The log has been printed in full. Perhaps it started
+ ;; with a copy or rename?
+ (let* ((last-revision (log-view-current-tag (point-max)))
+ ;; Could skip this when vc-git-print-log-follow = t.
+ (name-changes
+ (condition-case nil
+ (vc-call-backend log-view-vc-backend
+ 'file-name-changes last-revision)
+ (vc-not-supported nil)))
+ (matching-changes
+ (cl-delete-if-not (lambda (f) (member f log-view-vc-fileset))
+ name-changes :key #'cdr))
+ (old-names (mapcar #'car matching-changes))
+ (relatives (mapcar #'file-relative-name old-names)))
+ (when old-names
+ (goto-char (point-max))
+ (insert "\n")
+ (insert
+ (format
+ "Renamed from %s"
+ (mapconcat (lambda (s)
+ (propertize s 'font-lock-face
+ 'log-view-file))
+ relatives ", "))
+ " ")
+ ;; TODO: Also print a "Next log" button above the buffer
+ ;; created by this button to be able to go back quickly.
+ (insert-text-button
+ "View log"
+ 'action (lambda (&rest _ignore)
+ (vc-print-log-internal log-view-vc-backend old-names
+ last-revision nil limit))
+ 'help-echo
+ "Show the log for the file name(s) before the rename")))
+ ;; Perhaps there are more entries in the log.
(goto-char (point-max))
(insert "\n")
(insert-text-button
next prev parent reply other threads:[~2023-12-14 0:52 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 ` bug#55871: Acknowledgement (27.1; vc-git.el log view 'a', 'f', 'd' do not work when following renames) Nicolás Ojeda Bär
2022-08-18 2:10 ` 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 [this message]
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
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e8d60bed-9ac4-8569-49ef-cf3aa74f32a2@yandex.ru \
--to=dgutov@yandex.ru \
--cc=55871@debbugs.gnu.org \
--cc=n.oje.bar@gmail.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.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.