diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 1a114ed38ba..38ca44bc1fb 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -576,6 +576,8 @@ project backend implementation of `project-external-roots'.") See `project-vc-extra-root-markers' for the marker value format.") +;; FIXME: Should perhaps use `vc--repo-*prop' functions +;; (after promoting those to public). --spwhitton (defun project-try-vc (dir) ;; FIXME: Learn to invalidate when the value changes: ;; `project-vc-merge-submodules' or `project-vc-extra-root-markers'. diff --git a/lisp/vc/vc-hooks.el b/lisp/vc/vc-hooks.el index 32b410d5c3b..f275e792ccc 100644 --- a/lisp/vc/vc-hooks.el +++ b/lisp/vc/vc-hooks.el @@ -228,24 +228,26 @@ VC commands are globally reachable under the prefix \\[vc-prefix-map]: (defmacro vc-error-occurred (&rest body) `(condition-case nil (progn ,@body nil) (error t))) -;; We need a notion of per-file properties because the version -;; control state of a file is expensive to derive --- we compute -;; them when the file is initially found, keep them up to date -;; during any subsequent VC operations, and forget them when -;; the buffer is killed. +;; We need a notion of per-file properties because the version control +;; state of a file is expensive to derive -- we compute it when the file +;; is initially found, keep them up to date during any subsequent VC +;; operations, and forget them when the buffer is killed. +;; +;; In addition we store some whole-repository properties keyed to the +;; repository root. We invalidate/update these during VC operations, +;; but there isn't a point analagous to the killing of a buffer at which +;; we clear them all out, like there is for per-file properties. (defvar vc-file-prop-obarray (obarray-make 17) - "Obarray for per-file properties.") + "Obarray for VC per-file and per-repository properties.") (defvar vc-touched-properties nil) (defun vc-file-setprop (file property value) "Set per-file VC PROPERTY for FILE to VALUE." - (if (and vc-touched-properties - (not (memq property vc-touched-properties))) - (setq vc-touched-properties (append (list property) - vc-touched-properties))) - (put (intern (expand-file-name file) vc-file-prop-obarray) property value)) + (cl-pushnew property vc-touched-properties) + (put (intern (expand-file-name file) vc-file-prop-obarray) + property value)) (defun vc-file-getprop (file property) "Get per-file VC PROPERTY for FILE." @@ -257,6 +259,18 @@ VC commands are globally reachable under the prefix \\[vc-prefix-map]: (kill-local-variable 'vc-parent-buffer)) (setplist (intern (expand-file-name file) vc-file-prop-obarray) nil)) +(defun vc--repo-setprop (property value) + "Set per-repository VC PROPERTY to VALUE and return the value." + (vc-file-setprop (vc-root-dir) property value)) + +(defun vc--repo-getprop (property) + "Get per-repository VC PROPERTY." + (vc-file-getprop (vc-root-dir) property)) + +(defun vc--repo-clearprops () + "Clear all VC whole-repository properties." + (vc-file-clearprops (vc-root-dir))) + ;; We keep properties on each symbol naming a backend as follows: ;; * `vc-functions': an alist mapping vc-FUNCTION to vc-BACKEND-FUNCTION. diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index c2931dfbfca..a8a11f07db6 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -4015,7 +4015,28 @@ The command prompts for the branch whose change log to show." 'vc-remote-location-history))) (defun vc--incoming-revision (backend &optional upstream-location refresh) - (or (vc-call-backend backend 'incoming-revision upstream-location refresh) + ;; Some backends don't support REFRESH and so always behave as though + ;; REFRESH is non-nil. This is not just for a lack of implementation + ;; in Emacs; for example, Mercurial repositories don't store any + ;; representation of the incoming revision between running commands. + ;; + ;; Fetching the incoming revision is often slow, and in many cases the + ;; last known incoming revision will serve perfectly well. For + ;; example, when finding revisions that are outgoing, the last known + ;; incoming revision is fine except for the rare case in which someone + ;; else cherry-picks the very same commits that you have outstanding, + ;; and pushes them. Given this, we implement our own caching. + (or (and (not refresh) + (cdr (assoc upstream-location + (vc--repo-getprop 'vc-incoming-revision)))) + (let ((res (vc-call-backend backend 'incoming-revision + upstream-location refresh))) + (if-let* ((alist (vc--repo-getprop 'vc-incoming-revision))) + (setf (alist-get upstream-location alist nil nil #'equal) + res) + (vc--repo-setprop 'vc-incoming-revision + `((,upstream-location . ,res)))) + res) (user-error "No incoming revision -- local-only branch?"))) ;;;###autoload diff --git a/src/fns.c b/src/fns.c index 157022a5c44..14af8a0ec58 100644 --- a/src/fns.c +++ b/src/fns.c @@ -2702,8 +2702,8 @@ plist_put (Lisp_Object plist, Lisp_Object prop, Lisp_Object val) } DEFUN ("put", Fput, Sput, 3, 3, 0, - doc: /* Store SYMBOL's PROPNAME property with value VALUE. -It can be retrieved with `(get SYMBOL PROPNAME)'. */) + doc: /* Store SYMBOL's PROPNAME property with value VALUE and return that value. +It can later be retrieved with `(get SYMBOL PROPNAME)'. */) (Lisp_Object symbol, Lisp_Object propname, Lisp_Object value) { CHECK_SYMBOL (symbol);