aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/vc
diff options
context:
space:
mode:
authorHelmut Eller2026-02-13 09:10:16 +0100
committerHelmut Eller2026-02-13 09:10:16 +0100
commit91c9e9883488d715a30877dfd7641ef4b3c62658 (patch)
treee2c4525147e443f86baf9d0144aeadec082d7564 /lisp/vc
parent9a4a54af9192a6653164364c75721ee814ffb1e8 (diff)
parentf1fe4d46190263e164ccd1e066095d46a156297f (diff)
downloademacs-feature/igc.tar.gz
emacs-feature/igc.zip
Merge branch 'master' into feature/igcfeature/igc
Diffstat (limited to 'lisp/vc')
-rw-r--r--lisp/vc/diff-mode.el170
-rw-r--r--lisp/vc/vc-dispatcher.el6
-rw-r--r--lisp/vc/vc-git.el143
-rw-r--r--lisp/vc/vc.el69
4 files changed, 241 insertions, 147 deletions
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index 5c0fb5fba4c..559310ff770 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -201,8 +201,9 @@ The default \"-b\" means to ignore whitespace-only changes,
201(defvar-keymap diff-mode-shared-map 201(defvar-keymap diff-mode-shared-map
202 :doc "Bindings for read-only `diff-mode' buffers. 202 :doc "Bindings for read-only `diff-mode' buffers.
203These bindings are also available with an ESC prefix 203These bindings are also available with an ESC prefix
204(i.e. a \\=`M-' prefix) in read-write `diff-mode' buffers, 204(i.e. a \\=`M-' prefix) in all `diff-mode' buffers, including in
205and with a `diff-minor-mode-prefix' prefix in `diff-minor-mode'. 205particular read-write `diff-mode' buffers, and with a
206`diff-minor-mode-prefix' prefix in `diff-minor-mode'.
206See also `diff-mode-read-only-map'." 207See also `diff-mode-read-only-map'."
207 "n" #'diff-hunk-next 208 "n" #'diff-hunk-next
208 "N" #'diff-file-next 209 "N" #'diff-file-next
@@ -217,14 +218,7 @@ See also `diff-mode-read-only-map'."
217 "RET" #'diff-goto-source 218 "RET" #'diff-goto-source
218 "<mouse-2>" #'diff-goto-source 219 "<mouse-2>" #'diff-goto-source
219 "o" #'diff-goto-source ; other-window 220 "o" #'diff-goto-source ; other-window
220 "<remap> <undo>" #'undo-ignore-read-only 221 "<remap> <undo>" #'undo-ignore-read-only)
221
222 ;; The foregoing commands don't affect buffers beyond this one.
223 ;; The following command is the only one that has a single-letter
224 ;; binding and which affects buffers beyond this one.
225 ;; However, the following command asks for confirmation by default,
226 ;; so that seems okay. --spwhitton
227 "u" #'diff-revert-and-kill-hunk)
228 222
229;; Not `diff-read-only-mode-map' because there is no such mode 223;; Not `diff-read-only-mode-map' because there is no such mode
230;; `diff-read-only-mode'; see comment above. 224;; `diff-read-only-mode'; see comment above.
@@ -233,15 +227,28 @@ See also `diff-mode-read-only-map'."
233 :doc "Additional bindings for read-only `diff-mode' buffers. 227 :doc "Additional bindings for read-only `diff-mode' buffers.
234Most of the bindings for read-only `diff-mode' buffers are in 228Most of the bindings for read-only `diff-mode' buffers are in
235`diff-mode-shared-map'. This map contains additional bindings for 229`diff-mode-shared-map'. This map contains additional bindings for
236read-only `diff-mode' buffers that are *not* available with an ESC 230read-only `diff-mode' buffers that are *not* also available with an ESC
237prefix (i.e. a \\=`M-' prefix) in read-write `diff-mode' buffers." 231prefix (i.e. a \\=`M-' prefix) in read-write (nor read-only) `diff-mode'
232buffers."
238 ;; We don't want the following in read-write `diff-mode' buffers 233 ;; We don't want the following in read-write `diff-mode' buffers
239 ;; because they hide useful `M-<foo>' global bindings when editing. 234 ;; because they hide useful `M-<foo>' global bindings when editing.
240 "W" #'widen 235 "W" #'widen
241 "w" #'diff-kill-ring-save 236 "w" #'diff-kill-ring-save
242 "A" #'diff-ediff-patch 237 "A" #'diff-ediff-patch
243 "r" #'diff-restrict-view 238 "r" #'diff-restrict-view
244 "R" #'diff-reverse-direction) 239 "R" #'diff-reverse-direction
240 "s" #'diff-split-hunk
241
242 ;; The foregoing commands in `diff-mode-shared-map' and
243 ;; `diff-mode-read-only-map' don't affect buffers beyond this one.
244 ;; The following command is the only one that has a single-character
245 ;; binding and which affects buffers beyond this one. However, the
246 ;; following command asks for confirmation by default, so that seems
247 ;; okay. --spwhitton
248 "u" #'diff-revert-and-kill-hunk
249 ;; `diff-revert-and-kill-hunk' is the `diff-mode' analogue of what '@'
250 ;; does in VC-Dir, so give it the same short binding.
251 "@" #'diff-revert-and-kill-hunk)
245 252
246(defvar-keymap diff-mode-map 253(defvar-keymap diff-mode-map
247 :doc "Keymap for `diff-mode'. See also `diff-mode-shared-map'." 254 :doc "Keymap for `diff-mode'. See also `diff-mode-shared-map'."
@@ -882,31 +889,19 @@ If the prefix ARG is given, restrict the view to the current file instead."
882 (goto-char (point-min)) 889 (goto-char (point-min))
883 (re-search-forward diff-hunk-header-re nil t))) 890 (re-search-forward diff-hunk-header-re nil t)))
884 891
885(defun diff-hunk-kill () 892(defun diff-hunk-kill (&optional beg end)
886 "Kill the hunk at point." 893 "Kill the hunk at point.
887 (interactive) 894When killing the last hunk left for a file, kill the file header too.
888 (if (not (diff--some-hunks-p)) 895Interactively, if the region is active, kill all hunks that the region
889 (error "No hunks") 896overlaps.
890 (diff-beginning-of-hunk t) 897
891 (let* ((hunk-bounds (diff-bounds-of-hunk)) 898When called from Lisp with optional arguments BEG and END non-nil, kill
892 (file-bounds (ignore-errors (diff-bounds-of-file))) 899all hunks overlapped by the region from BEG to END as though called
893 ;; If the current hunk is the only one for its file, kill the 900interactively with an active region delimited by BEG and END."
894 ;; file header too. 901 (interactive "R")
895 (bounds (if (and file-bounds 902 (when (xor beg end)
896 (progn (goto-char (car file-bounds)) 903 (error "Invalid call to `diff-hunk-kill'"))
897 (= (progn (diff-hunk-next) (point)) 904 (diff--revert-kill-hunks beg end nil))
898 (car hunk-bounds)))
899 (progn (goto-char (cadr hunk-bounds))
900 ;; bzr puts a newline after the last hunk.
901 (while (looking-at "^\n")
902 (forward-char 1))
903 (= (point) (cadr file-bounds))))
904 file-bounds
905 hunk-bounds))
906 (inhibit-read-only t))
907 (apply #'kill-region bounds)
908 (goto-char (car bounds))
909 (ignore-errors (diff-beginning-of-hunk t)))))
910 905
911;; This is not `diff-kill-other-hunks' because we might need to make 906;; This is not `diff-kill-other-hunks' because we might need to make
912;; copies of file headers in order to ensure the new kill ring entry 907;; copies of file headers in order to ensure the new kill ring entry
@@ -2282,6 +2277,83 @@ With a prefix argument, try to REVERSE the hunk."
2282 :type 'boolean 2277 :type 'boolean
2283 :version "31.1") 2278 :version "31.1")
2284 2279
2280(defun diff--revert-kill-hunks (beg end revertp)
2281 "Workhorse routine for killing hunks, after possibly reverting them.
2282If BEG and END are nil, kill the hunk at point.
2283Otherwise kill all hunks overlapped by region delimited by BEG and END.
2284When killing a hunk that's the only one remaining for its file, kill the
2285file header too.
2286If REVERTP is non-nil, reverse-apply hunks before killing them."
2287 ;; With BEG and END non-nil, we push each hunk to the kill ring
2288 ;; separately. If we want to push to the kill ring just once, we have
2289 ;; to decide how to handle file headers such that the meanings of the
2290 ;; hunks in the kill ring entry, considered as a whole patch, do not
2291 ;; deviate too far from the meanings the hunks had in this buffer.
2292 ;;
2293 ;; For example, if we have a single hunk for one file followed by
2294 ;; multiple hunks for another file, and we naïvely kill the single
2295 ;; hunk and the first of the multiple hunks, our kill ring entry will
2296 ;; be a patch applying those two hunks to the first file. This is
2297 ;; because killing the single hunk will have brought its file header
2298 ;; with it, but not so killing the second hunk. So we will have put
2299 ;; together hunks that were previously for two different files.
2300 ;;
2301 ;; One option is to *copy* every file header that the region overlaps
2302 ;; (and that we will not kill, because we are leaving other hunks for
2303 ;; that file behind). But then the text this command pushes to the
2304 ;; kill ring would be different from the text it removes from the
2305 ;; buffer, which would be unintuitive for an Emacs kill command.
2306 ;;
2307 ;; An alternative might be to have restrictions as follows:
2308 ;;
2309 ;; Interactively, if the region is active, try to kill all hunks that the
2310 ;; region overlaps. This works when either
2311 ;; - all the hunks the region overlaps are for the same file; or
2312 ;; - the last hunk the region overlaps is the last hunk for its file.
2313 ;; These restrictions are so that the text added to the kill ring does not
2314 ;; merge together hunks for different files under a single file header.
2315 ;;
2316 ;; We would error out if neither property is met. When either holds,
2317 ;; any file headers the region overlaps are ones we should kill.
2318 (unless (diff--some-hunks-p)
2319 (error "No hunks"))
2320 (if beg
2321 (save-excursion
2322 (goto-char beg)
2323 (setq beg (car (diff-bounds-of-hunk)))
2324 (goto-char end)
2325 (unless (looking-at diff-hunk-header-re)
2326 (setq end (cadr (diff-bounds-of-hunk)))))
2327 (pcase-setq `(,beg ,end) (diff-bounds-of-hunk)))
2328 (when (or (not revertp) (null (diff-apply-buffer beg end t)))
2329 (goto-char end)
2330 (when-let* ((pos (diff--at-diff-header-p)))
2331 (goto-char pos))
2332 (setq beg (copy-marker beg) end (point-marker))
2333 (unwind-protect
2334 (cl-loop initially (goto-char beg)
2335 with inhibit-read-only = t
2336 for (hunk-beg hunk-end) = (diff-bounds-of-hunk)
2337 for file-bounds = (ignore-errors (diff-bounds-of-file))
2338 for (file-beg file-end) = file-bounds
2339 if (and file-bounds
2340 (progn
2341 (goto-char file-beg)
2342 (diff-hunk-next)
2343 (eq (point) hunk-beg))
2344 (progn
2345 (goto-char hunk-end)
2346 ;; bzr puts a newline after the last hunk.
2347 (while (looking-at "^\n") (forward-char 1))
2348 (eq (point) file-end)))
2349 do (kill-region file-beg file-end) (goto-char file-beg)
2350 else do (kill-region hunk-beg hunk-end) (goto-char hunk-beg)
2351 do (ignore-errors (diff-beginning-of-hunk t))
2352 until (or (< (point) (marker-position beg))
2353 (eql (point) (marker-position end))))
2354 (set-marker beg nil)
2355 (set-marker end nil))))
2356
2285(defun diff-revert-and-kill-hunk (&optional beg end) 2357(defun diff-revert-and-kill-hunk (&optional beg end)
2286 "Reverse-apply and then kill the hunk at point. Save changed buffer. 2358 "Reverse-apply and then kill the hunk at point. Save changed buffer.
2287Interactively, if the region is active, reverse-apply and kill all 2359Interactively, if the region is active, reverse-apply and kill all
@@ -2307,27 +2379,7 @@ BEG and END."
2307 (error "Invalid call to `diff-revert-and-kill-hunk'")) 2379 (error "Invalid call to `diff-revert-and-kill-hunk'"))
2308 (when (or (not diff-ask-before-revert-and-kill-hunk) 2380 (when (or (not diff-ask-before-revert-and-kill-hunk)
2309 (y-or-n-p "Really reverse-apply and kill hunk(s)?")) 2381 (y-or-n-p "Really reverse-apply and kill hunk(s)?"))
2310 (if beg 2382 (diff--revert-kill-hunks beg end t)))
2311 (save-excursion
2312 (goto-char beg)
2313 (setq beg (car (diff-bounds-of-hunk)))
2314 (goto-char end)
2315 (unless (looking-at diff-hunk-header-re)
2316 (setq end (cadr (diff-bounds-of-hunk)))))
2317 (pcase-setq `(,beg ,end) (diff-bounds-of-hunk)))
2318 (when (null (diff-apply-buffer beg end t))
2319 ;; Use `diff-hunk-kill' because it properly handles file headers.
2320 (goto-char end)
2321 (when-let* ((pos (diff--at-diff-header-p)))
2322 (goto-char pos))
2323 (setq beg (copy-marker beg) end (point-marker))
2324 (unwind-protect
2325 (cl-loop initially (goto-char beg)
2326 do (diff-hunk-kill)
2327 until (or (< (point) (marker-position beg))
2328 (eql (point) (marker-position end))))
2329 (set-marker beg nil)
2330 (set-marker end nil)))))
2331 2383
2332(defun diff-apply-buffer (&optional beg end reverse test-or-no-save) 2384(defun diff-apply-buffer (&optional beg end reverse test-or-no-save)
2333 "Apply the diff in the entire diff buffer. 2385 "Apply the diff in the entire diff buffer.
diff --git a/lisp/vc/vc-dispatcher.el b/lisp/vc/vc-dispatcher.el
index dc17b582ed7..2015e7540ae 100644
--- a/lisp/vc/vc-dispatcher.el
+++ b/lisp/vc/vc-dispatcher.el
@@ -384,6 +384,9 @@ the man pages for \"torsocks\" for more details about Tor."
384 :version "27.1" 384 :version "27.1"
385 :group 'vc) 385 :group 'vc)
386 386
387(defvar vc-user-edit-command-history nil
388 "Name of minibuffer history variable for `vc-user-edit-command'.")
389
387(defun vc-user-edit-command (command file-or-list flags) 390(defun vc-user-edit-command (command file-or-list flags)
388 "Prompt the user to edit VC command COMMAND and FLAGS. 391 "Prompt the user to edit VC command COMMAND and FLAGS.
389Intended to be used as the value of `vc-filter-command-function'." 392Intended to be used as the value of `vc-filter-command-function'."
@@ -398,7 +401,8 @@ Intended to be used as the value of `vc-filter-command-function'."
398 (cons command (remq nil (if files-separator-p 401 (cons command (remq nil (if files-separator-p
399 (butlast flags) 402 (butlast flags)
400 flags)))) 403 flags))))
401 " "))))) 404 " ")
405 vc-user-edit-command-history))))
402 (list (car edited) file-or-list 406 (list (car edited) file-or-list
403 (nconc (cdr edited) (and files-separator-p '("--")))))) 407 (nconc (cdr edited) (and files-separator-p '("--"))))))
404 408
diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index 73db9c0f181..5e51b28fb37 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -772,70 +772,91 @@ or an empty string if none."
772 (vc-git--out-match '("symbolic-ref" "HEAD") 772 (vc-git--out-match '("symbolic-ref" "HEAD")
773 "^\\(refs/heads/\\)?\\(.+\\)$" 2)) 773 "^\\(refs/heads/\\)?\\(.+\\)$" 2))
774 774
775(defun vc-git--branch-remotes ()
776 "Return alist of configured remote branches for current branch.
777If there is a configured upstream, return the remote-tracking branch
778with key `upstream'. If there is a distinct configured push remote,
779return the remote-tracking branch there with key `push'.
780A configured push remote that's just the same as the upstream remote is
781ignored because that means we're not actually in a triangular workflow."
782 ;; Possibly we could simplify this using @{push}, but that may involve
783 ;; an unwanted dependency on the setting of push.default.
784 (cl-flet ((get (key)
785 (string-trim-right (vc-git--out-str "config" key))))
786 (let* ((branch (vc-git-working-branch))
787 (pull (get (format "branch.%s.remote" branch)))
788 (merge (string-remove-prefix "refs/heads/"
789 (get (format "branch.%s.merge"
790 branch))))
791 (push (get (format "branch.%s.pushRemote" branch)))
792 (push (if (string-empty-p push)
793 (get "remote.pushDefault")
794 push))
795 (alist (and (not (string-empty-p pull))
796 (not (string-empty-p merge))
797 `((upstream . ,(format "%s/%s" pull merge))))))
798 (if (or (string-empty-p push) (equal push pull))
799 alist
800 (cl-acons 'push (format "%s/%s" push branch) alist)))))
801
775(defun vc-git-trunk-or-topic-p () 802(defun vc-git-trunk-or-topic-p ()
776 "Return `topic' if branch has distinct pull and push remotes, else nil. 803 "Return `topic' if branch has distinct pull and push remotes, else nil.
777This is able to identify topic branches for certain forge workflows." 804This is able to identify topic branches for certain forge workflows."
778 (let* ((branch (vc-git-working-branch)) 805 (let ((remotes (vc-git--branch-remotes)))
779 (merge (string-trim-right 806 (and (assq 'upstream remotes) (assq 'push remotes) 'topic)))
780 (vc-git--out-str "config" (format "branch.%s.remote"
781 branch))))
782 (push (string-trim-right
783 (vc-git--out-str "config" (format "branch.%s.pushRemote"
784 branch))))
785 (push (if (string-empty-p push)
786 (string-trim-right
787 (vc-git--out-str "config" "remote.pushDefault"))
788 push)))
789 (and (plusp (length merge))
790 (plusp (length push))
791 (not (equal merge push))
792 'topic)))
793 807
794(defun vc-git-topic-outgoing-base () 808(defun vc-git-topic-outgoing-base ()
795 "Return the outgoing base for the current branch as a string. 809 "Return the outgoing base for the current branch as a string.
796This works by considering the current branch as a topic branch 810This works by considering the current branch as a topic branch
797(whether or not it actually is). 811(whether or not it actually is).
798Requires that the corresponding trunk exists as a local branch. 812
799 813If there is a distinct push remote for this branch, assume the target
800The algorithm employed is as follows. Find all merge bases between the 814for outstanding changes is the tracking branch, and return that.
801current branch and other local branches. Each of these is a commit on 815
802the current branch. Use `git merge-base --independent' on them all to 816Otherwise, fall back to the following algorithm, which requires that the
803find the topologically most recent. Take the branch for which that 817corresponding trunk exists as a local branch. Find all merge bases
804commit is a merge base with the current branch to be the branch into 818between the current branch and other local branches. Each of these is a
805which the current branch will eventually be merged. Find its upstream. 819commit on the current branch. Use `git merge-base --independent' on
806(If there is more than one branch whose merge base with the current 820them all to find the topologically most recent. Take the branch for
807branch is that same topologically most recent commit, try them 821which that commit is a merge base with the current branch to be the
808one-by-one, accepting the first that has an upstream.)" 822branch into which the current branch will eventually be merged. Find
809 (cl-flet ((get-line () (buffer-substring (point) (pos-eol)))) 823its upstream. (If there is more than one branch whose merge base with
810 (let* ((branches (vc-git-branches)) 824the current branch is that same topologically most recent commit, try
811 (current (pop branches)) 825them one-by-one, accepting the first that has an upstream.)"
812 merge-bases) 826 (if-let* ((remotes (vc-git--branch-remotes))
813 (with-temp-buffer 827 (_ (assq 'push remotes))
814 (dolist (branch branches) 828 (upstream (assq 'upstream remotes)))
815 (erase-buffer) 829 (cdr upstream)
816 (when (vc-git--out-ok "merge-base" "--all" branch current) 830 (cl-flet ((get-line () (buffer-substring (point) (pos-eol))))
817 (goto-char (point-min)) 831 (let* ((branches (vc-git-branches))
818 (while (not (eobp)) 832 (current (pop branches))
819 (push branch 833 merge-bases)
820 (alist-get (get-line) merge-bases nil nil #'equal)) 834 (with-temp-buffer
821 (forward-line 1)))) 835 (dolist (branch branches)
822 (erase-buffer)
823 (unless (apply #'vc-git--out-ok "merge-base" "--independent"
824 (mapcar #'car merge-bases))
825 (error "`git merge-base --independent' failed"))
826 ;; If 'git merge-base --independent' printed more than one line,
827 ;; just pick the first.
828 (goto-char (point-min))
829 (catch 'ret
830 (dolist (target (cdr (assoc (get-line) merge-bases)))
831 (erase-buffer) 836 (erase-buffer)
832 (when (vc-git--out-ok "for-each-ref" 837 (when (vc-git--out-ok "merge-base" "--all" branch current)
833 "--format=%(upstream:short)"
834 (concat "refs/heads/" target))
835 (goto-char (point-min)) 838 (goto-char (point-min))
836 (let ((outgoing-base (get-line))) 839 (while (not (eobp))
837 (unless (string-empty-p outgoing-base) 840 (push branch (alist-get (get-line) merge-bases
838 (throw 'ret outgoing-base)))))))))) 841 nil nil #'equal))
842 (forward-line 1))))
843 (erase-buffer)
844 (unless (apply #'vc-git--out-ok "merge-base" "--independent"
845 (mapcar #'car merge-bases))
846 (error "`git merge-base --independent' failed"))
847 ;; If 'git merge-base --independent' printed more than one
848 ;; line, just pick the first.
849 (goto-char (point-min))
850 (catch 'ret
851 (dolist (target (cdr (assoc (get-line) merge-bases)))
852 (erase-buffer)
853 (when (vc-git--out-ok "for-each-ref"
854 "--format=%(upstream:short)"
855 (concat "refs/heads/" target))
856 (goto-char (point-min))
857 (let ((outgoing-base (get-line)))
858 (unless (string-empty-p outgoing-base)
859 (throw 'ret outgoing-base)))))))))))
839 860
840(defun vc-git-dir--branch-headers () 861(defun vc-git-dir--branch-headers ()
841 "Return headers for branch-related information." 862 "Return headers for branch-related information."
@@ -1451,7 +1472,9 @@ line of the commit message in an entry with key \"Subject\"."
1451 (if (eq system-type 'windows-nt) 1472 (if (eq system-type 'windows-nt)
1452 locale-coding-system 1473 locale-coding-system
1453 coding-system-for-write))) 1474 coding-system-for-write)))
1454 (vc-git--call input-file t "mailinfo" msg-file patch-file)) 1475 (vc-git--call input-file t "mailinfo"
1476 (file-local-name msg-file)
1477 (file-local-name patch-file)))
1455 (goto-char (point-min)) 1478 (goto-char (point-min))
1456 ;; git-mailinfo joins up any header continuation lines for us. 1479 ;; git-mailinfo joins up any header continuation lines for us.
1457 (while (re-search-forward "^\\([^\t\n\s:]+\\):\\(.*\\)$" nil t) 1480 (while (re-search-forward "^\\([^\t\n\s:]+\\):\\(.*\\)$" nil t)
@@ -1591,7 +1614,9 @@ If PROMPT is non-nil, prompt for the Git command to run."
1591 (vc-filter-command-function 1614 (vc-filter-command-function
1592 (if prompt 1615 (if prompt
1593 (lambda (&rest args) 1616 (lambda (&rest args)
1594 (cl-destructuring-bind (&whole args git _ flags) 1617 (cl-destructuring-bind
1618 (&whole args git _ flags
1619 &aux (vc-user-edit-command-history 'vc-git-history))
1595 (apply #'vc-user-edit-command args) 1620 (apply #'vc-user-edit-command args)
1596 (setq git-program git 1621 (setq git-program git
1597 command (car flags) 1622 command (car flags)
@@ -2567,9 +2592,9 @@ This command shares argument histories with \\[rgrep] and \\[grep]."
2567 ;; In *vc-dir*, if nothing is marked, act on the whole working tree 2592 ;; In *vc-dir*, if nothing is marked, act on the whole working tree
2568 ;; regardless of the position of point. This preserves historical 2593 ;; regardless of the position of point. This preserves historical
2569 ;; behavior and is also probably more useful. 2594 ;; behavior and is also probably more useful.
2570 (if (derived-mode-p 'vc-dir-mode) 2595 (mapcar #'file-relative-name (if (derived-mode-p 'vc-dir-mode)
2571 (vc-dir-marked-files) 2596 (vc-dir-marked-files)
2572 (cadr (vc-deduce-fileset)))) 2597 (cadr (vc-deduce-fileset)))))
2573 2598
2574(defun vc-git-stash (name) 2599(defun vc-git-stash (name)
2575 "Create a stash named NAME. 2600 "Create a stash named NAME.
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index 770906ff6cc..88324a2a444 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -3330,15 +3330,13 @@ to which `vc-push' would push as UPSTREAM-LOCATION, unconditionally.
3330(This is passed when the user invokes an outgoing base command with a 3330(This is passed when the user invokes an outgoing base command with a
3331 \\`C-u C-u' prefix argument; see `vc--maybe-read-outgoing-base'.) 3331 \\`C-u C-u' prefix argument; see `vc--maybe-read-outgoing-base'.)
3332REFRESH is passed on to `vc--incoming-revision'." 3332REFRESH is passed on to `vc--incoming-revision'."
3333 (if-let* ((incoming 3333 (vc-call-backend backend 'mergebase
3334 (vc--incoming-revision backend 3334 (vc--incoming-revision backend
3335 (pcase upstream-location 3335 (pcase upstream-location
3336 ('t nil) 3336 ('t nil)
3337 ('nil (vc--outgoing-base backend)) 3337 ('nil (vc--outgoing-base backend))
3338 (_ upstream-location)) 3338 (_ upstream-location))
3339 refresh))) 3339 refresh)))
3340 (vc-call-backend backend 'mergebase incoming)
3341 (user-error "No incoming revision -- local-only branch?")))
3342 3340
3343;;;###autoload 3341;;;###autoload
3344(defun vc-root-diff-outgoing-base (&optional upstream-location) 3342(defun vc-root-diff-outgoing-base (&optional upstream-location)
@@ -3349,7 +3347,9 @@ Uncommitted changes are included in the diff.
3349 3347
3350When unspecified, UPSTREAM-LOCATION is the outgoing base. 3348When unspecified, UPSTREAM-LOCATION is the outgoing base.
3351For a trunk branch this is always the place \\[vc-push] would push to. 3349For a trunk branch this is always the place \\[vc-push] would push to.
3352For a topic branch, query the backend for an appropriate outgoing base. 3350For a topic branch, see whether the branch matches one of
3351`vc-trunk-branch-regexps' or `vc-topic-branch-regexps', or else query
3352the backend for an appropriate outgoing base.
3353See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding 3353See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding
3354the difference between trunk and topic branches. 3354the difference between trunk and topic branches.
3355 3355
@@ -3377,7 +3377,9 @@ Uncommitted changes are included in the diff.
3377 3377
3378When unspecified, UPSTREAM-LOCATION is the outgoing base. 3378When unspecified, UPSTREAM-LOCATION is the outgoing base.
3379For a trunk branch this is always the place \\[vc-push] would push to. 3379For a trunk branch this is always the place \\[vc-push] would push to.
3380For a topic branch, query the backend for an appropriate outgoing base. 3380For a topic branch, see whether the branch matches one of
3381`vc-trunk-branch-regexps' or `vc-topic-branch-regexps', or else query
3382the backend for an appropriate outgoing base.
3381See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding 3383See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding
3382the difference between trunk and topic branches. 3384the difference between trunk and topic branches.
3383 3385
@@ -3411,7 +3413,9 @@ working revision and UPSTREAM-LOCATION.
3411 3413
3412When unspecified, UPSTREAM-LOCATION is the outgoing base. 3414When unspecified, UPSTREAM-LOCATION is the outgoing base.
3413For a trunk branch this is always the place \\[vc-push] would push to. 3415For a trunk branch this is always the place \\[vc-push] would push to.
3414For a topic branch, query the backend for an appropriate outgoing base. 3416For a topic branch, see whether the branch matches one of
3417`vc-trunk-branch-regexps' or `vc-topic-branch-regexps', or else query
3418the backend for an appropriate outgoing base.
3415See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding 3419See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding
3416the difference between trunk and topic branches. 3420the difference between trunk and topic branches.
3417 3421
@@ -3443,7 +3447,9 @@ working revision and UPSTREAM-LOCATION.
3443 3447
3444When unspecified, UPSTREAM-LOCATION is the outgoing base. 3448When unspecified, UPSTREAM-LOCATION is the outgoing base.
3445For a trunk branch this is always the place \\[vc-push] would push to. 3449For a trunk branch this is always the place \\[vc-push] would push to.
3446For a topic branch, query the backend for an appropriate outgoing base. 3450For a topic branch, see whether the branch matches one of
3451`vc-trunk-branch-regexps' or `vc-topic-branch-regexps', or else query
3452the backend for an appropriate outgoing base.
3447See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding 3453See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding
3448the difference between trunk and topic branches. 3454the difference between trunk and topic branches.
3449 3455
@@ -4435,20 +4441,23 @@ BACKEND is the VC backend."
4435 ;; Do store `nil', before signaling an error, if there is no incoming 4441 ;; Do store `nil', before signaling an error, if there is no incoming
4436 ;; revision, because that's also something that can be slow to 4442 ;; revision, because that's also something that can be slow to
4437 ;; determine and so should be remembered. 4443 ;; determine and so should be remembered.
4438 (if-let* ((_ (not refresh)) 4444 (or (if-let* ((_ (not refresh))
4439 (record (assoc upstream-location 4445 (record (assoc upstream-location
4440 (vc--repo-getprop backend 'vc-incoming-revision)))) 4446 (vc--repo-getprop backend
4441 (cdr record) 4447 'vc-incoming-revision))))
4442 (let ((res (vc-call-backend backend 'incoming-revision 4448 (cdr record)
4443 upstream-location refresh))) 4449 (let ((res (vc-call-backend backend 'incoming-revision
4444 (if-let* ((alist (vc--repo-getprop backend 'vc-incoming-revision))) 4450 upstream-location refresh)))
4445 (setf (alist-get upstream-location alist nil nil #'equal) 4451 (if-let* ((alist (vc--repo-getprop backend
4446 res) 4452 'vc-incoming-revision)))
4447 (vc--repo-setprop backend 4453 (setf (alist-get upstream-location alist
4448 'vc-incoming-revision 4454 nil nil #'equal)
4449 `((,upstream-location . ,res)))) 4455 res)
4450 (or res 4456 (vc--repo-setprop backend
4451 (user-error "No incoming revision -- local-only branch?"))))) 4457 'vc-incoming-revision
4458 `((,upstream-location . ,res))))
4459 res))
4460 (user-error "No incoming revision -- local-only branch?")))
4452 4461
4453;;;###autoload 4462;;;###autoload
4454(defun vc-root-log-incoming (&optional upstream-location) 4463(defun vc-root-log-incoming (&optional upstream-location)
@@ -5017,6 +5026,9 @@ log entries should be gathered."
5017 5026
5018(defvar vc-filter-command-function) 5027(defvar vc-filter-command-function)
5019 5028
5029(defvar vc-edit-next-command-history nil
5030 "Minibuffer history for `vc-edit-next-command'.")
5031
5020;;;###autoload 5032;;;###autoload
5021(defun vc-edit-next-command () 5033(defun vc-edit-next-command ()
5022 "Request editing the next VC shell command before execution. 5034 "Request editing the next VC shell command before execution.
@@ -5040,7 +5052,8 @@ immediately after this one."
5040 (add-hook 'prefix-command-echo-keystrokes-functions echofun) 5052 (add-hook 'prefix-command-echo-keystrokes-functions echofun)
5041 (setq vc-filter-command-function 5053 (setq vc-filter-command-function
5042 (lambda (&rest args) 5054 (lambda (&rest args)
5043 (apply #'vc-user-edit-command (apply old args)))))) 5055 (let ((vc-user-edit-command-history 'vc-edit-next-command-history))
5056 (apply #'vc-user-edit-command (apply old args)))))))
5044 5057
5045;; This is used in .dir-locals.el in the Emacs source tree. 5058;; This is used in .dir-locals.el in the Emacs source tree.
5046;;;###autoload (put 'vc-prepare-patches-separately 'safe-local-variable 'booleanp) 5059;;;###autoload (put 'vc-prepare-patches-separately 'safe-local-variable 'booleanp)