diff options
Diffstat (limited to 'lisp/vc/vc-git.el')
| -rw-r--r-- | lisp/vc/vc-git.el | 143 |
1 files changed, 84 insertions, 59 deletions
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. |