278 lines
11 KiB
EmacsLisp
278 lines
11 KiB
EmacsLisp
;;; magit-autorevert.el --- Revert buffers when files in repository change -*- lexical-binding:t -*-
|
|
|
|
;; Copyright (C) 2008-2025 The Magit Project Contributors
|
|
|
|
;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
|
;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
|
|
|
|
;; SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
;; Magit is free software: you can redistribute it and/or modify it
|
|
;; under the terms of the GNU General Public License as published by
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
;; (at your option) any later version.
|
|
;;
|
|
;; Magit is distributed in the hope that it will be useful, but WITHOUT
|
|
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
|
;; License for more details.
|
|
;;
|
|
;; You should have received a copy of the GNU General Public License
|
|
;; along with Magit. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
;;; Commentary:
|
|
|
|
;; This library implements support for automatically reverting buffers
|
|
;; when visited files in the repository change.
|
|
|
|
;; See (info "(magit)Automatic Reverting of File-Visiting Buffers").
|
|
|
|
;;; Code:
|
|
|
|
(require 'magit-process)
|
|
|
|
(require 'autorevert)
|
|
|
|
;;; Options
|
|
|
|
(defgroup magit-auto-revert nil
|
|
"Revert buffers when files in repository change."
|
|
:link '(custom-group-link auto-revert)
|
|
:link '(info-link "(magit)Automatic Reverting of File-Visiting Buffers")
|
|
:group 'auto-revert
|
|
:group 'magit-essentials
|
|
:group 'magit-modes)
|
|
|
|
(defcustom auto-revert-buffer-list-filter nil
|
|
"Filter that determines which buffers `auto-revert-buffers' reverts.
|
|
|
|
This option is provided by Magit, which also advises
|
|
`auto-revert-buffers' to respect it. Magit users who do not turn
|
|
on the local mode `auto-revert-mode' themselves, are best served
|
|
by setting the value to `magit-auto-revert-repository-buffer-p'.
|
|
|
|
However the default is nil, so as not to disturb users who do use
|
|
the local mode directly. If you experience delays when running
|
|
Magit commands, then you should consider using one of the
|
|
predicates provided by Magit - especially if you also use Tramp.
|
|
|
|
Users who do turn on `auto-revert-mode' in buffers in which Magit
|
|
doesn't do that for them, should likely not use any filter.
|
|
Users who turn on `global-auto-revert-mode', do not have to worry
|
|
about this option, because it is disregarded if the global mode
|
|
is enabled."
|
|
:package-version '(magit . "2.4.2")
|
|
:group 'auto-revert
|
|
:group 'magit-auto-revert
|
|
:group 'magit-related
|
|
:type `(radio (const :tag "No filter" nil)
|
|
(function-item ,#'magit-auto-revert-buffer-p)
|
|
(function-item ,#'magit-auto-revert-repository-buffer-p)
|
|
function))
|
|
|
|
(defcustom magit-auto-revert-tracked-only t
|
|
"Whether `magit-auto-revert-mode' only reverts tracked files."
|
|
:package-version '(magit . "2.4.0")
|
|
:group 'magit-auto-revert
|
|
:type 'boolean
|
|
:set (lambda (var val)
|
|
(set var val)
|
|
(when (and (bound-and-true-p magit-auto-revert-mode)
|
|
(featurep 'magit-autorevert))
|
|
(magit-auto-revert-mode -1)
|
|
(magit-auto-revert-mode))))
|
|
|
|
(defcustom magit-auto-revert-immediately t
|
|
"Whether Magit reverts buffers immediately.
|
|
|
|
If this is non-nil and either `global-auto-revert-mode' or
|
|
`magit-auto-revert-mode' is enabled, then Magit immediately
|
|
reverts buffers by explicitly calling `auto-revert-buffers'
|
|
after running Git for side-effects.
|
|
|
|
If `auto-revert-use-notify' is non-nil (and file notifications
|
|
are actually supported), then `magit-auto-revert-immediately'
|
|
does not have to be non-nil, because the reverts happen
|
|
immediately anyway.
|
|
|
|
If `magit-auto-revert-immediately' and `auto-revert-use-notify'
|
|
are both nil, then reverts happen after `auto-revert-interval'
|
|
seconds of user inactivity. That is not desirable."
|
|
:package-version '(magit . "2.4.0")
|
|
:group 'magit-auto-revert
|
|
:type 'boolean)
|
|
|
|
;;; Mode
|
|
|
|
;;;###autoload
|
|
(progn ; magit-custom-initialize-after-init
|
|
(defun magit-custom-initialize-after-init (symbol value)
|
|
;; Use `apply-partially' instead of the wonders of lexical bindings,
|
|
;; because of bugs in the autoload handling of package managers, which
|
|
;; cause these variables to be treated as dynamic. See #5476 and #5485.
|
|
(internal--define-uninitialized-variable symbol)
|
|
(cond ((not after-init-time)
|
|
(letrec ((f (apply-partially
|
|
(lambda (symbol value)
|
|
(ignore-errors
|
|
(remove-hook 'after-init-hook f))
|
|
(custom-initialize-set symbol value))
|
|
symbol value)))
|
|
(add-hook 'after-init-hook f)))
|
|
((not load-file-name)
|
|
(custom-initialize-set symbol value))
|
|
((letrec ((f (apply-partially
|
|
(lambda (thisfile symbol value file)
|
|
(when (equal file thisfile)
|
|
(ignore-errors
|
|
(remove-hook 'after-load-functions f))
|
|
(custom-initialize-set symbol value)))
|
|
load-file-name symbol value)))
|
|
(add-hook 'after-load-functions f))))))
|
|
|
|
(defun magit-turn-on-auto-revert-mode-if-desired (&optional file)
|
|
(cond (file
|
|
(when-let ((buffer (find-buffer-visiting file)))
|
|
(with-current-buffer buffer
|
|
(magit-turn-on-auto-revert-mode-if-desired))))
|
|
((and (not auto-revert-mode) ; see #3014
|
|
(not global-auto-revert-mode) ; see #3460
|
|
buffer-file-name
|
|
(or auto-revert-remote-files ; see #5422
|
|
(not (file-remote-p buffer-file-name)))
|
|
(file-readable-p buffer-file-name)
|
|
(compat-call executable-find (magit-git-executable) t)
|
|
(magit-toplevel)
|
|
(or (not magit-auto-revert-tracked-only)
|
|
(magit-file-tracked-p buffer-file-name)))
|
|
(auto-revert-mode 1))))
|
|
|
|
;;;###autoload
|
|
(define-globalized-minor-mode magit-auto-revert-mode auto-revert-mode
|
|
magit-turn-on-auto-revert-mode-if-desired
|
|
:package-version '(magit . "2.4.0")
|
|
:link '(info-link "(magit)Automatic Reverting of File-Visiting Buffers")
|
|
:group 'magit-auto-revert
|
|
:group 'magit-essentials
|
|
:init-value (not (or global-auto-revert-mode noninteractive))
|
|
:initialize #'magit-custom-initialize-after-init)
|
|
|
|
(defun magit-auto-revert-mode--disable ()
|
|
"When enabling `global-auto-revert-mode', disable `magit-auto-revert-mode'."
|
|
(when (and global-auto-revert-mode
|
|
(bound-and-true-p magit-auto-revert-mode))
|
|
(magit-auto-revert-mode -1)))
|
|
|
|
(add-hook 'global-auto-revert-mode-hook #'magit-auto-revert-mode--disable)
|
|
|
|
(put 'magit-auto-revert-mode 'function-documentation
|
|
"Toggle Magit Auto Revert mode.
|
|
|
|
If called interactively, enable Magit Auto Revert mode if ARG is
|
|
positive, and disable it if ARG is zero or negative. If called
|
|
from Lisp, also enable the mode if ARG is omitted or nil, and
|
|
toggle it if ARG is `toggle'; disable the mode otherwise.
|
|
|
|
Magit Auto Revert mode is a global minor mode that reverts
|
|
buffers associated with a file that is located inside a Git
|
|
repository when the file changes on disk. Use `auto-revert-mode'
|
|
to revert a particular buffer. Or use `global-auto-revert-mode'
|
|
to revert all file-visiting buffers, not just those that visit
|
|
a file located inside a Git repository.
|
|
|
|
This global mode works by turning on the buffer-local mode
|
|
`auto-revert-mode' at the time a buffer is first created. The
|
|
local mode is turned on if the visited file is being tracked in
|
|
a Git repository at the time when the buffer is created.
|
|
|
|
If `magit-auto-revert-tracked-only' is non-nil (the default),
|
|
then only tracked files are reverted. But if you stage a
|
|
previously untracked file using `magit-stage', then this mode
|
|
notices that.
|
|
|
|
Unlike `global-auto-revert-mode', this mode never reverts any
|
|
buffers that are not visiting files.
|
|
|
|
The behavior of this mode can be customized using the options
|
|
in the `autorevert' and `magit-autorevert' groups.
|
|
|
|
This function calls the hook `magit-auto-revert-mode-hook'.
|
|
|
|
Like nearly every mode, this mode should be enabled or disabled
|
|
by calling the respective mode function, the reason being that
|
|
changing the state of a mode involves more than merely toggling
|
|
a single switch, so setting the mode variable is not enough.
|
|
Also, you should not use `after-init-hook' to disable this mode.
|
|
|
|
\(fn &optional ARG)")
|
|
|
|
;;;###autoload
|
|
(defun magit-auto-revert-buffers ()
|
|
(when (and magit-auto-revert-immediately
|
|
(or global-auto-revert-mode
|
|
(and magit-auto-revert-mode auto-revert-buffer-list)))
|
|
(let ((auto-revert-buffer-list-filter
|
|
(or auto-revert-buffer-list-filter
|
|
#'magit-auto-revert-repository-buffer-p)))
|
|
(auto-revert-buffers))))
|
|
|
|
(defvar magit-auto-revert-toplevel nil)
|
|
|
|
(defvar magit-auto-revert-counter 1
|
|
"Incremented each time `auto-revert-buffers' is called.")
|
|
|
|
(defun magit-auto-revert-buffer-p (buffer)
|
|
"Return non-nil if BUFFER visits a file inside the current repository.
|
|
The current repository is the one containing `default-directory'.
|
|
If there is no current repository, then return t for any BUFFER."
|
|
(magit-auto-revert-repository-buffer-p buffer t))
|
|
|
|
(defun magit-auto-revert-repository-buffer-p (buffer &optional fallback)
|
|
"Return non-nil if BUFFER visits a file inside the current repository.
|
|
The current repository is the one containing `default-directory'.
|
|
If there is no current repository, then return FALLBACK (which
|
|
defaults to nil) for any BUFFER."
|
|
;; Call `magit-toplevel' just once per cycle.
|
|
(unless (and magit-auto-revert-toplevel
|
|
(= (cdr magit-auto-revert-toplevel)
|
|
magit-auto-revert-counter))
|
|
(setq magit-auto-revert-toplevel
|
|
(cons (or (magit-toplevel) 'no-repo)
|
|
magit-auto-revert-counter)))
|
|
(let ((top (car magit-auto-revert-toplevel)))
|
|
(if (eq top 'no-repo)
|
|
fallback
|
|
(let ((dir (buffer-local-value 'default-directory buffer)))
|
|
(and (equal (file-remote-p dir)
|
|
(file-remote-p top))
|
|
;; ^ `tramp-handle-file-in-directory-p' lacks this optimization.
|
|
(file-in-directory-p dir top))))))
|
|
|
|
(define-advice auto-revert-buffers (:around (fn) buffer-list-filter)
|
|
(cl-incf magit-auto-revert-counter)
|
|
(if (or global-auto-revert-mode
|
|
(not auto-revert-buffer-list)
|
|
(not auto-revert-buffer-list-filter))
|
|
(funcall fn)
|
|
(let ((auto-revert-buffer-list
|
|
(seq-filter auto-revert-buffer-list-filter
|
|
auto-revert-buffer-list)))
|
|
(funcall fn))
|
|
(unless auto-revert-timer
|
|
(auto-revert-set-timer))))
|
|
|
|
;;; _
|
|
(provide 'magit-autorevert)
|
|
;; Local Variables:
|
|
;; read-symbol-shorthands: (
|
|
;; ("and$" . "cond-let--and$")
|
|
;; ("and>" . "cond-let--and>")
|
|
;; ("and-let" . "cond-let--and-let")
|
|
;; ("if-let" . "cond-let--if-let")
|
|
;; ("when-let" . "cond-let--when-let")
|
|
;; ("while-let" . "cond-let--while-let")
|
|
;; ("match-string" . "match-string")
|
|
;; ("match-str" . "match-string-no-properties"))
|
|
;; End:
|
|
;;; magit-autorevert.el ends here
|