diff options
| author | Helmut Eller | 2026-02-13 09:10:16 +0100 |
|---|---|---|
| committer | Helmut Eller | 2026-02-13 09:10:16 +0100 |
| commit | 91c9e9883488d715a30877dfd7641ef4b3c62658 (patch) | |
| tree | e2c4525147e443f86baf9d0144aeadec082d7564 /lisp/vc | |
| parent | 9a4a54af9192a6653164364c75721ee814ffb1e8 (diff) | |
| parent | f1fe4d46190263e164ccd1e066095d46a156297f (diff) | |
| download | emacs-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.el | 170 | ||||
| -rw-r--r-- | lisp/vc/vc-dispatcher.el | 6 | ||||
| -rw-r--r-- | lisp/vc/vc-git.el | 143 | ||||
| -rw-r--r-- | lisp/vc/vc.el | 69 |
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. |
| 203 | These bindings are also available with an ESC prefix | 203 | These 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 |
| 205 | and with a `diff-minor-mode-prefix' prefix in `diff-minor-mode'. | 205 | particular read-write `diff-mode' buffers, and with a |
| 206 | `diff-minor-mode-prefix' prefix in `diff-minor-mode'. | ||
| 206 | See also `diff-mode-read-only-map'." | 207 | See 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. |
| 234 | Most of the bindings for read-only `diff-mode' buffers are in | 228 | Most 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 |
| 236 | read-only `diff-mode' buffers that are *not* available with an ESC | 230 | read-only `diff-mode' buffers that are *not* also available with an ESC |
| 237 | prefix (i.e. a \\=`M-' prefix) in read-write `diff-mode' buffers." | 231 | prefix (i.e. a \\=`M-' prefix) in read-write (nor read-only) `diff-mode' |
| 232 | buffers." | ||
| 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) | 894 | When killing the last hunk left for a file, kill the file header too. |
| 888 | (if (not (diff--some-hunks-p)) | 895 | Interactively, if the region is active, kill all hunks that the region |
| 889 | (error "No hunks") | 896 | overlaps. |
| 890 | (diff-beginning-of-hunk t) | 897 | |
| 891 | (let* ((hunk-bounds (diff-bounds-of-hunk)) | 898 | When called from Lisp with optional arguments BEG and END non-nil, kill |
| 892 | (file-bounds (ignore-errors (diff-bounds-of-file))) | 899 | all 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 | 900 | interactively 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. | ||
| 2282 | If BEG and END are nil, kill the hunk at point. | ||
| 2283 | Otherwise kill all hunks overlapped by region delimited by BEG and END. | ||
| 2284 | When killing a hunk that's the only one remaining for its file, kill the | ||
| 2285 | file header too. | ||
| 2286 | If 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. |
| 2287 | Interactively, if the region is active, reverse-apply and kill all | 2359 | Interactively, 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. |
| 389 | Intended to be used as the value of `vc-filter-command-function'." | 392 | Intended 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. | ||
| 777 | If there is a configured upstream, return the remote-tracking branch | ||
| 778 | with key `upstream'. If there is a distinct configured push remote, | ||
| 779 | return the remote-tracking branch there with key `push'. | ||
| 780 | A configured push remote that's just the same as the upstream remote is | ||
| 781 | ignored 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. |
| 777 | This is able to identify topic branches for certain forge workflows." | 804 | This 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. |
| 796 | This works by considering the current branch as a topic branch | 810 | This works by considering the current branch as a topic branch |
| 797 | (whether or not it actually is). | 811 | (whether or not it actually is). |
| 798 | Requires that the corresponding trunk exists as a local branch. | 812 | |
| 799 | 813 | If there is a distinct push remote for this branch, assume the target | |
| 800 | The algorithm employed is as follows. Find all merge bases between the | 814 | for outstanding changes is the tracking branch, and return that. |
| 801 | current branch and other local branches. Each of these is a commit on | 815 | |
| 802 | the current branch. Use `git merge-base --independent' on them all to | 816 | Otherwise, fall back to the following algorithm, which requires that the |
| 803 | find the topologically most recent. Take the branch for which that | 817 | corresponding trunk exists as a local branch. Find all merge bases |
| 804 | commit is a merge base with the current branch to be the branch into | 818 | between the current branch and other local branches. Each of these is a |
| 805 | which the current branch will eventually be merged. Find its upstream. | 819 | commit on the current branch. Use `git merge-base --independent' on |
| 806 | (If there is more than one branch whose merge base with the current | 820 | them all to find the topologically most recent. Take the branch for |
| 807 | branch is that same topologically most recent commit, try them | 821 | which that commit is a merge base with the current branch to be the |
| 808 | one-by-one, accepting the first that has an upstream.)" | 822 | branch into which the current branch will eventually be merged. Find |
| 809 | (cl-flet ((get-line () (buffer-substring (point) (pos-eol)))) | 823 | its upstream. (If there is more than one branch whose merge base with |
| 810 | (let* ((branches (vc-git-branches)) | 824 | the current branch is that same topologically most recent commit, try |
| 811 | (current (pop branches)) | 825 | them 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'.) |
| 3332 | REFRESH is passed on to `vc--incoming-revision'." | 3332 | REFRESH 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 | ||
| 3350 | When unspecified, UPSTREAM-LOCATION is the outgoing base. | 3348 | When unspecified, UPSTREAM-LOCATION is the outgoing base. |
| 3351 | For a trunk branch this is always the place \\[vc-push] would push to. | 3349 | For a trunk branch this is always the place \\[vc-push] would push to. |
| 3352 | For a topic branch, query the backend for an appropriate outgoing base. | 3350 | For a topic branch, see whether the branch matches one of |
| 3351 | `vc-trunk-branch-regexps' or `vc-topic-branch-regexps', or else query | ||
| 3352 | the backend for an appropriate outgoing base. | ||
| 3353 | See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding | 3353 | See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding |
| 3354 | the difference between trunk and topic branches. | 3354 | the difference between trunk and topic branches. |
| 3355 | 3355 | ||
| @@ -3377,7 +3377,9 @@ Uncommitted changes are included in the diff. | |||
| 3377 | 3377 | ||
| 3378 | When unspecified, UPSTREAM-LOCATION is the outgoing base. | 3378 | When unspecified, UPSTREAM-LOCATION is the outgoing base. |
| 3379 | For a trunk branch this is always the place \\[vc-push] would push to. | 3379 | For a trunk branch this is always the place \\[vc-push] would push to. |
| 3380 | For a topic branch, query the backend for an appropriate outgoing base. | 3380 | For a topic branch, see whether the branch matches one of |
| 3381 | `vc-trunk-branch-regexps' or `vc-topic-branch-regexps', or else query | ||
| 3382 | the backend for an appropriate outgoing base. | ||
| 3381 | See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding | 3383 | See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding |
| 3382 | the difference between trunk and topic branches. | 3384 | the difference between trunk and topic branches. |
| 3383 | 3385 | ||
| @@ -3411,7 +3413,9 @@ working revision and UPSTREAM-LOCATION. | |||
| 3411 | 3413 | ||
| 3412 | When unspecified, UPSTREAM-LOCATION is the outgoing base. | 3414 | When unspecified, UPSTREAM-LOCATION is the outgoing base. |
| 3413 | For a trunk branch this is always the place \\[vc-push] would push to. | 3415 | For a trunk branch this is always the place \\[vc-push] would push to. |
| 3414 | For a topic branch, query the backend for an appropriate outgoing base. | 3416 | For a topic branch, see whether the branch matches one of |
| 3417 | `vc-trunk-branch-regexps' or `vc-topic-branch-regexps', or else query | ||
| 3418 | the backend for an appropriate outgoing base. | ||
| 3415 | See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding | 3419 | See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding |
| 3416 | the difference between trunk and topic branches. | 3420 | the difference between trunk and topic branches. |
| 3417 | 3421 | ||
| @@ -3443,7 +3447,9 @@ working revision and UPSTREAM-LOCATION. | |||
| 3443 | 3447 | ||
| 3444 | When unspecified, UPSTREAM-LOCATION is the outgoing base. | 3448 | When unspecified, UPSTREAM-LOCATION is the outgoing base. |
| 3445 | For a trunk branch this is always the place \\[vc-push] would push to. | 3449 | For a trunk branch this is always the place \\[vc-push] would push to. |
| 3446 | For a topic branch, query the backend for an appropriate outgoing base. | 3450 | For a topic branch, see whether the branch matches one of |
| 3451 | `vc-trunk-branch-regexps' or `vc-topic-branch-regexps', or else query | ||
| 3452 | the backend for an appropriate outgoing base. | ||
| 3447 | See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding | 3453 | See `vc-trunk-branch-regexps' and `vc-topic-branch-regexps' regarding |
| 3448 | the difference between trunk and topic branches. | 3454 | the 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) |