mirror of https://github.com/Chizi123/.emacs.d.git

Chizi123
2018-11-18 76bbd07de7add0f9d13c6914f158d19630fe2f62
commit | author | age
5cb5f7 1 ;;; magit-remote.el --- transfer Git commits  -*- lexical-binding: t -*-
C 2
3 ;; Copyright (C) 2008-2018  The Magit Project Contributors
4 ;;
5 ;; You should have received a copy of the AUTHORS.md file which
6 ;; lists all contributors.  If not, see http://magit.vc/authors.
7
8 ;; Author: Jonas Bernoulli <jonas@bernoul.li>
9 ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>
10
11 ;; Magit is free software; you can redistribute it and/or modify it
12 ;; under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 3, or (at your option)
14 ;; any later version.
15 ;;
16 ;; Magit is distributed in the hope that it will be useful, but WITHOUT
17 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 ;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
19 ;; License for more details.
20 ;;
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with Magit.  If not, see http://www.gnu.org/licenses.
23
24 ;;; Commentary:
25
26 ;; This library implements remote commands.
27
28 ;;; Code:
29
30 (require 'magit)
31
32 ;;; Options
33
34 (defcustom magit-remote-add-set-remote.pushDefault 'ask-if-unset
35   "Whether to set the value of `remote.pushDefault' after adding a remote.
36
37 If `ask', then always ask.  If `ask-if-unset', then ask, but only
38 if the variable isn't set already.  If nil, then don't ever set.
39 If the value is a string, then set without asking, provided that
40 the name of the added remote is equal to that string and the
41 variable isn't already set."
42   :package-version '(magit . "2.4.0")
43   :group 'magit-commands
44   :type '(choice (const  :tag "ask if unset" ask-if-unset)
45                  (const  :tag "always ask" ask)
46                  (string :tag "set if named")
47                  (const  :tag "don't set")))
48
49 (defcustom magit-remote-popup-show-variables t
50   "Whether the `magit-remote-popup' shows Git variables.
51 When set to nil, no variables are displayed directly in this
52 popup, instead the sub-popup `magit-remote-config-popup' has
53 to be used to view and change remote related variables."
54   :package-version '(magit . "2.12.0")
55   :group 'magit-commands
56   :type 'boolean)
57
58 ;;; Commands
59
60 (defvar magit-remote-config-variables)
61
62 ;;;###autoload (autoload 'magit-remote-popup "magit-remote" nil t)
63 (magit-define-popup magit-remote-popup
64   "Popup console for remote commands."
65   :man-page "git-remote"
66   :default-arguments '("-f")
67   :variables (lambda ()
68                (and magit-remote-popup-show-variables
69                     magit-remote-config-variables))
70   :switches '("Switches for add"
71               (?f "Fetch after add" "-f"))
72   :actions  '((?a "Add"                  magit-remote-add)
73               (?C "Configure..."         magit-remote-config-popup)
74               (?r "Rename"               magit-remote-rename)
75               (?p "Prune stale branches" magit-remote-prune)
76               (?k "Remove"               magit-remote-remove)
77               (?P "Prune stale refspecs" magit-remote-prune-refspecs))
78   :max-action-columns 2)
79
80 (defun magit-read-url (prompt &optional initial-input)
81   (let ((url (magit-read-string-ns prompt initial-input)))
82     (if (string-prefix-p "~" url)
83         (expand-file-name url)
84       url)))
85
86 ;;;###autoload
87 (defun magit-remote-add (remote url &optional args)
88   "Add a remote named REMOTE and fetch it."
89   (interactive (list (magit-read-string-ns "Remote name")
90                      (magit-read-url "Remote url")
91                      (magit-remote-arguments)))
92   (if (pcase (list magit-remote-add-set-remote.pushDefault
93                    (magit-get "remote.pushDefault"))
94         (`(,(pred stringp) ,_) t)
95         ((or `(ask ,_) `(ask-if-unset nil))
96          (y-or-n-p (format "Set `remote.pushDefault' to \"%s\"? " remote))))
97       (progn (magit-call-git "remote" "add" args remote url)
98              (setf (magit-get "remote.pushDefault") remote)
99              (magit-refresh))
100     (magit-run-git-async "remote" "add" args remote url)))
101
102 ;;;###autoload
103 (defun magit-remote-rename (old new)
104   "Rename the remote named OLD to NEW."
105   (interactive
106    (let  ((remote (magit-read-remote "Rename remote")))
107      (list remote (magit-read-string-ns (format "Rename %s to" remote)))))
108   (unless (string= old new)
109     (magit-call-git "remote" "rename" old new)
110     (magit-remote--cleanup-push-variables old new)
111     (magit-refresh)))
112
113 ;;;###autoload
114 (defun magit-remote-remove (remote)
115   "Delete the remote named REMOTE."
116   (interactive (list (magit-read-remote "Delete remote")))
117   (magit-call-git "remote" "rm" remote)
118   (magit-remote--cleanup-push-variables remote)
119   (magit-refresh))
120
121 (defun magit-remote--cleanup-push-variables (remote &optional new-name)
122   (magit-with-toplevel
123     (when (equal (magit-get "remote.pushDefault") remote)
124       (magit-set new-name "remote.pushDefault"))
125     (dolist (var (magit-git-lines "config" "--name-only"
126                                   "--get-regexp" "^branch\.[^.]*\.pushRemote"
127                                   (format "^%s$" remote)))
128       (magit-call-git "config" (and (not new-name) "--unset") var new-name))))
129
130 (defconst magit--refspec-re "\\`\\(\\+\\)?\\([^:]+\\):\\(.*\\)\\'")
131
132 ;;;###autoload
133 (defun magit-remote-prune (remote)
134   "Remove stale remote-tracking branches for REMOTE."
135   (interactive (list (magit-read-remote "Prune stale branches of remote")))
136   (magit-run-git-async "remote" "prune" remote))
137
138 ;;;###autoload
139 (defun magit-remote-prune-refspecs (remote)
140   "Remove stale refspecs for REMOTE.
141
142 A refspec is stale if there no longer exists at least one branch
143 on the remote that would be fetched due to that refspec.  A stale
144 refspec is problematic because its existence causes Git to refuse
145 to fetch according to the remaining non-stale refspecs.
146
147 If only stale refspecs remain, then offer to either delete the
148 remote or to replace the stale refspecs with the default refspec.
149
150 Also remove the remote-tracking branches that were created due to
151 the now stale refspecs.  Other stale branches are not removed."
152   (interactive (list (magit-read-remote "Prune refspecs of remote")))
153   (let* ((tracking-refs (magit-list-remote-branches remote))
154          (remote-refs (magit-remote-list-refs remote))
155          (variable (format "remote.%s.fetch" remote))
156          (refspecs (magit-get-all variable))
157          stale)
158     (dolist (refspec refspecs)
159       (when (string-match magit--refspec-re refspec)
160         (let ((theirs (match-string 2 refspec))
161               (ours   (match-string 3 refspec)))
162           (unless (if (string-match "\\*" theirs)
163                       (let ((re (replace-match ".*" t t theirs)))
164                         (--some (string-match-p re it) remote-refs))
165                     (member theirs remote-refs))
166             (push (cons refspec
167                         (if (string-match "\\*" ours)
168                             (let ((re (replace-match ".*" t t ours)))
169                               (--filter (string-match-p re it) tracking-refs))
170                           (list (car (member ours tracking-refs)))))
171                   stale)))))
172     (if (not stale)
173         (message "No stale refspecs for remote %S" remote)
174       (if (= (length stale)
175              (length refspecs))
176           (magit-read-char-case
177               (format "All of %s's refspecs are stale.  " remote) nil
178             (?s "replace with [d]efault refspec"
179                 (magit-set-all
180                  (list (format "+refs/heads/*:refs/remotes/%s/*" remote))
181                  variable))
182             (?r "[r]emove remote"
183                 (magit-call-git "remote" "rm" remote))
184             (?a "or [a]abort"
185                 (user-error "Abort")))
186         (if (if (= (length stale) 1)
187                 (pcase-let ((`(,refspec . ,refs) (car stale)))
188                   (magit-confirm 'prune-stale-refspecs
189                     (format "Prune stale refspec %s and branch %%s" refspec)
190                     (format "Prune stale refspec %s and %%i branches" refspec)
191                     nil refs))
192               (magit-confirm 'prune-stale-refspecs nil
193                 (format "Prune %%i stale refspecs and %i branches"
194                         (length (cl-mapcan (lambda (s) (copy-sequence (cdr s)))
195                                            stale)))
196                 nil
197                 (mapcar (pcase-lambda (`(,refspec . ,refs))
198                           (concat refspec "\n"
199                                   (mapconcat (lambda (b) (concat "  " b))
200                                              refs "\n")))
201                         stale)))
202             (pcase-dolist (`(,refspec . ,refs) stale)
203               (magit-call-git "config" "--unset" variable
204                               (regexp-quote refspec))
205               (magit--log-action
206                (lambda (refs)
207                  (format "Deleting %i branches" (length refs)))
208                (lambda (ref)
209                  (format "Deleting branch %s (was %s)" ref
210                          (magit-rev-parse "--short" ref)))
211                refs)
212               (dolist (ref refs)
213                 (magit-call-git "update-ref" "-d" ref)))
214           (user-error "Abort")))
215       (magit-refresh))))
216
217 ;;;###autoload
218 (defun magit-remote-set-head (remote &optional branch)
219   "Set the local representation of REMOTE's default branch.
220 Query REMOTE and set the symbolic-ref refs/remotes/<remote>/HEAD
221 accordingly.  With a prefix argument query for the branch to be
222 used, which allows you to select an incorrect value if you fancy
223 doing that."
224   (interactive
225    (let  ((remote (magit-read-remote "Set HEAD for remote")))
226      (list remote
227            (and current-prefix-arg
228                 (magit-read-remote-branch (format "Set %s/HEAD to" remote)
229                                           remote nil nil t)))))
230   (magit-run-git "remote" "set-head" remote (or branch "--auto")))
231
232 ;;;###autoload
233 (defun magit-remote-unset-head (remote)
234   "Unset the local representation of REMOTE's default branch.
235 Delete the symbolic-ref \"refs/remotes/<remote>/HEAD\"."
236   (interactive (list (magit-read-remote "Unset HEAD for remote")))
237   (magit-run-git "remote" "set-head" remote "--delete"))
238
239 ;;; Configure
240
241 (defvar magit-remote-config--remote nil)
242
243 ;;;###autoload
244 (defun magit-remote-config-popup (remote)
245   "Popup console for setting remote variables."
246   (interactive
247    (list (if (or current-prefix-arg
248                  (and (eq magit-current-popup 'magit-remote-popup)
249                       magit-remote-popup-show-variables))
250              (magit-read-remote "Configure remote")
251            (magit-remote-config--remote-1))))
252   (let ((magit-remote-config--remote remote))
253     (magit-invoke-popup 'magit-remote-config-popup nil nil)))
254
255 (defvar magit-remote-config-variables
256   '((lambda ()
257       (concat
258        (propertize "Configure " 'face 'magit-popup-heading)
259        (propertize (magit-remote-config--remote) 'face 'magit-branch-remote)))
260     (?u "remote.%s.url"
261         magit-set-remote*url
262         magit-format-remote*url)
263     (?U "remote.%s.fetch"
264         magit-set-remote*fetch
265         magit-format-remote*fetch)
266     (?s "remote.%s.pushurl"
267         magit-set-remote*pushurl
268         magit-format-remote*pushurl)
269     (?S "remote.%s.push"
270         magit-set-remote*push
271         magit-format-remote*push)
272     (?O "remote.%s.tagOpt"
273         magit-cycle-remote*tagOpt
274         magit-format-remote*tagOpt)))
275
276 (defvar magit-remote-config-popup
277   `(:man-page "git-remote"
278     :variables ,magit-remote-config-variables
279     :setup-function magit-remote-config-popup-setup))
280
281 (defun magit-remote-config-popup-setup (val def)
282   (magit-popup-default-setup val def)
283   (setq-local magit-remote-config--remote magit-remote-config--remote))
284
285 (defun magit-remote-config--remote (&optional prompt)
286   (if prompt
287       (or (and (not current-prefix-arg)
288                (or magit-remote-config--remote
289                    (magit-remote-config--remote-1)))
290           (magit-read-remote prompt))
291     (or magit-remote-config--remote
292         (magit-remote-config--remote-1)
293         "<name>")))
294
295 (defun magit-remote-config--remote-1 ()
296   (let ((remote (magit-get-upstream-remote)))
297     (if (or (not remote)
298             (equal remote "."))
299         (and (magit-remote-p "origin") "origin")
300       remote)))
301
302 (defun magit-set-remote*url (remote urls)
303   "Set the variable `url' for the remote named REMOTE to URLS."
304   (interactive (magit-remote-config--read-args "url" "Urls: "))
305   (magit-remote-config--set-url remote "url" urls))
306
307 (defun magit-set-remote*fetch (remote values)
308   "Set the variable `fetch' for the remote named REMOTE to VALUES."
309   (interactive (magit-remote-config--read-args "fetch" "Fetch specs: "))
310   (magit-set-all values "remote" remote "fetch")
311   (magit-refresh))
312
313 (defun magit-set-remote*pushurl (remote urls)
314   "Set the variable `pushurl' for the remote named REMOTE to URLS."
315   (interactive (magit-remote-config--read-args "pushurl" "Urls: "))
316   (magit-remote-config--set-url remote "pushurl" urls "--push"))
317
318 (defun magit-set-remote*push (remote values)
319   "Set the variable `push' for the remote named REMOTE to VALUES."
320   (interactive (magit-remote-config--read-args "push" "Push specs: "))
321   (magit-set-all values "remote" remote "push")
322   (magit-refresh))
323
324 (defun magit-cycle-remote*tagOpt (remote)
325   (interactive (list (magit-remote-config--remote)))
326   (magit--set-popup-variable (format "remote.%s.tagOpt" remote)
327                              '("--no-tags" "--tags") nil))
328
329 (defun magit-format-remote*url ()
330   (magit-remote-config--format-variable "url"))
331
332 (defun magit-format-remote*fetch ()
333   (magit-remote-config--format-variable "fetch"))
334
335 (defun magit-format-remote*pushurl ()
336   (magit-remote-config--format-variable "pushurl"))
337
338 (defun magit-format-remote*push ()
339   (magit-remote-config--format-variable "push"))
340
341 (defun magit-format-remote*tagOpt ()
342   (let ((remote (magit-remote-config--remote)))
343     (magit--format-popup-variable:choices
344      (format "remote.%s.tagOpts" remote)
345      '("--no-tags" "--tags") nil nil
346      (+ (length remote) 16))))
347
348 (defun magit-remote-config--read-args (var prompt)
349   (let* ((remote (magit-remote-config--remote (format "Set `%s' of remote" var)))
350          (value (magit-get-all "remote" remote var)))
351     (list remote
352           (mapcar (lambda (url)
353                     (if (string-prefix-p "~" url)
354                         (expand-file-name url)
355                       url))
356                   (completing-read-multiple
357                    prompt nil nil nil
358                    (and value (mapconcat #'identity value ",")))))))
359
360 (defun magit-remote-config--set-url (remote var values &optional arg)
361   (let ((old (magit-get-all "remote" remote var)))
362     (dolist (v (-difference values old))
363       (magit-call-git "remote" "set-url" arg "--add" remote v))
364     (dolist (v (-difference old values))
365       (magit-call-git "remote" "set-url" arg "--delete" remote
366                       (concat "^" (regexp-quote v) "$"))))
367   (magit-refresh))
368
369 (defun magit-remote-config--format-variable (variable)
370   (magit--format-popup-variable:values
371    (format "remote.%s.%s" (magit-remote-config--remote) variable)
372    25))
373
374 ;;; _
375 (provide 'magit-remote)
376 ;;; magit-remote.el ends here