| 1 | ;;; magit-pull.el --- Update local objects and refs -*- lexical-binding:t -*- |
| 2 | |
| 3 | ;; Copyright (C) 2008-2026 The Magit Project Contributors |
| 4 | |
| 5 | ;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev> |
| 6 | ;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev> |
| 7 | |
| 8 | ;; SPDX-License-Identifier: GPL-3.0-or-later |
| 9 | |
| 10 | ;; Magit is free software: you can redistribute it and/or modify it |
| 11 | ;; under the terms of the GNU General Public License as published by |
| 12 | ;; the Free Software Foundation, either version 3 of the License, or |
| 13 | ;; (at your option) any later version. |
| 14 | ;; |
| 15 | ;; Magit is distributed in the hope that it will be useful, but WITHOUT |
| 16 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| 17 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
| 18 | ;; License for more details. |
| 19 | ;; |
| 20 | ;; You should have received a copy of the GNU General Public License |
| 21 | ;; along with Magit. If not, see <https://www.gnu.org/licenses/>. |
| 22 | |
| 23 | ;;; Commentary: |
| 24 | |
| 25 | ;; This library implements pull commands. |
| 26 | |
| 27 | ;;; Code: |
| 28 | |
| 29 | (require 'magit) |
| 30 | |
| 31 | ;;; Options |
| 32 | |
| 33 | (defcustom magit-pull-or-fetch nil |
| 34 | "Whether `magit-pull' also offers some fetch suffixes." |
| 35 | :package-version '(magit . "3.0.0") |
| 36 | :group 'magit-commands |
| 37 | :type 'boolean) |
| 38 | |
| 39 | ;;; Commands |
| 40 | |
| 41 | ;;;###autoload(autoload 'magit-pull "magit-pull" nil t) |
| 42 | (transient-define-prefix magit-pull () |
| 43 | "Pull from another repository." |
| 44 | :man-page "git-pull" |
| 45 | :incompatible |
| 46 | '(("--ff-only" "--rebase=true" "--rebase=merges" "--rebase=interactive")) |
| 47 | [:description |
| 48 | (lambda () (if magit-pull-or-fetch "Pull arguments" "Arguments")) |
| 49 | ("-f" "Fast-forward only" "--ff-only") |
| 50 | ("-r" magit-pull:--rebase) |
| 51 | ("-A" "Autostash" "--autostash" :level 7) |
| 52 | ("-F" "Force" ("-f" "--force"))] |
| 53 | [:description |
| 54 | (lambda () |
| 55 | (if-let ((branch (magit-get-current-branch))) |
| 56 | (concat |
| 57 | (propertize "Pull into " 'face 'transient-heading) |
| 58 | (propertize branch 'face 'magit-branch-local) |
| 59 | (propertize " from" 'face 'transient-heading)) |
| 60 | (propertize "Pull from" 'face 'transient-heading))) |
| 61 | ("p" magit-pull-from-pushremote) |
| 62 | ("u" magit-pull-from-upstream) |
| 63 | ("e" "elsewhere" magit-pull-branch)] |
| 64 | [:description |
| 65 | (lambda () |
| 66 | (format (propertize "Pull into %s from" 'face 'transient-heading) |
| 67 | (magit-get-upstream-branch))) |
| 68 | ("U" magit-pull-into-upstream :level 7)] |
| 69 | ["Fetch from" |
| 70 | :if-non-nil magit-pull-or-fetch |
| 71 | ("f" "remotes" magit-fetch-all-no-prune) |
| 72 | ("F" "remotes and prune" magit-fetch-all-prune)] |
| 73 | ["Fetch" |
| 74 | :if-non-nil magit-pull-or-fetch |
| 75 | ("o" "another branch" magit-fetch-branch) |
| 76 | ("s" "explicit refspec" magit-fetch-refspec) |
| 77 | ("m" "submodules" magit-fetch-modules)] |
| 78 | ["Configure" |
| 79 | ("r" magit-branch.<branch>.rebase :if magit-get-current-branch) |
| 80 | ("C" "variables..." magit-branch-configure)] |
| 81 | (interactive) |
| 82 | (transient-setup 'magit-pull nil nil :scope (magit-get-current-branch))) |
| 83 | |
| 84 | (defun magit-pull-arguments () |
| 85 | (transient-args 'magit-pull)) |
| 86 | |
| 87 | (transient-define-infix magit-pull:--rebase () |
| 88 | :class 'transient-option |
| 89 | :description "Rebase local commits" |
| 90 | :argument "--rebase=" |
| 91 | :shortarg "-r" |
| 92 | :choices '("true" "merges" "interactive" "false")) |
| 93 | |
| 94 | ;;;###autoload(autoload 'magit-pull-from-pushremote "magit-pull" nil t) |
| 95 | (transient-define-suffix magit-pull-from-pushremote (args) |
| 96 | "Pull from the push-remote of the current branch. |
| 97 | |
| 98 | With a prefix argument or when the push-remote is either not |
| 99 | configured or unusable, then let the user first configure the |
| 100 | push-remote." |
| 101 | :if #'magit-get-current-branch |
| 102 | :description #'magit-pull--pushbranch-description |
| 103 | (interactive (list (magit-pull-arguments))) |
| 104 | (pcase-let ((`(,branch ,remote) |
| 105 | (magit--select-push-remote "pull from there"))) |
| 106 | (run-hooks 'magit-credential-hook) |
| 107 | (magit-run-git-with-editor "pull" args remote branch))) |
| 108 | |
| 109 | (defun magit-pull--pushbranch-description () |
| 110 | ;; Also used by `magit-rebase-onto-pushremote'. |
| 111 | (let* ((branch (magit-get-current-branch)) |
| 112 | (target (magit-get-push-branch branch t)) |
| 113 | (remote (magit-get-push-remote branch)) |
| 114 | (v (magit--push-remote-variable branch t))) |
| 115 | (cond (target) |
| 116 | ((member remote (magit-list-remotes)) |
| 117 | (format "%s, replacing non-existent" v)) |
| 118 | (remote |
| 119 | (format "%s, replacing invalid" v)) |
| 120 | ((format "%s, setting that" v))))) |
| 121 | |
| 122 | ;;;###autoload(autoload 'magit-pull-from-upstream "magit-pull" nil t) |
| 123 | (transient-define-suffix magit-pull-from-upstream (args) |
| 124 | "Pull from the upstream of the current branch. |
| 125 | |
| 126 | With a prefix argument or when the upstream is either not |
| 127 | configured or unusable, then let the user first configure |
| 128 | the upstream." |
| 129 | :if #'magit-get-current-branch |
| 130 | :description #'magit-pull--upstream-description |
| 131 | (interactive (list (magit-pull-arguments))) |
| 132 | (let* ((branch (or (magit-get-current-branch) |
| 133 | (user-error "No branch is checked out"))) |
| 134 | (remote (magit-get "branch" branch "remote")) |
| 135 | (merge (magit-get "branch" branch "merge"))) |
| 136 | (when (or current-prefix-arg |
| 137 | (not (or (magit-get-upstream-branch branch) |
| 138 | (magit--unnamed-upstream-p remote merge)))) |
| 139 | (magit-set-upstream-branch |
| 140 | branch (magit-read-upstream-branch |
| 141 | branch (format "Set upstream of %s and pull from there" branch))) |
| 142 | (setq remote (magit-get "branch" branch "remote")) |
| 143 | (setq merge (magit-get "branch" branch "merge"))) |
| 144 | (run-hooks 'magit-credential-hook) |
| 145 | (magit-run-git-with-editor "pull" args remote merge))) |
| 146 | |
| 147 | (defun magit-pull--upstream-description () |
| 148 | (and-let ((branch (magit-get-current-branch))) |
| 149 | (or (magit-get-upstream-branch branch) |
| 150 | (let ((remote (magit-get "branch" branch "remote")) |
| 151 | (merge (magit-get "branch" branch "merge")) |
| 152 | (u (magit--propertize-face "@{upstream}" 'bold))) |
| 153 | (cond |
| 154 | ((magit--unnamed-upstream-p remote merge) |
| 155 | (format "%s of %s" |
| 156 | (magit--propertize-face merge 'magit-branch-remote) |
| 157 | (magit--propertize-face remote 'bold))) |
| 158 | ((magit--valid-upstream-p remote merge) |
| 159 | (concat u ", replacing non-existent")) |
| 160 | ((or remote merge) |
| 161 | (concat u ", replacing invalid")) |
| 162 | ((concat u ", setting that"))))))) |
| 163 | |
| 164 | ;;;###autoload(autoload 'magit-pull-into-upstream "magit-pull" nil t) |
| 165 | (transient-define-suffix magit-pull-into-upstream () |
| 166 | "Update the upstream of the current branch from its own upstream. |
| 167 | |
| 168 | If the upstream of the current branch is a local branch, and the |
| 169 | upstream of that is a remote branch, then update the local upstream. |
| 170 | This makes it possible to update the upstream branch, without having |
| 171 | to first check it out." |
| 172 | :if #'magit-pull--upstreams |
| 173 | :description (##cdr (magit-pull--upstreams)) |
| 174 | (interactive) |
| 175 | (if-let ((upstreams (magit-pull--upstreams))) |
| 176 | (pcase-let* ((`(,up1 . ,up2) upstreams) |
| 177 | (`(,remote . ,branch) (magit-split-branch-name up2))) |
| 178 | (magit-run-git-async "fetch" remote branch) |
| 179 | (set-process-sentinel |
| 180 | magit-this-process |
| 181 | (lambda (process _event) |
| 182 | (when (memq (process-status process) '(exit signal)) |
| 183 | (magit-run-git-async "update-ref" (concat "refs/heads/" up1) |
| 184 | "FETCH_HEAD"))))) |
| 185 | (user-error "Cannot perform background update of upstream branch"))) |
| 186 | |
| 187 | (defun magit-pull--upstreams () |
| 188 | (and-let* ((up1 (magit-get-upstream-branch)) |
| 189 | (_ (magit-local-branch-p up1)) |
| 190 | (up2 (magit-get-upstream-branch up1)) |
| 191 | (_ (magit-remote-branch-p up2))) |
| 192 | (cons up1 up2))) |
| 193 | |
| 194 | ;;;###autoload |
| 195 | (defun magit-pull-branch (source args) |
| 196 | "Pull from a branch read in the minibuffer." |
| 197 | (interactive (list (magit-read-remote-branch "Pull" nil nil nil t) |
| 198 | (magit-pull-arguments))) |
| 199 | (run-hooks 'magit-credential-hook) |
| 200 | (pcase-let ((`(,remote . ,branch) |
| 201 | (magit-get-tracked source))) |
| 202 | (magit-run-git-with-editor "pull" args remote branch))) |
| 203 | |
| 204 | ;;; _ |
| 205 | (provide 'magit-pull) |
| 206 | ;; Local Variables: |
| 207 | ;; read-symbol-shorthands: ( |
| 208 | ;; ("and$" . "cond-let--and$") |
| 209 | ;; ("thread$" . "cond-let--thread$") |
| 210 | ;; ("when$" . "cond-let--when$") |
| 211 | ;; ("and-let*" . "cond-let--and-let*") |
| 212 | ;; ("and-let" . "cond-let--and-let") |
| 213 | ;; ("if-let*" . "cond-let--if-let*") |
| 214 | ;; ("if-let" . "cond-let--if-let") |
| 215 | ;; ("when-let*" . "cond-let--when-let*") |
| 216 | ;; ("when-let" . "cond-let--when-let") |
| 217 | ;; ("while-let*" . "cond-let--while-let*") |
| 218 | ;; ("while-let" . "cond-let--while-let") |
| 219 | ;; ("match-string" . "match-string") |
| 220 | ;; ("match-str" . "match-string-no-properties")) |
| 221 | ;; End: |
| 222 | ;;; magit-pull.el ends here |