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 |