From 206d6bf4b0ce960912ca8a7fb2d4c078230d55d9 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sun, 13 Jul 2025 12:50:22 +0100 Subject: [PATCH] New global minor mode vc-auto-revert-mode * lisp/vc/vc-hooks.el (auto-revert-mode): Declare. (vc-auto-revert-mode): New global minor mode. (vc-turn-on-auto-revert-mode-for-tracked-files): New function. * lisp/vc/vc-dispatcher.el (auto-revert-mode) (auto-revert-buffers): Declare. (vc-resynch-window): Don't call vc-revert-buffer-internal when auto-revert-mode will revert the buffer. Call auto-revert-buffers to ensure that this reversion happens in a timely manner. * lisp/vc/vc.el (vc-register): Apply vc-auto-revert-mode to buffers visiting newly registered files. * lisp/emacs-lisp/easy-mmode.el (define-globalized-minor-mode): Improve the generated docstring. * doc/emacs/vc1-xtra.texi (VC Auto-Reverting): * etc/NEWS: Document the new minor mode. (cherry picked from commit 9d750c7e8041437758c919f6088d6f3686847812) --- doc/emacs/vc1-xtra.texi | 30 +++++++++++++++- lisp/emacs-lisp/easy-mmode.el | 10 +++--- lisp/vc/vc-dispatcher.el | 66 ++++++++++++++++++++--------------- lisp/vc/vc-hooks.el | 14 ++++++++ lisp/vc/vc.el | 9 +++-- 5 files changed, 92 insertions(+), 37 deletions(-) diff --git a/doc/emacs/vc1-xtra.texi b/doc/emacs/vc1-xtra.texi index 51fefe231da..fa7b3a6b3ba 100644 --- a/doc/emacs/vc1-xtra.texi +++ b/doc/emacs/vc1-xtra.texi @@ -16,7 +16,8 @@ * Revision Tags:: Symbolic names for revisions. * Version Headers:: Inserting version control headers into working files. * Editing VC Commands:: Editing the VC shell commands that Emacs will run. -* Preparing Patches:: Preparing and Composing patches from within VC +* Preparing Patches:: Preparing and composing patches from within VC. +* VC Auto-Reverting:: Updating buffer contents after VCS operations. @end menu @node Change Logs and VC @@ -314,6 +315,33 @@ you wish to use. This will be used as the default value when invoking @code{vc-prepare-patch}. Project maintainers may consider setting this as a directory local variable (@pxref{Directory Variables}). +@node VC Auto-Reverting +@subsubsection Auto-Reverting Buffers Visiting Tracked Files + + When Emacs executes VCS operations that it knows may change the +contents of tracked files, it reverts buffers visiting those files +(@pxref{Reverting}). It does this in a VCS-aware fashion that retains +the positions of point and the mark even when the VCS operation causes +VCS keywords to be expanded (for example, CVS keywords: @pxref{Keyword +substitution,,,cvs,CVS--Concurrent Versions System}). + +@findex vc-auto-revert-mode + An important limitation of this feature is that Emacs won't know to +revert buffers when you execute additional VCS operations outside of +Emacs, such as at a shell prompt, or by means of scripts. If you +regularly do this, and you don't use a VCS with keyword expansion (all +modern VCS, absent special configuration), you may wish to enable +@code{vc-auto-revert-mode} instead, by customizing that variable to +non-@code{nil}. + + This mode is just like @code{global-auto-revert-mode} (@pxref{Auto +Revert}) except limited to files visiting VCS-tracked files. It ensures +that Emacs will always revert buffers when VCS operations change their +contents, regardless of whether Emacs initiated those operations. + + @xref{VC Mode Line} regarding Auto Revert mode in buffers visiting +tracked files (which is what @code{vc-auto-revert-mode} enables). + @node Customizing VC @subsection Customizing VC diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el index 8e219da7946..915dcfa2392 100644 --- a/lisp/emacs-lisp/easy-mmode.el +++ b/lisp/emacs-lisp/easy-mmode.el @@ -540,7 +540,11 @@ on if the hook has explicitly disabled it. ,@(when predicate `((defvar ,MODE-predicate)))) ;; The actual global minor-mode (define-minor-mode ,global-mode - ,(concat (format "Toggle %s in all buffers.\n" pretty-name) + ,(concat (format "Toggle %s in many buffers.\n" pretty-name) + (internal--format-docstring-line + "Specifically, %s is enabled in all buffers where `%s' would do it." + pretty-name turn-on) + "\n\n" (internal--format-docstring-line (concat "With prefix ARG, enable %s if ARG is positive; " "otherwise, disable it.") @@ -549,10 +553,6 @@ on if the hook has explicitly disabled it. "If called from Lisp, toggle the mode if ARG is `toggle'. Enable the mode if ARG is nil, omitted, or is a positive number. Disable the mode if ARG is a negative number.\n\n" - (internal--format-docstring-line - "%s is enabled in all buffers where `%s' would do it." - pretty-name turn-on) - "\n\n" (internal--format-docstring-line "See `%s' for more information on %s." mode pretty-name) diff --git a/lisp/vc/vc-dispatcher.el b/lisp/vc/vc-dispatcher.el index a3b8a5af8ad..d16e2fbb8d9 100644 --- a/lisp/vc/vc-dispatcher.el +++ b/lisp/vc/vc-dispatcher.el @@ -656,9 +656,10 @@ CONTEXT is that which `vc-buffer-context' returns." (when new-mark (set-mark new-mark)))))) (defun vc-revert-buffer-internal (&optional arg no-confirm) - "Revert buffer, keeping point and mark where user expects them. -Try to be clever in the face of changes due to expanded version-control -key words. This is important for typeahead to work as expected. + "Revert buffer keeping point and the mark where the user expects them. +Try to be clever in the face of changes due to expanded VCS +keywords (cf., e.g., info node `(cvs)Keyword substitution'). +This is important for typeahead to work as expected. ARG and NO-CONFIRM are passed on to `revert-buffer'." (interactive "P") (widen) @@ -678,6 +679,9 @@ ARG and NO-CONFIRM are passed on to `revert-buffer'." (defvar view-old-buffer-read-only) +(defvar auto-revert-mode) +(declare-function auto-revert-buffers "autorevert") + (defun vc-resynch-window (file &optional keep noquery reset-vc-info) "If FILE is in the current buffer, either revert or unvisit it. The choice between revert (to see expanded keywords) and unvisit @@ -686,31 +690,37 @@ reverting. NOQUERY should be t *only* if it is known the only difference between the buffer and the file is due to modifications by the dispatcher client code, rather than user editing!" - (and (string= buffer-file-name - (if (file-name-absolute-p file) - file - (expand-file-name file (vc-root-dir)))) - (if keep - (when (file-exists-p file) - (when reset-vc-info - (vc-file-clearprops file)) - (vc-revert-buffer-internal t noquery) - - ;; VC operations might toggle the read-only state. In - ;; that case we need to adjust the `view-mode' status - ;; when `view-read-only' is non-nil. - (and view-read-only - (if (file-writable-p file) - (and view-mode - (let ((view-old-buffer-read-only nil)) - (view-mode-exit t))) - (and (not view-mode) - (not (eq (get major-mode 'mode-class) 'special)) - (view-mode-enter)))) - - ;; FIXME: Why use a hook? Why pass it buffer-file-name? - (run-hook-with-args 'vc-mode-line-hook buffer-file-name)) - (kill-buffer (current-buffer))))) + (and (equal buffer-file-name + (if (file-name-absolute-p file) + file + (expand-file-name file (vc-root-dir)))) + (cond ((not keep) + (kill-buffer)) + ((file-exists-p file) + (when reset-vc-info + (vc-file-clearprops file)) + ;; If `auto-revert-mode' is on (probably due to either + ;; `global-auto-revert-mode' or `vc-auto-revert-mode') + ;; then defer to that. Otherwise we do our own + ;; VC-specific reverting. + (if (and auto-revert-mode noquery) + (auto-revert-buffers) + (vc-revert-buffer-internal t noquery)) + + ;; VC operations might toggle the read-only state. In + ;; that case we need to adjust the `view-mode' status + ;; when `view-read-only' is non-nil. + (and view-read-only + (if (file-writable-p file) + (and view-mode + (let ((view-old-buffer-read-only nil)) + (view-mode-exit t))) + (and (not view-mode) + (not (eq (get major-mode 'mode-class) 'special)) + (view-mode-enter)))) + + ;; FIXME: Why use a hook? Why pass it buffer-file-name? + (run-hook-with-args 'vc-mode-line-hook buffer-file-name))))) (declare-function vc-dir-resynch-file "vc-dir" (&optional fname)) diff --git a/lisp/vc/vc-hooks.el b/lisp/vc/vc-hooks.el index 8a159d39c41..3945e5c10cd 100644 --- a/lisp/vc/vc-hooks.el +++ b/lisp/vc/vc-hooks.el @@ -206,6 +206,20 @@ VC commands are globally reachable under the prefix \\[vc-prefix-map]: \\{vc-prefix-map}" nil) +(defvar auto-revert-mode) +(define-globalized-minor-mode vc-auto-revert-mode auto-revert-mode + vc-turn-on-auto-revert-mode-for-tracked-files + :group 'vc + :version "31.1") + +(defun vc-turn-on-auto-revert-mode-for-tracked-files () + "Turn on Auto Revert mode in buffers visiting VCS-tracked files." + ;; This should turn on Auto Revert mode whenever `vc-mode' is non-nil. + ;; We can't just check that variable directly because `vc-mode-line' + ;; may not have been called yet. + (when (vc-backend buffer-file-name) + (auto-revert-mode 1))) + (defmacro vc-error-occurred (&rest body) `(condition-case nil (progn ,@body nil) (error t))) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 9645b82f317..e591f527be2 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -1666,13 +1666,14 @@ from which to check out the file(s)." (find-file-other-window file)) (if (save-window-excursion (vc-diff-internal nil - (cons (car vc-fileset) (cons (cadr vc-fileset) (list file))) + (cons (car vc-fileset) + (cons (cadr vc-fileset) (list file))) (vc-working-revision file) nil) (goto-char (point-min)) (let ((inhibit-read-only t)) (insert (format "Changes to %s since last lock:\n\n" file))) - (not (beep)) + (beep) (yes-or-no-p (concat "File has unlocked changes. " "Claim lock retaining changes? "))) (progn (vc-call-backend backend 'steal-lock file) @@ -1733,7 +1734,9 @@ first backend that could register the file is used." (when-let* ((bname (get-file-buffer fname))) (with-current-buffer bname (unless vc-make-backup-files - (setq-local backup-inhibited t)))) + (setq-local backup-inhibited t)) + (when vc-auto-revert-mode + (auto-revert-mode 1)))) (vc-resynch-buffer fname t t)) (message "Registering %s... done" files))) -- 2.39.5