Subject: Re: Subject: Re: bug#21555: 25.0.50; per-project *vc-change-log* buffers

From: Matthias Meulien <[email-masked]>
Date: Sun, 25 May 2025 09:06:28 +0200

Le dim. 25 mai 2025 à 08:38, Juri Linkov <[email-masked]> a écrit :

> >>>> However, all calls to vc-print-log use this same buffer.
> >>>> This seems to be hard-coded in vc.el (vc-print-log-internal).
> >>>>
> >>>> I would like to be able to make it so that each use of "l" from a
> >>>> *vc-dir* buffer gets its own *vc-change-log* buffer; and that "C-x v
> l"
> >>>> in some source file gets a separate buffer.  This way the per-project
> >>>> change-log buffers could be relatively long-lived; as it is
> recomputing
> >>>> the data in them takes much too long.
> >>>
> >>> You can already do something similar by using C-x x u to rename the
> >>> *vc-change-log* buffer, and then 'g' inside it works to regenerate it.
> >>
> >> Or (add-hook 'log-view-mode-hook 'rename-uniquely)
> >
> > Note that while browsing a change log buffer, when one runs the command
> > `log-view-diff', the log buffer is not killed.
> >
> > For people like me who makes an heavy use of "C-x v l", "C-x v L" and "l"
> > in *vc-dir* buffers, it leads to have hundreds of *vc-change-log* buffers
> > at the end of the day…
>
> Indeed, hundreds of *vc-change-log* buffers is what I have.
> But not a problem.  On the contrary, preserved buffers often help
> to check for their previous content.
>

Ok, I'll try to live with that.

For the record I attach two patchs that enable per projet change log and
diff buffers.

-- 
Matthias
From 3f622e9a763308fa34fcb604849c517c92fd6be0 Mon Sep 17 00:00:00 2001
From: Matthias Meulien <[email-masked]>
Date: Sat, 24 May 2025 23:03:05 +0200
Subject: [PATCH 1/2] Per project change log buffer

* lisp/progmodes/project.el (project-vc-get-log-buffer-name):
(vc-get-log-buffer-name-function):
* lisp/vc/vc-annotate.el (vc-annotate-show-log-revision-at-line):
* lisp/vc/vc.el (vc-get-log-buffer-name-function):
(vc-get-log-buffer-name):
(vc-print-log-internal):
---
 lisp/progmodes/project.el |  8 ++++++++
 lisp/vc/vc-annotate.el    |  2 +-
 lisp/vc/vc.el             | 14 +++++++++++++-
 3 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index 808d2890b8d..7bc5040104a 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -1539,6 +1539,14 @@ project-recompile
              compilation-buffer-name-function)))
     (recompile edit-command)))
 
+;;;###autoload
+(defun project-vc-get-log-buffer-name (&optional _)
+  (project-prefixed-buffer-name "vc-change-log"))
+
+(custom-add-choice 'vc-get-log-buffer-name-function
+                   '(const :tag "Prefixed with project name" project-vc-get-log-buffer-name))
+
+
 (defcustom project-ignore-buffer-conditions nil
   "List of conditions to filter the buffers to be switched to.
 If any of these conditions are satisfied for a buffer in the
diff --git a/lisp/vc/vc-annotate.el b/lisp/vc/vc-annotate.el
index 9e9d4df8cc0..642b3bbf53c 100644
--- a/lisp/vc/vc-annotate.el
+++ b/lisp/vc/vc-annotate.el
@@ -570,7 +570,7 @@ vc-annotate-show-log-revision-at-line
       (if (not rev-at-line)
 	  (message "Cannot extract revision number from the current line")
 	(let ((backend vc-annotate-backend)
-	      (log-buf (get-buffer "*vc-change-log*"))
+	      (log-buf (get-buffer (vc-get-log-buffer-name)))
               ;; No need to follow renames: we specify the historical file name.
               vc-git-print-log-follow
 	      pos)
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index 60748d05ed8..86e4731ff97 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -947,6 +947,12 @@ vc-allow-rewriting-published-history
                  (const :tag "Allow without prompting" t))
   :version "31.1")
 
+(defcustom vc-get-log-buffer-name-function nil
+  "Function to compute the name of the change log buffer."
+  :version "31.1"
+  :type '(choice (const :tag "Default" nil)
+                 (function :tag "Custom function")))
+
 (defconst vc-cloneable-backends-custom-type
   `(choice :convert-widget
            ,(lambda (widget)
@@ -3033,6 +3039,12 @@ vc-print-log-renamed-add-button
       "Show the log for the file name(s) %s the rename"
       before-after))))
 
+(defun vc-get-log-buffer-name (&optional buffer)
+  (if (functionp vc-get-log-buffer-name-function)
+      (funcall vc-get-log-buffer-name-function
+               (or buffer (window-buffer)))
+    "*vc-change-log*"))
+
 (defun vc-print-log-internal (backend files working-revision
                                       &optional is-start-revision limit type)
   "For specified BACKEND and FILES, show the VC log.
@@ -3047,7 +3059,7 @@ vc-print-log-internal
   (let* ((dir-present (cl-some #'file-directory-p files))
          (shortlog (not (null (memq (if dir-present 'directory 'file)
                                     vc-log-short-style))))
-	(buffer-name "*vc-change-log*")
+	(buffer-name (vc-get-log-buffer-name))
          (type (or type (if shortlog 'short 'long))))
       (vc-log-internal-common
        backend buffer-name files type
-- 
2.39.5

From f48e2df1a10e1ca2d443b117cf3e3e2b6973e48b Mon Sep 17 00:00:00 2001
From: Matthias Meulien <[email-masked]>
Date: Sat, 24 May 2025 23:19:49 +0200
Subject: [PATCH 2/2] Per project vc-diff buffer

* lisp/obsolete/vc-arch.el (vc-arch-diff):
* lisp/obsolete/vc-mtn.el (vc-mtn-diff):
* lisp/progmodes/project.el (project-vc-get-diff-buffer-name):
(vc-get-diff-buffer-name-function):
* lisp/vc/pcvs-defs.el (cvs-buffer-name-alist):
* lisp/vc/smerge-mode.el (smerge-diff-buffer-name):
* lisp/vc/vc-bzr.el (vc-bzr-diff):
(vc-bzr-shelve-show):
* lisp/vc/vc-cvs.el (vc-cvs-diff):
* lisp/vc/vc-git.el (vc-git-diff):
* lisp/vc/vc-hg.el (vc-hg-diff):
* lisp/vc/vc-rcs.el (vc-rcs-diff):
* lisp/vc/vc-sccs.el (vc-sccs-diff):
* lisp/vc/vc-src.el (vc-src-diff):
* lisp/vc/vc.el (vc-get-diff-buffer-name-function):
(vc-get-diff-buffer-name):
(vc-diff-patch-string):
(vc-diff-internal):
(vc-revert):
* test/lisp/vc/vc-tests/vc-tests.el (vc-test--version-diff):
---
 lisp/obsolete/vc-arch.el          |  2 +-
 lisp/obsolete/vc-mtn.el           |  2 +-
 lisp/progmodes/project.el         |  6 ++++++
 lisp/vc/pcvs-defs.el              |  2 +-
 lisp/vc/smerge-mode.el            |  1 +
 lisp/vc/vc-bzr.el                 | 17 +++++++++--------
 lisp/vc/vc-cvs.el                 |  4 ++--
 lisp/vc/vc-git.el                 |  4 ++--
 lisp/vc/vc-hg.el                  |  2 +-
 lisp/vc/vc-rcs.el                 |  2 +-
 lisp/vc/vc-sccs.el                |  2 +-
 lisp/vc/vc-src.el                 |  2 +-
 lisp/vc/vc.el                     | 18 +++++++++++++++---
 test/lisp/vc/vc-tests/vc-tests.el |  4 ++--
 14 files changed, 44 insertions(+), 24 deletions(-)

diff --git a/lisp/obsolete/vc-arch.el b/lisp/obsolete/vc-arch.el
index 9d991d22bef..778acef11b7 100644
--- a/lisp/obsolete/vc-arch.el
+++ b/lisp/obsolete/vc-arch.el
@@ -467,7 +467,7 @@ vc-arch-diff
              (default-directory (vc-arch-root file))
              (status
               (vc-arch-command
-               (or buffer "*vc-diff*")
+               (or buffer (vc-get-diff-buffer-name))
                (if async 'async 1)
                nil "file-diffs"
                (vc-switches 'Arch 'diff)
diff --git a/lisp/obsolete/vc-mtn.el b/lisp/obsolete/vc-mtn.el
index 96dc0734f20..be4f5aba468 100644
--- a/lisp/obsolete/vc-mtn.el
+++ b/lisp/obsolete/vc-mtn.el
@@ -255,7 +255,7 @@ vc-mtn-log-view-mode
 
 (defun vc-mtn-diff (files &optional rev1 rev2 buffer _async)
   "Get a difference report using monotone between two revisions of FILES."
-  (apply #'vc-mtn-command (or buffer "*vc-diff*")
+  (apply #'vc-mtn-command (or buffer (vc-get-diff-buffer-name))
 	 1 ; bug#21969
 	 files "diff"
          (append
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index 7bc5040104a..edc18598b6c 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -1546,6 +1546,12 @@ project-vc-get-log-buffer-name
 (custom-add-choice 'vc-get-log-buffer-name-function
                    '(const :tag "Prefixed with project name" project-vc-get-log-buffer-name))
 
+;;;###autoload
+(defun project-vc-get-diff-buffer-name (&optional _)
+  (project-prefixed-buffer-name "vc-diff"))
+
+(custom-add-choice 'vc-get-diff-buffer-name-function
+                   '(const :tag "Prefixed with project name" project-vc-get-diff-buffer-name))
 
 (defcustom project-ignore-buffer-conditions nil
   "List of conditions to filter the buffers to be switched to.
diff --git a/lisp/vc/pcvs-defs.el b/lisp/vc/pcvs-defs.el
index d32b6633bda..d1e2c69a2dd 100644
--- a/lisp/vc/pcvs-defs.el
+++ b/lisp/vc/pcvs-defs.el
@@ -189,7 +189,7 @@ cvs-buffer-name-alist
 			(const "message")
 			(const "log")
 			(string))
-		(choice (const "*vc-diff*")
+		(choice (const "*vc-diff*") ;TODO
 			(const "*cvs-info*")
 			(const "*cvs-commit*")
 			(const (expand-file-name "*cvs-commit*"))
diff --git a/lisp/vc/smerge-mode.el b/lisp/vc/smerge-mode.el
index d1b27f6763b..d98d0f22915 100644
--- a/lisp/vc/smerge-mode.el
+++ b/lisp/vc/smerge-mode.el
@@ -57,6 +57,7 @@ smerge
   :prefix "smerge-")
 
 (defcustom smerge-diff-buffer-name "*vc-diff*"
+  ;; TODO
   "Buffer name to use for displaying diffs."
   :type '(choice
 	  (const "*vc-diff*")
diff --git a/lisp/vc/vc-bzr.el b/lisp/vc/vc-bzr.el
index 1ba4450cf5d..28287a2b905 100644
--- a/lisp/vc/vc-bzr.el
+++ b/lisp/vc/vc-bzr.el
@@ -860,7 +860,7 @@ vc-bzr-diff
                                 (or rev1 "revno:-1")
                                 (or rev2 "")))))))
     ;; `bzr diff' exits with code 1 if diff is non-empty.
-    (apply #'vc-bzr-command "diff" (or buffer "*vc-diff*")
+    (apply #'vc-bzr-command "diff" (or buffer (vc-get-diff-buffer-name))
            (if async 1 'async) files
            args)))
 
@@ -1157,13 +1157,14 @@ vc-bzr-shelve
 (defun vc-bzr-shelve-show (name)
   "Show the contents of shelve NAME."
   (interactive "sShelve name: ")
-  (vc-setup-buffer "*vc-diff*")
-  ;; FIXME: how can you show the contents of a shelf?
-  (vc-bzr-command "unshelve" "*vc-diff*" 'async nil "--preview" name)
-  (set-buffer "*vc-diff*")
-  (diff-mode)
-  (setq buffer-read-only t)
-  (pop-to-buffer (current-buffer)))
+  (let ((vc-diff-buffer-name (vc-get-diff-buffer-name)))
+    (vc-setup-buffer vc-diff-buffer-name)
+    ;; FIXME: how can you show the contents of a shelf?
+    (vc-bzr-command "unshelve" vc-diff-buffer-name 'async nil "--preview" name)
+    (set-buffer vc-diff-buffer-name)
+    (diff-mode)
+    (setq buffer-read-only t)
+    (pop-to-buffer (current-buffer))))
 
 (defun vc-bzr-shelve-apply (name)
   "Apply shelve NAME and remove it afterwards."
diff --git a/lisp/vc/vc-cvs.el b/lisp/vc/vc-cvs.el
index 06597ed7853..830140c42b0 100644
--- a/lisp/vc/vc-cvs.el
+++ b/lisp/vc/vc-cvs.el
@@ -592,7 +592,7 @@ vc-cvs-diff
 		;; This used to append diff-switches and vc-diff-switches,
 		;; which was consistent with the vc-diff-switches doc at that
 		;; time, but not with the actual behavior of any other VC diff.
-		(apply #'vc-do-command (or buffer "*vc-diff*") 1 "diff" nil
+		(apply #'vc-do-command (or buffer (vc-get-diff-buffer-name)) 1 "diff" nil
 		       ;; Not a CVS diff, does not use vc-cvs-diff-switches.
 		       (append (vc-switches nil 'diff)
 			       (list (file-relative-name file-oldvers)
@@ -600,7 +600,7 @@ vc-cvs-diff
 		(setq status 0))
 	    (push file invoke-cvs-diff-list)))))
     (when invoke-cvs-diff-list
-      (setq status (apply #'vc-cvs-command (or buffer "*vc-diff*")
+      (setq status (apply #'vc-cvs-command (or buffer (vc-get-diff-buffer-name))
 			  (if async 'async 1)
 			  invoke-cvs-diff-list "diff"
 			  (and oldvers (concat "-r" oldvers))
diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index c75c61cb3f4..ca31efa7d4e 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -1805,14 +1805,14 @@ vc-git-diff
       (setq command "diff-index")
       (unless rev1 (setq rev1 "HEAD")))
     (if vc-git-diff-switches
-        (apply #'vc-git-command (or buffer "*vc-diff*")
+        (apply #'vc-git-command (or buffer (vc-get-diff-buffer-name))
 	       1 ; bug#21969
                files
                command
                "--exit-code"
                (append (vc-switches 'git 'diff)
                        (list "-p" (or rev1 "HEAD") rev2 "--")))
-      (vc-git-command (or buffer "*vc-diff*") 1 files
+      (vc-git-command (or buffer (vc-get-diff-buffer-name)) 1 files
                       "difftool" "--exit-code" "--no-prompt" "-x"
                       (concat "diff "
                               (mapconcat #'identity
diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el
index 5c0758b93b2..1f44861f219 100644
--- a/lisp/vc/vc-hg.el
+++ b/lisp/vc/vc-hg.el
@@ -534,7 +534,7 @@ vc-hg-diff
     (when (and (not oldvers) newvers)
       (setq oldvers working))
     (apply #'vc-hg-command
-	   (or buffer "*vc-diff*")
+	   (or buffer (vc-get-diff-buffer-name))
            nil ; bug#21969
            files "diff"
            (append
diff --git a/lisp/vc/vc-rcs.el b/lisp/vc/vc-rcs.el
index 2a6c8d925b1..1591991e4d1 100644
--- a/lisp/vc/vc-rcs.el
+++ b/lisp/vc/vc-rcs.el
@@ -556,7 +556,7 @@ vc-rcs-print-log
 
 (defun vc-rcs-diff (files &optional oldvers newvers buffer _async)
   "Get a difference report using RCS between two sets of files."
-  (apply #'vc-do-command (or buffer "*vc-diff*")
+  (apply #'vc-do-command (or buffer (vc-get-diff-buffer-name))
 	 ;; The repo is local, so this is fast anyway.
 	 1 ; bug#21969
 	 "rcsdiff" (vc-expand-dirs files 'RCS)
diff --git a/lisp/vc/vc-sccs.el b/lisp/vc/vc-sccs.el
index 40feafd9c48..55c2c530ea3 100644
--- a/lisp/vc/vc-sccs.el
+++ b/lisp/vc/vc-sccs.el
@@ -324,7 +324,7 @@ vc-sccs-diff
   (setq files (vc-expand-dirs files 'SCCS))
   (setq oldvers (vc-sccs-lookup-triple (car files) oldvers))
   (setq newvers (vc-sccs-lookup-triple (car files) newvers))
-  (or buffer (setq buffer "*vc-diff*"))
+  (or buffer (setq buffer (vc-get-diff-buffer-name)))
   ;; We have to reimplement pieces of vc-do-command, because
   ;; we want to run multiple external commands, and only do the setup
   ;; and exit pieces once.
diff --git a/lisp/vc/vc-src.el b/lisp/vc/vc-src.el
index f9551165363..d3d0c421e89 100644
--- a/lisp/vc/vc-src.el
+++ b/lisp/vc/vc-src.el
@@ -320,7 +320,7 @@ vc-src-diff
       (setq oldvers nil))
     (when (and (not oldvers) newvers)
       (setq oldvers working))
-    (apply #'vc-src-command (or buffer "*vc-diff*") files "diff"
+    (apply #'vc-src-command (or buffer (vc-get-diff-buffer-name)) files "diff"
 	   (when oldvers
 	     (if newvers
 		 (list (concat oldvers "-" newvers))
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index 86e4731ff97..47979fd4af5 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -953,6 +953,12 @@ vc-get-log-buffer-name-function
   :type '(choice (const :tag "Default" nil)
                  (function :tag "Custom function")))
 
+(defcustom vc-get-diff-buffer-name-function nil
+  "Function to compute the name of the diff buffer."
+  :version "31.1"
+  :type '(choice (const :tag "Default" nil)
+                 (function :tag "Custom function")))
+
 (defconst vc-cloneable-backends-custom-type
   `(choice :convert-widget
            ,(lambda (widget)
@@ -2080,13 +2086,19 @@ vc-diff-finish
 (defvar vc-diff-added-files nil
   "If non-nil, diff added files by comparing them to /dev/null.")
 
+(defun vc-get-diff-buffer-name (&optional buffer)
+  (if (functionp vc-get-diff-buffer-name-function)
+      (funcall vc-get-diff-buffer-name-function
+               (or buffer (window-buffer)))
+    "*vc-diff*"))
+
 (defvar vc-patch-string nil)
 
 (defun vc-diff-patch-string (patch-string)
   "Report diffs to be committed from the patch.
 Like `vc-diff-internal' but uses PATCH-STRING to display
 in the output buffer."
-  (let ((buffer "*vc-diff*"))
+  (let ((buffer (vc-get-diff-buffer-name)))
     (vc-setup-buffer buffer)
     (let ((buffer-undo-list t)
           (inhibit-read-only t))
@@ -2108,7 +2120,7 @@ vc-diff-internal
 BUFFER, if non-nil, should be a buffer or a buffer name.
 Return t if the buffer had changes, nil otherwise."
   (unless buffer
-    (setq buffer "*vc-diff*"))
+    (setq buffer (vc-get-diff-buffer-name)))
   (let* ((files (cadr vc-fileset))
 	 (messages (cons (format "Finding changes in %s..."
                                  (vc-delistify files))
@@ -3390,7 +3402,7 @@ vc-revert
     (unwind-protect
 	(when (if vc-revert-show-diff
 		  (progn
-		    (setq diff-buffer (generate-new-buffer "*vc-diff*"))
+		    (setq diff-buffer (generate-new-buffer (vc-get-diff-buffer-name)))
 		    (vc-diff-internal vc-allow-async-revert vc-fileset
 				      nil nil nil diff-buffer))
 		;; Avoid querying the user again.
diff --git a/test/lisp/vc/vc-tests/vc-tests.el b/test/lisp/vc/vc-tests/vc-tests.el
index 632a6a792bd..85cc5224db0 100644
--- a/test/lisp/vc/vc-tests/vc-tests.el
+++ b/test/lisp/vc/vc-tests/vc-tests.el
@@ -640,9 +640,9 @@ vc-test--version-diff
 
               ;; Check version diff.
               (vc-version-diff files nil nil)
-              (should (bufferp (get-buffer "*vc-diff*")))
+              (should (bufferp (get-buffer (vc-get-diff-buffer-name))))
 
-              (with-current-buffer "*vc-diff*"
+              (with-current-buffer (vc-get-diff-buffer-name)
                 (progn
                   (let ((rawtext (buffer-substring-no-properties (point-min)
                                                                  (point-max))))
-- 
2.39.5