diff options
Diffstat (limited to 'elpa/magit-4.3.1/magit-push.el')
-rw-r--r-- | elpa/magit-4.3.1/magit-push.el | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/elpa/magit-4.3.1/magit-push.el b/elpa/magit-4.3.1/magit-push.el new file mode 100644 index 0000000..51bd4f3 --- /dev/null +++ b/elpa/magit-4.3.1/magit-push.el @@ -0,0 +1,373 @@ +;;; magit-push.el --- Update remote objects and refs -*- lexical-binding:t -*- + +;; Copyright (C) 2008-2025 The Magit Project Contributors + +;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev> +;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev> + +;; SPDX-License-Identifier: GPL-3.0-or-later + +;; Magit is free software: you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; Magit is distributed in the hope that it will be useful, but WITHOUT +;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +;; License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Magit. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This library implements push commands. + +;;; Code: + +(require 'magit) + +;;; Commands + +;;;###autoload (autoload 'magit-push "magit-push" nil t) +(transient-define-prefix magit-push () + "Push to another repository." + :man-page "git-push" + ["Arguments" + ("-f" "Force with lease" (nil "--force-with-lease")) + ("-F" "Force" ("-f" "--force")) + ("-h" "Disable hooks" "--no-verify") + ("-n" "Dry run" ("-n" "--dry-run")) + (5 "-u" "Set upstream" "--set-upstream") + (7 "-t" "Follow tags" "--follow-tags")] + [:if magit-get-current-branch + :description (lambda () + (format (propertize "Push %s to" 'face 'transient-heading) + (propertize (magit-get-current-branch) + 'face 'magit-branch-local))) + ("p" magit-push-current-to-pushremote) + ("u" magit-push-current-to-upstream) + ("e" "elsewhere" magit-push-current)] + ["Push" + [("o" "another branch" magit-push-other) + ("r" "explicit refspecs" magit-push-refspecs) + ("m" "matching branches" magit-push-matching)] + [("T" "a tag" magit-push-tag) + ("t" "all tags" magit-push-tags) + (6 "n" "a note ref" magit-push-notes-ref)]] + ["Configure" + ("C" "Set variables..." magit-branch-configure)]) + +(defun magit-push-arguments () + (transient-args 'magit-push)) + +(defun magit-git-push (branch target args) + (run-hooks 'magit-credential-hook) + ;; If the remote branch already exists, then we do not have to + ;; qualify the target, which we prefer to avoid doing because + ;; using the default namespace is wrong in obscure cases. + (pcase-let ((namespace (if (magit-get-tracked target) "" "refs/heads/")) + (`(,remote . ,target) + (magit-split-branch-name target))) + (magit-run-git-async "push" "-v" args remote + (format "%s:%s%s" branch namespace target)))) + +;;;###autoload (autoload 'magit-push-current-to-pushremote "magit-push" nil t) +(transient-define-suffix magit-push-current-to-pushremote (args) + "Push the current branch to its push-remote. + +When the push-remote is not configured, then read the push-remote +from the user, set it, and then push to it. With a prefix +argument the push-remote can be changed before pushed to it." + :if #'magit-get-current-branch + :description #'magit-push--pushbranch-description + (interactive (list (magit-push-arguments))) + (pcase-let ((`(,branch ,remote ,changed) + (magit--select-push-remote "push there"))) + (when changed + (magit-confirm 'set-and-push + (list "Really use \"%s\" as push-remote and push \"%s\" there" + remote branch))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args remote + (format "refs/heads/%s:refs/heads/%s" + branch branch)))) ; see #3847 and #3872 + +(defun magit-push--pushbranch-description () + (let* ((branch (magit-get-current-branch)) + (target (magit-get-push-branch branch t)) + (remote (magit-get-push-remote branch)) + (v (magit--push-remote-variable branch t))) + (cond + (target) + ((member remote (magit-list-remotes)) + (format "%s, creating it" + (magit--propertize-face (concat remote "/" branch) + 'magit-branch-remote))) + (remote + (format "%s, replacing invalid" v)) + (t + (format "%s, setting that" v))))) + +;;;###autoload (autoload 'magit-push-current-to-upstream "magit-push" nil t) +(transient-define-suffix magit-push-current-to-upstream (args) + "Push the current branch to its upstream branch. + +With a prefix argument or when the upstream is either not +configured or unusable, then let the user first configure +the upstream." + :if #'magit-get-current-branch + :description #'magit-push--upstream-description + (interactive (list (magit-push-arguments))) + (let* ((branch (or (magit-get-current-branch) + (user-error "No branch is checked out"))) + (remote (magit-get "branch" branch "remote")) + (merge (magit-get "branch" branch "merge"))) + (when (or current-prefix-arg + (not (or (magit-get-upstream-branch branch) + (magit--unnamed-upstream-p remote merge) + (magit--valid-upstream-p remote merge)))) + (let* ((branches (cl-union (mapcar (##concat % "/" branch) + (magit-list-remotes)) + (magit-list-remote-branch-names) + :test #'equal)) + (upstream (magit-completing-read + (format "Set upstream of %s and push there" branch) + branches nil nil nil 'magit-revision-history + (or (car (member (magit-remote-branch-at-point) branches)) + (car (member "origin/master" branches))))) + (upstream* (or (magit-get-tracked upstream) + (magit-split-branch-name upstream)))) + (setq remote (car upstream*)) + (setq merge (cdr upstream*)) + (unless (string-prefix-p "refs/" merge) + ;; User selected a non-existent remote-tracking branch. + ;; It is very likely, but not certain, that this is the + ;; correct thing to do. It is even more likely that it + ;; is what the user wants to happen. + (setq merge (concat "refs/heads/" merge))) + (magit-confirm 'set-and-push + (list "Really use \"%s\" as upstream and push \"%s\" there" + upstream branch))) + (cl-pushnew "--set-upstream" args :test #'equal)) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args remote (concat branch ":" merge)))) + +(defun magit-push--upstream-description () + (and-let* ((branch (magit-get-current-branch))) + (or (magit-get-upstream-branch branch) + (let ((remote (magit-get "branch" branch "remote")) + (merge (magit-get "branch" branch "merge")) + (u (magit--propertize-face "@{upstream}" 'bold))) + (cond + ((magit--unnamed-upstream-p remote merge) + (format "%s as %s" + (magit--propertize-face remote 'bold) + (magit--propertize-face merge 'magit-branch-remote))) + ((magit--valid-upstream-p remote merge) + (format "%s creating %s" + (magit--propertize-face remote 'magit-branch-remote) + (magit--propertize-face merge 'magit-branch-remote))) + ((or remote merge) + (concat u ", creating it and replacing invalid")) + (t + (concat u ", creating it"))))))) + +;;;###autoload +(defun magit-push-current (target args) + "Push the current branch to a branch read in the minibuffer." + (interactive + (if-let ((current (magit-get-current-branch))) + (list (magit-read-remote-branch (format "Push %s to" current) + nil nil current 'confirm) + (magit-push-arguments)) + (user-error "No branch is checked out"))) + (magit-git-push (magit-get-current-branch) target args)) + +;;;###autoload +(defun magit-push-other (source target args) + "Push an arbitrary branch or commit somewhere. +Both the source and the target are read in the minibuffer." + (interactive + (let ((source (magit-read-local-branch-or-commit "Push"))) + (list source + (magit-read-remote-branch + (format "Push %s to" source) nil + (if (magit-local-branch-p source) + (or (magit-get-push-branch source) + (magit-get-upstream-branch source)) + (and (magit-rev-ancestor-p source "HEAD") + (or (magit-get-push-branch) + (magit-get-upstream-branch)))) + source 'confirm) + (magit-push-arguments)))) + (magit-git-push source target args)) + +(defvar magit-push-refspecs-history nil) + +;;;###autoload +(defun magit-push-refspecs (remote refspecs args) + "Push one or multiple REFSPECS to a REMOTE. +Both the REMOTE and the REFSPECS are read in the minibuffer. To +use multiple REFSPECS, separate them with commas. Completion is +only available for the part before the colon, or when no colon +is used." + (interactive + (list (magit-read-remote "Push to remote") + (magit-completing-read-multiple + "Push refspec,s: " + (cons "HEAD" (magit-list-local-branch-names)) + nil nil nil 'magit-push-refspecs-history) + (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args remote refspecs)) + +;;;###autoload +(defun magit-push-matching (remote &optional args) + "Push all matching branches to another repository. +If multiple remotes exist, then read one from the user. +If just one exists, use that without requiring confirmation." + (interactive (list (magit-read-remote "Push matching branches to" nil t) + (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args remote ":")) + +;;;###autoload +(defun magit-push-tags (remote &optional args) + "Push all tags to another repository. +If only one remote exists, then push to that. Otherwise prompt +for a remote, offering the remote configured for the current +branch as default." + (interactive (list (magit-read-remote "Push tags to remote" nil t) + (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" remote "--tags" args)) + +;;;###autoload +(defun magit-push-tag (tag remote &optional args) + "Push a tag to another repository." + (interactive + (let ((tag (magit-read-tag "Push tag"))) + (list tag (magit-read-remote (format "Push %s to remote" tag) nil t) + (magit-push-arguments)))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" remote tag args)) + +;;;###autoload +(defun magit-push-notes-ref (ref remote &optional args) + "Push a notes ref to another repository." + (interactive + (let ((note (magit-notes-read-ref "Push notes" nil nil))) + (list note + (magit-read-remote (format "Push %s to remote" note) nil t) + (magit-push-arguments)))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" remote ref args)) + +;;;###autoload (autoload 'magit-push-implicitly "magit-push" nil t) +(transient-define-suffix magit-push-implicitly (args) + "Push somewhere without using an explicit refspec. + +This command simply runs \"git push -v [ARGS]\". ARGS are the +arguments specified in the popup buffer. No explicit refspec +arguments are used. Instead the behavior depends on at least +these Git variables: `push.default', `remote.pushDefault', +`branch.<branch>.pushRemote', `branch.<branch>.remote', +`branch.<branch>.merge', and `remote.<remote>.push'. + +If you add this suffix to a transient prefix without explicitly +specifying the description, then an attempt is made to predict +what this command will do. To add it use something like: + + (transient-insert-suffix \\='magit-push \"o\" + \\='(\"i\" magit-push-implicitly))" + :description #'magit-push-implicitly--desc + (interactive (list (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args)) + +(defun magit-push-implicitly--desc () + ;; This implements the logic for git push as documented. + ;; First, we resolve a remote to use based on various remote and + ;; pushRemote options. + ;; Then, we resolve the refspec to use for the remote based on push + ;; and pushDefault options. + ;; Note that the remote and refspec to push are handled separately, + ;; so it doesn't make sense to talk about "pushing to upstream". + ;; Depending on the options, you could end up pushing to the + ;; "upstream" remote but not the "upstream" branch, and vice versa. + (let* ((branch (magit-get-current-branch)) + (remote (or (magit-get-push-remote branch) + ;; Note: Avoid `magit-get-remote' because it + ;; filters out the local repo case ("."). + (magit-get "branch" branch "remote") + (let ((remotes (magit-list-remotes))) + (cond + ((and (magit-git-version>= "2.27") + (length= remotes 1)) + (car remotes)) + ((member "origin" remotes) "origin")))))) + (if (null remote) + "nothing (no remote)" + (let ((refspec (magit-get "remote" remote "push"))) + (if refspec + (format "to %s with refspecs %s" + (magit--propertize-face remote 'bold) + (magit--propertize-face refspec 'bold)) + (pcase (or (magit-get "push.default") "simple") + ("nothing" "nothing (due to push.default)") + ((or "current" "simple") + (format "%s to %s" + (magit--propertize-face branch 'magit-branch-current) + (magit--propertize-face (format "%s/%s" remote branch) + 'magit-branch-remote))) + ((or "upstream" "tracking") + (let ((ref (magit-get "branch" branch "merge"))) + (if ref + (format "%s to %s" + (magit--propertize-face branch 'magit-branch-current) + (cond + ((string-prefix-p "refs/heads/" ref) + (magit--propertize-face + (format "%s/%s" remote + (substring ref (length "refs/heads/"))) + 'magit-branch-remote)) + ((not (string-match "/" ref)) + (magit--propertize-face (format "%s/%s" remote ref) + 'magit-branch-remote)) + ((format "%s as %s" + (magit--propertize-face remote 'bold) + (magit--propertize-face ref 'bold))))) + "nothing (no upstream)"))) + ("matching" (format "all matching to %s" + (magit--propertize-face remote 'bold))))))))) + +;;;###autoload (autoload 'magit-push-to-remote "magit-push" nil t) +(transient-define-suffix magit-push-to-remote (remote args) + "Push to REMOTE without using an explicit refspec. +The REMOTE is read in the minibuffer. + +This command simply runs \"git push -v [ARGS] REMOTE\". ARGS +are the arguments specified in the popup buffer. No refspec +arguments are used. Instead the behavior depends on at least +these Git variables: `push.default', `remote.pushDefault', +`branch.<branch>.pushRemote', `branch.<branch>.remote', +`branch.<branch>.merge', and `remote.<remote>.push'. + +You can add this command as a suffix using something like: + + (transient-insert-suffix \\='magit-push \"o\" + \\='(\"x\" magit-push-to-remote))" + :description #'magit-push-to-remote--desc + (interactive (list (magit-read-remote "Push to remote") + (magit-push-arguments))) + (run-hooks 'magit-credential-hook) + (magit-run-git-async "push" "-v" args remote)) + +(defun magit-push-to-remote--desc () + (format "using %s" (magit--propertize-face "git push <remote>" 'bold))) + +;;; _ +(provide 'magit-push) +;;; magit-push.el ends here |