Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

376 lines
15 KiB

;;; magit-remote.el --- transfer Git commits -*- lexical-binding: t -*-
;; Copyright (C) 2008-2018 The Magit Project Contributors
;;
;; You should have received a copy of the AUTHORS.md file which
;; lists all contributors. If not, see http://magit.vc/authors.
;; Author: Jonas Bernoulli <jonas@bernoul.li>
;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>
;; 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, 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 http://www.gnu.org/licenses.
;;; Commentary:
;; This library implements remote commands.
;;; Code:
(require 'magit)
;;; Options
(defcustom magit-remote-add-set-remote.pushDefault 'ask-if-unset
"Whether to set the value of `remote.pushDefault' after adding a remote.
If `ask', then always ask. If `ask-if-unset', then ask, but only
if the variable isn't set already. If nil, then don't ever set.
If the value is a string, then set without asking, provided that
the name of the added remote is equal to that string and the
variable isn't already set."
:package-version '(magit . "2.4.0")
:group 'magit-commands
:type '(choice (const :tag "ask if unset" ask-if-unset)
(const :tag "always ask" ask)
(string :tag "set if named")
(const :tag "don't set")))
(defcustom magit-remote-popup-show-variables t
"Whether the `magit-remote-popup' shows Git variables.
When set to nil, no variables are displayed directly in this
popup, instead the sub-popup `magit-remote-config-popup' has
to be used to view and change remote related variables."
:package-version '(magit . "2.12.0")
:group 'magit-commands
:type 'boolean)
;;; Commands
(defvar magit-remote-config-variables)
;;;###autoload (autoload 'magit-remote-popup "magit-remote" nil t)
(magit-define-popup magit-remote-popup
"Popup console for remote commands."
:man-page "git-remote"
:default-arguments '("-f")
:variables (lambda ()
(and magit-remote-popup-show-variables
magit-remote-config-variables))
:switches '("Switches for add"
(?f "Fetch after add" "-f"))
:actions '((?a "Add" magit-remote-add)
(?C "Configure..." magit-remote-config-popup)
(?r "Rename" magit-remote-rename)
(?p "Prune stale branches" magit-remote-prune)
(?k "Remove" magit-remote-remove)
(?P "Prune stale refspecs" magit-remote-prune-refspecs))
:max-action-columns 2)
(defun magit-read-url (prompt &optional initial-input)
(let ((url (magit-read-string-ns prompt initial-input)))
(if (string-prefix-p "~" url)
(expand-file-name url)
url)))
;;;###autoload
(defun magit-remote-add (remote url &optional args)
"Add a remote named REMOTE and fetch it."
(interactive (list (magit-read-string-ns "Remote name")
(magit-read-url "Remote url")
(magit-remote-arguments)))
(if (pcase (list magit-remote-add-set-remote.pushDefault
(magit-get "remote.pushDefault"))
(`(,(pred stringp) ,_) t)
((or `(ask ,_) `(ask-if-unset nil))
(y-or-n-p (format "Set `remote.pushDefault' to \"%s\"? " remote))))
(progn (magit-call-git "remote" "add" args remote url)
(setf (magit-get "remote.pushDefault") remote)
(magit-refresh))
(magit-run-git-async "remote" "add" args remote url)))
;;;###autoload
(defun magit-remote-rename (old new)
"Rename the remote named OLD to NEW."
(interactive
(let ((remote (magit-read-remote "Rename remote")))
(list remote (magit-read-string-ns (format "Rename %s to" remote)))))
(unless (string= old new)
(magit-call-git "remote" "rename" old new)
(magit-remote--cleanup-push-variables old new)
(magit-refresh)))
;;;###autoload
(defun magit-remote-remove (remote)
"Delete the remote named REMOTE."
(interactive (list (magit-read-remote "Delete remote")))
(magit-call-git "remote" "rm" remote)
(magit-remote--cleanup-push-variables remote)
(magit-refresh))
(defun magit-remote--cleanup-push-variables (remote &optional new-name)
(magit-with-toplevel
(when (equal (magit-get "remote.pushDefault") remote)
(magit-set new-name "remote.pushDefault"))
(dolist (var (magit-git-lines "config" "--name-only"
"--get-regexp" "^branch\.[^.]*\.pushRemote"
(format "^%s$" remote)))
(magit-call-git "config" (and (not new-name) "--unset") var new-name))))
(defconst magit--refspec-re "\\`\\(\\+\\)?\\([^:]+\\):\\(.*\\)\\'")
;;;###autoload
(defun magit-remote-prune (remote)
"Remove stale remote-tracking branches for REMOTE."
(interactive (list (magit-read-remote "Prune stale branches of remote")))
(magit-run-git-async "remote" "prune" remote))
;;;###autoload
(defun magit-remote-prune-refspecs (remote)
"Remove stale refspecs for REMOTE.
A refspec is stale if there no longer exists at least one branch
on the remote that would be fetched due to that refspec. A stale
refspec is problematic because its existence causes Git to refuse
to fetch according to the remaining non-stale refspecs.
If only stale refspecs remain, then offer to either delete the
remote or to replace the stale refspecs with the default refspec.
Also remove the remote-tracking branches that were created due to
the now stale refspecs. Other stale branches are not removed."
(interactive (list (magit-read-remote "Prune refspecs of remote")))
(let* ((tracking-refs (magit-list-remote-branches remote))
(remote-refs (magit-remote-list-refs remote))
(variable (format "remote.%s.fetch" remote))
(refspecs (magit-get-all variable))
stale)
(dolist (refspec refspecs)
(when (string-match magit--refspec-re refspec)
(let ((theirs (match-string 2 refspec))
(ours (match-string 3 refspec)))
(unless (if (string-match "\\*" theirs)
(let ((re (replace-match ".*" t t theirs)))
(--some (string-match-p re it) remote-refs))
(member theirs remote-refs))
(push (cons refspec
(if (string-match "\\*" ours)
(let ((re (replace-match ".*" t t ours)))
(--filter (string-match-p re it) tracking-refs))
(list (car (member ours tracking-refs)))))
stale)))))
(if (not stale)
(message "No stale refspecs for remote %S" remote)
(if (= (length stale)
(length refspecs))
(magit-read-char-case
(format "All of %s's refspecs are stale. " remote) nil
(?s "replace with [d]efault refspec"
(magit-set-all
(list (format "+refs/heads/*:refs/remotes/%s/*" remote))
variable))
(?r "[r]emove remote"
(magit-call-git "remote" "rm" remote))
(?a "or [a]abort"
(user-error "Abort")))
(if (if (= (length stale) 1)
(pcase-let ((`(,refspec . ,refs) (car stale)))
(magit-confirm 'prune-stale-refspecs
(format "Prune stale refspec %s and branch %%s" refspec)
(format "Prune stale refspec %s and %%i branches" refspec)
nil refs))
(magit-confirm 'prune-stale-refspecs nil
(format "Prune %%i stale refspecs and %i branches"
(length (cl-mapcan (lambda (s) (copy-sequence (cdr s)))
stale)))
nil
(mapcar (pcase-lambda (`(,refspec . ,refs))
(concat refspec "\n"
(mapconcat (lambda (b) (concat " " b))
refs "\n")))
stale)))
(pcase-dolist (`(,refspec . ,refs) stale)
(magit-call-git "config" "--unset" variable
(regexp-quote refspec))
(magit--log-action
(lambda (refs)
(format "Deleting %i branches" (length refs)))
(lambda (ref)
(format "Deleting branch %s (was %s)" ref
(magit-rev-parse "--short" ref)))
refs)
(dolist (ref refs)
(magit-call-git "update-ref" "-d" ref)))
(user-error "Abort")))
(magit-refresh))))
;;;###autoload
(defun magit-remote-set-head (remote &optional branch)
"Set the local representation of REMOTE's default branch.
Query REMOTE and set the symbolic-ref refs/remotes/<remote>/HEAD
accordingly. With a prefix argument query for the branch to be
used, which allows you to select an incorrect value if you fancy
doing that."
(interactive
(let ((remote (magit-read-remote "Set HEAD for remote")))
(list remote
(and current-prefix-arg
(magit-read-remote-branch (format "Set %s/HEAD to" remote)
remote nil nil t)))))
(magit-run-git "remote" "set-head" remote (or branch "--auto")))
;;;###autoload
(defun magit-remote-unset-head (remote)
"Unset the local representation of REMOTE's default branch.
Delete the symbolic-ref \"refs/remotes/<remote>/HEAD\"."
(interactive (list (magit-read-remote "Unset HEAD for remote")))
(magit-run-git "remote" "set-head" remote "--delete"))
;;; Configure
(defvar magit-remote-config--remote nil)
;;;###autoload
(defun magit-remote-config-popup (remote)
"Popup console for setting remote variables."
(interactive
(list (if (or current-prefix-arg
(and (eq magit-current-popup 'magit-remote-popup)
magit-remote-popup-show-variables))
(magit-read-remote "Configure remote")
(magit-remote-config--remote-1))))
(let ((magit-remote-config--remote remote))
(magit-invoke-popup 'magit-remote-config-popup nil nil)))
(defvar magit-remote-config-variables
'((lambda ()
(concat
(propertize "Configure " 'face 'magit-popup-heading)
(propertize (magit-remote-config--remote) 'face 'magit-branch-remote)))
(?u "remote.%s.url"
magit-set-remote*url
magit-format-remote*url)
(?U "remote.%s.fetch"
magit-set-remote*fetch
magit-format-remote*fetch)
(?s "remote.%s.pushurl"
magit-set-remote*pushurl
magit-format-remote*pushurl)
(?S "remote.%s.push"
magit-set-remote*push
magit-format-remote*push)
(?O "remote.%s.tagOpt"
magit-cycle-remote*tagOpt
magit-format-remote*tagOpt)))
(defvar magit-remote-config-popup
`(:man-page "git-remote"
:variables ,magit-remote-config-variables
:setup-function magit-remote-config-popup-setup))
(defun magit-remote-config-popup-setup (val def)
(magit-popup-default-setup val def)
(setq-local magit-remote-config--remote magit-remote-config--remote))
(defun magit-remote-config--remote (&optional prompt)
(if prompt
(or (and (not current-prefix-arg)
(or magit-remote-config--remote
(magit-remote-config--remote-1)))
(magit-read-remote prompt))
(or magit-remote-config--remote
(magit-remote-config--remote-1)
"<name>")))
(defun magit-remote-config--remote-1 ()
(let ((remote (magit-get-upstream-remote)))
(if (or (not remote)
(equal remote "."))
(and (magit-remote-p "origin") "origin")
remote)))
(defun magit-set-remote*url (remote urls)
"Set the variable `url' for the remote named REMOTE to URLS."
(interactive (magit-remote-config--read-args "url" "Urls: "))
(magit-remote-config--set-url remote "url" urls))
(defun magit-set-remote*fetch (remote values)
"Set the variable `fetch' for the remote named REMOTE to VALUES."
(interactive (magit-remote-config--read-args "fetch" "Fetch specs: "))
(magit-set-all values "remote" remote "fetch")
(magit-refresh))
(defun magit-set-remote*pushurl (remote urls)
"Set the variable `pushurl' for the remote named REMOTE to URLS."
(interactive (magit-remote-config--read-args "pushurl" "Urls: "))
(magit-remote-config--set-url remote "pushurl" urls "--push"))
(defun magit-set-remote*push (remote values)
"Set the variable `push' for the remote named REMOTE to VALUES."
(interactive (magit-remote-config--read-args "push" "Push specs: "))
(magit-set-all values "remote" remote "push")
(magit-refresh))
(defun magit-cycle-remote*tagOpt (remote)
(interactive (list (magit-remote-config--remote)))
(magit--set-popup-variable (format "remote.%s.tagOpt" remote)
'("--no-tags" "--tags") nil))
(defun magit-format-remote*url ()
(magit-remote-config--format-variable "url"))
(defun magit-format-remote*fetch ()
(magit-remote-config--format-variable "fetch"))
(defun magit-format-remote*pushurl ()
(magit-remote-config--format-variable "pushurl"))
(defun magit-format-remote*push ()
(magit-remote-config--format-variable "push"))
(defun magit-format-remote*tagOpt ()
(let ((remote (magit-remote-config--remote)))
(magit--format-popup-variable:choices
(format "remote.%s.tagOpts" remote)
'("--no-tags" "--tags") nil nil
(+ (length remote) 16))))
(defun magit-remote-config--read-args (var prompt)
(let* ((remote (magit-remote-config--remote (format "Set `%s' of remote" var)))
(value (magit-get-all "remote" remote var)))
(list remote
(mapcar (lambda (url)
(if (string-prefix-p "~" url)
(expand-file-name url)
url))
(completing-read-multiple
prompt nil nil nil
(and value (mapconcat #'identity value ",")))))))
(defun magit-remote-config--set-url (remote var values &optional arg)
(let ((old (magit-get-all "remote" remote var)))
(dolist (v (-difference values old))
(magit-call-git "remote" "set-url" arg "--add" remote v))
(dolist (v (-difference old values))
(magit-call-git "remote" "set-url" arg "--delete" remote
(concat "^" (regexp-quote v) "$"))))
(magit-refresh))
(defun magit-remote-config--format-variable (variable)
(magit--format-popup-variable:values
(format "remote.%s.%s" (magit-remote-config--remote) variable)
25))
;;; _
(provide 'magit-remote)
;;; magit-remote.el ends here