commit | author | age
|
5cb5f7
|
1 |
;;; magit-branch.el --- branch support -*- lexical-binding: t -*- |
C |
2 |
|
|
3 |
;; Copyright (C) 2010-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 support for branches. It defines popups |
|
27 |
;; and commands for creating, checking out, manipulating, and |
|
28 |
;; configuring branches. Commands defined here are mainly concerned |
|
29 |
;; with branches as pointers, commands that deal with what a branch |
|
30 |
;; points at, are defined elsewhere. |
|
31 |
|
|
32 |
;;; Code: |
|
33 |
|
|
34 |
(eval-when-compile |
|
35 |
(require 'subr-x)) |
|
36 |
|
|
37 |
(require 'magit) |
|
38 |
(require 'magit-collab) |
|
39 |
(require 'magit-reset) |
|
40 |
|
|
41 |
;;; Options |
|
42 |
|
|
43 |
(defcustom magit-branch-read-upstream-first t |
|
44 |
"Whether to read upstream before name of new branch when creating a branch. |
|
45 |
|
|
46 |
`nil' Read the branch name first. |
|
47 |
`t' Read the upstream first. |
|
48 |
`fallback' Read the upstream first, but if it turns out that the chosen |
|
49 |
value is not a valid upstream (because it cannot be resolved |
|
50 |
as an existing revision), then treat it as the name of the |
|
51 |
new branch and continue by reading the upstream next." |
|
52 |
:package-version '(magit . "2.2.0") |
|
53 |
:group 'magit-commands |
|
54 |
:type '(choice (const :tag "read branch name first" nil) |
|
55 |
(const :tag "read upstream first" t) |
|
56 |
(const :tag "read upstream first, with fallback" fallback))) |
|
57 |
|
|
58 |
(defcustom magit-branch-prefer-remote-upstream nil |
|
59 |
"Whether to favor remote upstreams when creating new branches. |
|
60 |
|
|
61 |
When a new branch is created, then the branch, commit, or stash |
|
62 |
at point is suggested as the default starting point of the new |
|
63 |
branch, or if there is no such revision at point the current |
|
64 |
branch. In either case the user may choose another starting |
|
65 |
point. |
|
66 |
|
|
67 |
If the chosen starting point is a branch, then it may also be set |
|
68 |
as the upstream of the new branch, depending on the value of the |
|
69 |
Git variable `branch.autoSetupMerge'. By default this is done |
|
70 |
for remote branches, but not for local branches. |
|
71 |
|
|
72 |
You might prefer to always use some remote branch as upstream. |
|
73 |
If the chosen starting point is (1) a local branch, (2) whose |
|
74 |
name matches a member of the value of this option, (3) the |
|
75 |
upstream of that local branch is a remote branch with the same |
|
76 |
name, and (4) that remote branch can be fast-forwarded to the |
|
77 |
local branch, then the chosen branch is used as starting point, |
|
78 |
but its own upstream is used as the upstream of the new branch. |
|
79 |
|
|
80 |
Members of this option's value are treated as branch names that |
|
81 |
have to match exactly unless they contain a character that makes |
|
82 |
them invalid as a branch name. Recommended characters to use |
|
83 |
to trigger interpretation as a regexp are \"*\" and \"^\". Some |
|
84 |
other characters which you might expect to be invalid, actually |
|
85 |
are not, e.g. \".+$\" are all perfectly valid. More precisely, |
|
86 |
if `git check-ref-format --branch STRING' exits with a non-zero |
|
87 |
status, then treat STRING as a regexp. |
|
88 |
|
|
89 |
Assuming the chosen branch matches these conditions you would end |
|
90 |
up with with e.g.: |
|
91 |
|
|
92 |
feature --upstream--> origin/master |
|
93 |
|
|
94 |
instead of |
|
95 |
|
|
96 |
feature --upstream--> master --upstream--> origin/master |
|
97 |
|
|
98 |
Which you prefer is a matter of personal preference. If you do |
|
99 |
prefer the former, then you should add branches such as \"master\", |
|
100 |
\"next\", and \"maint\" to the value of this options." |
|
101 |
:package-version '(magit . "2.4.0") |
|
102 |
:group 'magit-commands |
|
103 |
:type '(repeat string)) |
|
104 |
|
|
105 |
(defcustom magit-branch-adjust-remote-upstream-alist nil |
|
106 |
"Alist of upstreams to be used when branching from remote branches. |
|
107 |
|
|
108 |
When creating a local branch from an ephemeral branch located |
|
109 |
on a remote, e.g. a feature or hotfix branch, then that remote |
|
110 |
branch should usually not be used as the upstream branch, since |
|
111 |
the push-remote already allows accessing it and having both the |
|
112 |
upstream and the push-remote reference the same related branch |
|
113 |
would be wasteful. Instead a branch like \"maint\" or \"master\" |
|
114 |
should be used as the upstream. |
|
115 |
|
|
116 |
This option allows specifing the branch that should be used as |
|
117 |
the upstream when branching certain remote branches. The value |
|
118 |
is an alist of the form ((UPSTREAM . RULE)...). The first |
|
119 |
matching element is used, the following elements are ignored. |
|
120 |
|
|
121 |
UPSTREAM is the branch to be used as the upstream for branches |
|
122 |
specified by RULE. It can be a local or a remote branch. |
|
123 |
|
|
124 |
RULE can either be a regular expression, matching branches whose |
|
125 |
upstream should be the one specified by UPSTREAM. Or it can be |
|
126 |
a list of the only branches that should *not* use UPSTREAM; all |
|
127 |
other branches will. Matching is done after stripping the remote |
|
128 |
part of the name of the branch that is being branched from. |
|
129 |
|
|
130 |
If you use a finite set of non-ephemeral branches across all your |
|
131 |
repositories, then you might use something like: |
|
132 |
|
|
133 |
((\"origin/master\" \"master\" \"next\" \"maint\")) |
|
134 |
|
|
135 |
Or if the names of all your ephemeral branches contain a slash, |
|
136 |
at least in some repositories, then a good value could be: |
|
137 |
|
|
138 |
((\"origin/master\" . \"/\")) |
|
139 |
|
|
140 |
Of course you can also fine-tune: |
|
141 |
|
|
142 |
((\"origin/maint\" . \"\\\\\\=`hotfix/\") |
|
143 |
(\"origin/master\" . \"\\\\\\=`feature/\")) |
|
144 |
|
|
145 |
If you use remote branches as UPSTREAM, then you might also want |
|
146 |
to set `magit-branch-prefer-remote-upstream' to a non-nil value. |
|
147 |
However, I recommend that you use local branches as UPSTREAM." |
|
148 |
:package-version '(magit . "2.9.0") |
|
149 |
:group 'magit-commands |
|
150 |
:type '(repeat (cons (string :tag "Use upstream") |
|
151 |
(choice :tag "for branches" |
|
152 |
(regexp :tag "matching") |
|
153 |
(repeat :tag "except" |
|
154 |
(string :tag "branch")))))) |
|
155 |
|
|
156 |
(defcustom magit-branch-rename-push-target t |
|
157 |
"Whether the push-remote setup is preserved when renaming a branch. |
|
158 |
|
|
159 |
The command `magit-branch-rename' renames a branch named OLD to |
|
160 |
NEW. This option controls how much of the push-remote setup is |
|
161 |
preserved when doing so. |
|
162 |
|
|
163 |
When nil, then preserve nothing and unset `branch.OLD.pushRemote'. |
|
164 |
|
|
165 |
When `local-only', then first set `branch.NEW.pushRemote' to the |
|
166 |
same value as `branch.OLD.pushRemote', provided the latter is |
|
167 |
actually set and unless the former already has another value. |
|
168 |
|
|
169 |
When t, then rename the branch named OLD on the remote specified |
|
170 |
by `branch.OLD.pushRemote' to NEW, provided OLD exists on that |
|
171 |
remote and unless NEW already exists on the remote. |
|
172 |
|
|
173 |
When `forge-only' and the `forge' package is available, then |
|
174 |
behave like `t' if the remote points to a repository on a forge |
|
175 |
(currently Github or Gitlab), otherwise like `local-only'. |
|
176 |
|
|
177 |
Another supported but obsolete value is `github-only'. It is a |
|
178 |
misnomer because it now treated as an alias for `forge-only'." |
|
179 |
:package-version '(magit . "2.90.0") |
|
180 |
:group 'magit-commands |
|
181 |
:type '(choice |
|
182 |
(const :tag "Don't preserve push-remote setup" nil) |
|
183 |
(const :tag "Preserve push-remote setup" local-only) |
|
184 |
(const :tag "... and rename corresponding branch on remote" t) |
|
185 |
(const :tag "... but only if remote is on a forge" forge-only))) |
|
186 |
|
|
187 |
(defcustom magit-branch-popup-show-variables t |
|
188 |
"Whether the `magit-branch-popup' shows Git variables. |
|
189 |
This defaults to t to avoid changing key bindings. When set to |
|
190 |
nil, no variables are displayed directly in this popup, instead |
|
191 |
the sub-popup `magit-branch-config-popup' has to be used to view |
|
192 |
and change branch related variables." |
|
193 |
:package-version '(magit . "2.7.0") |
|
194 |
:group 'magit-commands |
|
195 |
:type 'boolean) |
|
196 |
|
|
197 |
(defcustom magit-published-branches '("origin/master") |
|
198 |
"List of branches that are considered to be published." |
|
199 |
:package-version '(magit . "2.13.0") |
|
200 |
:group 'magit-commands |
|
201 |
:type '(repeat string)) |
|
202 |
|
|
203 |
;;; Branch Popup |
|
204 |
|
|
205 |
(defvar magit-branch-config-variables) |
|
206 |
|
|
207 |
;;;###autoload (autoload 'magit-branch-popup "magit" nil t) |
|
208 |
(magit-define-popup magit-branch-popup |
|
209 |
"Popup console for branch commands." |
|
210 |
:man-page "git-branch" |
|
211 |
:variables (lambda () |
|
212 |
(and magit-branch-popup-show-variables |
|
213 |
magit-branch-config-variables)) |
|
214 |
:actions `((?b "Checkout" magit-checkout) nil |
|
215 |
(?C "Configure..." magit-branch-config-popup) |
|
216 |
(?l "Checkout local branch" magit-branch-checkout) |
|
217 |
(?s "Create new spin-off" magit-branch-spinoff) |
|
218 |
(?m "Rename" magit-branch-rename) |
|
219 |
(?c "Checkout new branch" magit-branch-and-checkout) |
|
220 |
(?n "Create new branch" magit-branch-create) |
|
221 |
(?x "Reset" magit-branch-reset) |
|
222 |
(?w "Checkout new worktree" magit-worktree-checkout) |
|
223 |
(?W "Create new worktree" magit-worktree-branch) |
|
224 |
(?k "Delete" magit-branch-delete) |
|
225 |
,@(and (not (require (quote forge) nil t)) |
|
226 |
'((?y "Checkout pull-request" magit-checkout-pull-request) |
|
227 |
(?Y "Create from pull-request" magit-branch-pull-request)))) |
|
228 |
:default-action 'magit-checkout |
|
229 |
:max-action-columns 3 |
|
230 |
:setup-function 'magit-branch-popup-setup) |
|
231 |
|
|
232 |
(defun magit-branch-popup-setup (val def) |
|
233 |
(magit-popup-default-setup val def) |
|
234 |
(use-local-map (copy-keymap magit-popup-mode-map)) |
|
235 |
(dolist (ev (-filter #'magit-popup-event-p (magit-popup-get :variables))) |
|
236 |
(local-set-key (vector (magit-popup-event-key ev)) |
|
237 |
'magit-invoke-popup-action))) |
|
238 |
|
|
239 |
;;; Branch Commands |
|
240 |
|
|
241 |
;;;###autoload |
|
242 |
(defun magit-checkout (revision) |
|
243 |
"Checkout REVISION, updating the index and the working tree. |
|
244 |
If REVISION is a local branch, then that becomes the current |
|
245 |
branch. If it is something else, then `HEAD' becomes detached. |
|
246 |
Checkout fails if the working tree or the staging area contain |
|
247 |
changes. |
|
248 |
\n(git checkout REVISION)." |
|
249 |
(interactive (list (magit-read-other-branch-or-commit "Checkout"))) |
|
250 |
(when (string-match "\\`heads/\\(.+\\)" revision) |
|
251 |
(setq revision (match-string 1 revision))) |
|
252 |
(magit-run-git "checkout" revision)) |
|
253 |
|
|
254 |
;;;###autoload |
|
255 |
(defun magit-branch-create (branch start-point &optional args) |
|
256 |
"Create BRANCH at branch or revision START-POINT. |
|
257 |
\n(git branch [ARGS] BRANCH START-POINT)." |
|
258 |
(interactive (magit-branch-read-args "Create branch")) |
|
259 |
(magit-call-git "branch" args branch start-point) |
|
260 |
(magit-branch-maybe-adjust-upstream branch start-point) |
|
261 |
(magit-refresh)) |
|
262 |
|
|
263 |
;;;###autoload |
|
264 |
(defun magit-branch-and-checkout (branch start-point &optional args) |
|
265 |
"Create and checkout BRANCH at branch or revision START-POINT. |
|
266 |
\n(git checkout [ARGS] -b BRANCH START-POINT)." |
|
267 |
(interactive (magit-branch-read-args "Create and checkout branch")) |
|
268 |
(if (string-match-p "^stash@{[0-9]+}$" start-point) |
|
269 |
(magit-run-git "stash" "branch" branch start-point) |
|
270 |
(magit-call-git "checkout" args "-b" branch start-point) |
|
271 |
(magit-branch-maybe-adjust-upstream branch start-point) |
|
272 |
(magit-refresh))) |
|
273 |
|
|
274 |
;;;###autoload |
|
275 |
(defun magit-branch-or-checkout (arg &optional start-point) |
|
276 |
"Hybrid between `magit-checkout' and `magit-branch-and-checkout'. |
|
277 |
|
|
278 |
Ask the user for an existing branch or revision. If the user |
|
279 |
input actually can be resolved as a branch or revision, then |
|
280 |
check that out, just like `magit-checkout' would. |
|
281 |
|
|
282 |
Otherwise create and checkout a new branch using the input as |
|
283 |
its name. Before doing so read the starting-point for the new |
|
284 |
branch. This is similar to what `magit-branch-and-checkout' |
|
285 |
does." |
|
286 |
(interactive |
|
287 |
(let ((arg (magit-read-other-branch-or-commit "Checkout"))) |
|
288 |
(list arg |
|
289 |
(and (not (magit-rev-verify-commit arg)) |
|
290 |
(magit-read-starting-point "Create and checkout branch" arg))))) |
|
291 |
(when (string-match "\\`heads/\\(.+\\)" arg) |
|
292 |
(setq arg (match-string 1 arg))) |
|
293 |
(if start-point |
|
294 |
(magit-branch-and-checkout arg start-point (magit-branch-arguments)) |
|
295 |
(magit-checkout arg))) |
|
296 |
|
|
297 |
;;;###autoload |
|
298 |
(defun magit-branch-checkout (branch &optional start-point) |
|
299 |
"Checkout an existing or new local branch. |
|
300 |
|
|
301 |
Read a branch name from the user offering all local branches and |
|
302 |
a subset of remote branches as candidates. Omit remote branches |
|
303 |
for which a local branch by the same name exists from the list |
|
304 |
of candidates. The user can also enter a completely new branch |
|
305 |
name. |
|
306 |
|
|
307 |
- If the user selects an existing local branch, then check that |
|
308 |
out. |
|
309 |
|
|
310 |
- If the user selects a remote branch, then create and checkout |
|
311 |
a new local branch with the same name. Configure the selected |
|
312 |
remote branch as push target. |
|
313 |
|
|
314 |
- If the user enters a new branch name, then create and check |
|
315 |
that out, after also reading the starting-point from the user. |
|
316 |
|
|
317 |
In the latter two cases the upstream is also set. Whether it is |
|
318 |
set to the chosen START-POINT or something else depends on the |
|
319 |
value of `magit-branch-adjust-remote-upstream-alist', just like |
|
320 |
when using `magit-branch-and-checkout'." |
|
321 |
(interactive |
|
322 |
(let* ((current (magit-get-current-branch)) |
|
323 |
(local (magit-list-local-branch-names)) |
|
324 |
(remote (--filter (and (string-match "[^/]+/" it) |
|
325 |
(not (member (substring it (match-end 0)) |
|
326 |
(cons "HEAD" local)))) |
|
327 |
(magit-list-remote-branch-names))) |
|
328 |
(choices (nconc (delete current local) remote)) |
|
329 |
(atpoint (magit-branch-at-point)) |
|
330 |
(choice (magit-completing-read |
|
331 |
"Checkout branch" choices |
|
332 |
nil nil nil 'magit-revision-history |
|
333 |
(or (car (member atpoint choices)) |
|
334 |
(and atpoint |
|
335 |
(car (member (and (string-match "[^/]+/" atpoint) |
|
336 |
(substring atpoint (match-end 0))) |
|
337 |
choices))))))) |
|
338 |
(cond ((member choice remote) |
|
339 |
(list (and (string-match "[^/]+/" choice) |
|
340 |
(substring choice (match-end 0))) |
|
341 |
choice)) |
|
342 |
((member choice local) |
|
343 |
(list choice)) |
|
344 |
(t |
|
345 |
(list choice (magit-read-starting-point "Create" choice)))))) |
|
346 |
(if (not start-point) |
|
347 |
(magit-checkout branch) |
|
348 |
(when (magit-anything-modified-p) |
|
349 |
(user-error "Cannot checkout when there are uncommitted changes")) |
|
350 |
(magit-branch-and-checkout branch start-point (magit-branch-arguments)) |
|
351 |
(when (magit-remote-branch-p start-point) |
|
352 |
(pcase-let ((`(,remote . ,remote-branch) |
|
353 |
(magit-split-branch-name start-point))) |
|
354 |
(when (and (equal branch remote-branch) |
|
355 |
(not (equal remote (magit-get "remote.pushDefault")))) |
|
356 |
(magit-set remote "branch" branch "pushRemote")))))) |
|
357 |
|
|
358 |
(defun magit-branch-maybe-adjust-upstream (branch start-point) |
|
359 |
(--when-let |
|
360 |
(or (and (magit-get-upstream-branch branch) |
|
361 |
(magit-get-indirect-upstream-branch start-point)) |
|
362 |
(and (magit-remote-branch-p start-point) |
|
363 |
(let ((name (cdr (magit-split-branch-name start-point)))) |
|
364 |
(car (--first (if (listp (cdr it)) |
|
365 |
(not (member name (cdr it))) |
|
366 |
(string-match-p (cdr it) name)) |
|
367 |
magit-branch-adjust-remote-upstream-alist))))) |
|
368 |
(magit-call-git "branch" (concat "--set-upstream-to=" it) branch))) |
|
369 |
|
|
370 |
;;;###autoload |
|
371 |
(defun magit-branch-orphan (branch start-point &optional args) |
|
372 |
"Create and checkout an orphan BRANCH with contents from revision START-POINT. |
|
373 |
\n(git checkout --orphan [ARGS] BRANCH START-POINT)." |
|
374 |
(interactive (magit-branch-read-args "Create and checkout orphan branch")) |
|
375 |
(magit-run-git "checkout" "--orphan" args branch start-point)) |
|
376 |
|
|
377 |
;;;###autoload |
|
378 |
(defun magit-branch-pull-request (pr) |
|
379 |
"Create and configure a new branch from a pull-request. |
|
380 |
Please see the manual for more information." |
|
381 |
(interactive (list (magit-read-pull-request "Branch pull request"))) |
|
382 |
(let-alist pr |
|
383 |
(let* ((upstream (or (--first (magit--github-url-equal |
|
384 |
(magit-get "remote" it "url") |
|
385 |
.base.repo.ssh_url) |
|
386 |
(magit-list-remotes)) |
|
387 |
(user-error |
|
388 |
"Upstream repository %s not available as a remote" |
|
389 |
.base.repo.ssh_url))) |
|
390 |
(upstream-url (magit-get "remote" upstream "url")) |
|
391 |
(remote .head.repo.owner.login) |
|
392 |
(branch (magit--pullreq-branch pr t)) |
|
393 |
(pr-branch .head.ref)) |
|
394 |
(if (magit--pullreq-from-upstream-p pr) |
|
395 |
(let ((tracking (concat upstream "/" pr-branch))) |
|
396 |
(unless (magit-branch-p tracking) |
|
397 |
(magit-call-git "fetch" upstream)) |
|
398 |
(let ((inhibit-magit-refresh t)) |
|
399 |
(magit-branch-create branch tracking)) |
|
400 |
(magit-set upstream "branch" branch "pushRemote") |
|
401 |
(magit-set upstream "branch" branch "pullRequestRemote")) |
|
402 |
(if (magit-remote-p remote) |
|
403 |
(let ((url (magit-get "remote" remote "url")) |
|
404 |
(fetch (magit-get-all "remote" remote "fetch"))) |
|
405 |
(unless (magit--github-url-equal url .head.repo.ssh_url) |
|
406 |
(user-error |
|
407 |
"Remote `%s' already exists but does not point to %s" |
|
408 |
remote url)) |
|
409 |
(unless (member (format "+refs/heads/*:refs/remotes/%s/*" remote) |
|
410 |
fetch) |
|
411 |
(magit-call-git "remote" "set-branches" |
|
412 |
"--add" remote pr-branch) |
|
413 |
(magit-call-git "fetch" remote))) |
|
414 |
(magit-call-git |
|
415 |
"remote" "add" "-f" "--no-tags" |
|
416 |
"-t" pr-branch remote |
|
417 |
(cond ((or (string-prefix-p "git@" upstream-url) |
|
418 |
(string-prefix-p "ssh://git@" upstream-url)) |
|
419 |
.head.repo.ssh_url) |
|
420 |
((string-prefix-p "https://" upstream-url) |
|
421 |
.head.repo.clone_url) |
|
422 |
((string-prefix-p "git://" upstream-url) |
|
423 |
.head.repo.git_url) |
|
424 |
(t (error "%s has an unexpected format" upstream-url))))) |
|
425 |
(magit-call-git "branch" branch (concat remote "/" pr-branch)) |
|
426 |
(if (or .locked (not (equal branch pr-branch))) |
|
427 |
(magit-set upstream "branch" branch "pushRemote") |
|
428 |
(magit-set remote "branch" branch "pushRemote")) |
|
429 |
(magit-set remote "branch" branch "pullRequestRemote")) |
|
430 |
(magit-set "true" "branch" branch "rebase") |
|
431 |
(magit-call-git "branch" branch |
|
432 |
(concat "--set-upstream-to=" |
|
433 |
(if magit-branch-prefer-remote-upstream |
|
434 |
(concat upstream "/" .base.ref) |
|
435 |
.base.ref))) |
|
436 |
(magit-set (number-to-string .number) "branch" branch "pullRequest") |
|
437 |
(magit-set .title "branch" branch "description") |
|
438 |
(magit-refresh) |
|
439 |
branch))) |
|
440 |
|
|
441 |
(defun magit-checkout-pull-request (pr) |
|
442 |
"Create, configure and checkout a new branch from a pull-request. |
|
443 |
Please see the manual for more information." |
|
444 |
(interactive (list (magit-read-pull-request "Checkout pull request"))) |
|
445 |
(magit-checkout |
|
446 |
(let ((inhibit-magit-refresh t)) |
|
447 |
(magit-branch-pull-request pr)))) |
|
448 |
|
|
449 |
(defun magit-branch-read-args (prompt) |
|
450 |
(let ((args (magit-branch-arguments))) |
|
451 |
(if magit-branch-read-upstream-first |
|
452 |
(let ((choice (magit-read-starting-point prompt))) |
|
453 |
(if (magit-rev-verify choice) |
|
454 |
(list (magit-read-string-ns |
|
455 |
(if magit-completing-read--silent-default |
|
456 |
(format "%s (starting at `%s')" prompt choice) |
|
457 |
"Name for new branch") |
|
458 |
(let ((def (mapconcat #'identity |
|
459 |
(cdr (split-string choice "/")) |
|
460 |
"/"))) |
|
461 |
(and (member choice (magit-list-remote-branch-names)) |
|
462 |
(not (member def (magit-list-local-branch-names))) |
|
463 |
def))) |
|
464 |
choice args) |
|
465 |
(if (eq magit-branch-read-upstream-first 'fallback) |
|
466 |
(list choice (magit-read-starting-point prompt choice) args) |
|
467 |
(user-error "Not a valid starting-point: %s" choice)))) |
|
468 |
(let ((branch (magit-read-string-ns (concat prompt " named")))) |
|
469 |
(list branch |
|
470 |
(magit-read-starting-point prompt branch) |
|
471 |
args))))) |
|
472 |
|
|
473 |
;;;###autoload |
|
474 |
(defun magit-branch-spinoff (branch &optional from &rest args) |
|
475 |
"Create new branch from the unpushed commits. |
|
476 |
|
|
477 |
Create and checkout a new branch starting at and tracking the |
|
478 |
current branch. That branch in turn is reset to the last commit |
|
479 |
it shares with its upstream. If the current branch has no |
|
480 |
upstream or no unpushed commits, then the new branch is created |
|
481 |
anyway and the previously current branch is not touched. |
|
482 |
|
|
483 |
This is useful to create a feature branch after work has already |
|
484 |
began on the old branch (likely but not necessarily \"master\"). |
|
485 |
|
|
486 |
If the current branch is a member of the value of option |
|
487 |
`magit-branch-prefer-remote-upstream' (which see), then the |
|
488 |
current branch will be used as the starting point as usual, but |
|
489 |
the upstream of the starting-point may be used as the upstream |
|
490 |
of the new branch, instead of the starting-point itself. |
|
491 |
|
|
492 |
If optional FROM is non-nil, then the source branch is reset |
|
493 |
to `FROM~', instead of to the last commit it shares with its |
|
494 |
upstream. Interactively, FROM is only ever non-nil, if the |
|
495 |
region selects some commits, and among those commits, FROM is |
|
496 |
the commit that is the fewest commits ahead of the source |
|
497 |
branch. |
|
498 |
|
|
499 |
The commit at the other end of the selection actually does not |
|
500 |
matter, all commits between FROM and `HEAD' are moved to the new |
|
501 |
branch. If FROM is not reachable from `HEAD' or is reachable |
|
502 |
from the source branch's upstream, then an error is raised." |
|
503 |
(interactive (list (magit-read-string-ns "Spin off branch") |
|
504 |
(car (last (magit-region-values 'commit))) |
|
505 |
(magit-branch-arguments))) |
|
506 |
(when (magit-branch-p branch) |
|
507 |
(user-error "Cannot spin off %s. It already exists" branch)) |
|
508 |
(if-let ((current (magit-get-current-branch))) |
|
509 |
(let ((tracked (magit-get-upstream-branch current)) |
|
510 |
base) |
|
511 |
(when from |
|
512 |
(unless (magit-rev-ancestor-p from current) |
|
513 |
(user-error "Cannot spin off %s. %s is not reachable from %s" |
|
514 |
branch from current)) |
|
515 |
(when (and tracked |
|
516 |
(magit-rev-ancestor-p from tracked)) |
|
517 |
(user-error "Cannot spin off %s. %s is ancestor of upstream %s" |
|
518 |
branch from tracked))) |
|
519 |
(let ((magit-process-raise-error t)) |
|
520 |
(magit-call-git "checkout" args "-b" branch current)) |
|
521 |
(--when-let (magit-get-indirect-upstream-branch current) |
|
522 |
(magit-call-git "branch" "--set-upstream-to" it branch)) |
|
523 |
(when (and tracked |
|
524 |
(setq base |
|
525 |
(if from |
|
526 |
(concat from "^") |
|
527 |
(magit-git-string "merge-base" current tracked))) |
|
528 |
(not (magit-rev-eq base current))) |
|
529 |
(magit-call-git "update-ref" "-m" |
|
530 |
(format "reset: moving to %s" base) |
|
531 |
(concat "refs/heads/" current) base)) |
|
532 |
(magit-refresh)) |
|
533 |
(magit-run-git "checkout" "-b" branch))) |
|
534 |
|
|
535 |
;;;###autoload |
|
536 |
(defun magit-branch-reset (branch to &optional args set-upstream) |
|
537 |
"Reset a branch to the tip of another branch or any other commit. |
|
538 |
|
|
539 |
When the branch being reset is the current branch, then do a |
|
540 |
hard reset. If there are any uncommitted changes, then the user |
|
541 |
has to confirm the reset because those changes would be lost. |
|
542 |
|
|
543 |
This is useful when you have started work on a feature branch but |
|
544 |
realize it's all crap and want to start over. |
|
545 |
|
|
546 |
When resetting to another branch and a prefix argument is used, |
|
547 |
then also set the target branch as the upstream of the branch |
|
548 |
that is being reset." |
|
549 |
(interactive |
|
550 |
(let* ((atpoint (magit-local-branch-at-point)) |
|
551 |
(branch (magit-read-local-branch "Reset branch" atpoint))) |
|
552 |
(list branch |
|
553 |
(magit-completing-read (format "Reset %s to" branch) |
|
554 |
(delete branch (magit-list-branch-names)) |
|
555 |
nil nil nil 'magit-revision-history |
|
556 |
(or (and (not (equal branch atpoint)) atpoint) |
|
557 |
(magit-get-upstream-branch branch))) |
|
558 |
(magit-branch-arguments) |
|
559 |
current-prefix-arg))) |
|
560 |
(unless (member "--force" args) |
|
561 |
(setq args (cons "--force" args))) |
|
562 |
(if (equal branch (magit-get-current-branch)) |
|
563 |
(if (and (magit-anything-modified-p) |
|
564 |
(not (yes-or-no-p "Uncommitted changes will be lost. Proceed? "))) |
|
565 |
(user-error "Abort") |
|
566 |
(magit-reset-hard to) |
|
567 |
(when (and set-upstream (magit-branch-p to)) |
|
568 |
(magit-set-upstream-branch branch to))) |
|
569 |
(magit-branch-create branch to args))) |
|
570 |
|
|
571 |
;;;###autoload |
|
572 |
(defun magit-branch-delete (branches &optional force) |
|
573 |
"Delete one or multiple branches. |
|
574 |
If the region marks multiple branches, then offer to delete |
|
575 |
those, otherwise prompt for a single branch to be deleted, |
|
576 |
defaulting to the branch at point." |
|
577 |
;; One would expect this to be a command as simple as, for example, |
|
578 |
;; `magit-branch-rename'; but it turns out everyone wants to squeeze |
|
579 |
;; a bit of extra functionality into this one, including myself. |
|
580 |
(interactive |
|
581 |
(let ((branches (magit-region-values 'branch t)) |
|
582 |
(force current-prefix-arg)) |
|
583 |
(if (> (length branches) 1) |
|
584 |
(magit-confirm t nil "Delete %i branches" nil branches) |
|
585 |
(setq branches |
|
586 |
(list (magit-read-branch-prefer-other |
|
587 |
(if force "Force delete branch" "Delete branch"))))) |
|
588 |
(unless force |
|
589 |
(when-let ((unmerged (-remove #'magit-branch-merged-p branches))) |
|
590 |
(if (magit-confirm 'delete-unmerged-branch |
|
591 |
"Delete unmerged branch %s" |
|
592 |
"Delete %i unmerged branches" |
|
593 |
'noabort unmerged) |
|
594 |
(setq force branches) |
|
595 |
(or (setq branches (-difference branches unmerged)) |
|
596 |
(user-error "Abort"))))) |
|
597 |
(list branches force))) |
|
598 |
(let* ((refs (mapcar #'magit-ref-fullname branches)) |
|
599 |
(ambiguous (--remove it refs))) |
|
600 |
(when ambiguous |
|
601 |
(user-error |
|
602 |
"%s ambiguous. Please cleanup using git directly." |
|
603 |
(let ((len (length ambiguous))) |
|
604 |
(cond |
|
605 |
((= len 1) |
|
606 |
(format "%s is" (-first #'magit-ref-ambiguous-p branches))) |
|
607 |
((= len (length refs)) |
|
608 |
(format "These %s names are" len)) |
|
609 |
(t |
|
610 |
(format "%s of these names are" len)))))) |
|
611 |
(cond |
|
612 |
((string-match "^refs/remotes/\\([^/]+\\)" (car refs)) |
|
613 |
(let* ((remote (match-string 1 (car refs))) |
|
614 |
(offset (1+ (length remote)))) |
|
615 |
;; Assume the branches actually still exists on the remote. |
|
616 |
(magit-run-git-async |
|
617 |
"push" remote (--map (concat ":" (substring it offset)) branches)) |
|
618 |
;; If that is not the case, then this deletes the tracking branches. |
|
619 |
(set-process-sentinel |
|
620 |
magit-this-process |
|
621 |
(apply-partially 'magit-delete-remote-branch-sentinel refs)))) |
|
622 |
((> (length branches) 1) |
|
623 |
(setq branches (delete (magit-get-current-branch) branches)) |
|
624 |
(mapc 'magit-branch-maybe-delete-pr-remote branches) |
|
625 |
(mapc 'magit-branch-unset-pushRemote branches) |
|
626 |
(magit-run-git "branch" (if force "-D" "-d") branches)) |
|
627 |
(t ; And now for something completely different. |
|
628 |
(let* ((branch (car branches)) |
|
629 |
(prompt (format "Branch %s is checked out. " branch))) |
|
630 |
(when (equal branch (magit-get-current-branch)) |
|
631 |
(pcase (if (or (equal branch "master") |
|
632 |
(not (magit-rev-verify "master"))) |
|
633 |
(magit-read-char-case prompt nil |
|
634 |
(?d "[d]etach HEAD & delete" 'detach) |
|
635 |
(?a "[a]bort" 'abort)) |
|
636 |
(magit-read-char-case prompt nil |
|
637 |
(?d "[d]etach HEAD & delete" 'detach) |
|
638 |
(?c "[c]heckout master & delete" 'master) |
|
639 |
(?a "[a]bort" 'abort))) |
|
640 |
(`detach (unless (or (equal force '(4)) |
|
641 |
(member branch force) |
|
642 |
(magit-branch-merged-p branch t)) |
|
643 |
(magit-confirm 'delete-unmerged-branch |
|
644 |
"Delete unmerged branch %s" "" |
|
645 |
nil (list branch))) |
|
646 |
(magit-call-git "checkout" "--detach")) |
|
647 |
(`master (unless (or (equal force '(4)) |
|
648 |
(member branch force) |
|
649 |
(magit-branch-merged-p branch "master")) |
|
650 |
(magit-confirm 'delete-unmerged-branch |
|
651 |
"Delete unmerged branch %s" "" |
|
652 |
nil (list branch))) |
|
653 |
(magit-call-git "checkout" "master")) |
|
654 |
(`abort (user-error "Abort"))) |
|
655 |
(setq force t)) |
|
656 |
(magit-branch-maybe-delete-pr-remote branch) |
|
657 |
(magit-branch-unset-pushRemote branch) |
|
658 |
(magit-run-git "branch" (if force "-D" "-d") branch)))))) |
|
659 |
|
|
660 |
(put 'magit-branch-delete 'interactive-only t) |
|
661 |
|
|
662 |
(defun magit-branch-maybe-delete-pr-remote (branch) |
|
663 |
(when-let ((remote (magit-get "branch" branch "pullRequestRemote"))) |
|
664 |
(let* ((variable (format "remote.%s.fetch" remote)) |
|
665 |
(refspecs (magit-get-all variable))) |
|
666 |
(unless (member (format "+refs/heads/*:refs/remotes/%s/*" remote) |
|
667 |
refspecs) |
|
668 |
(let ((refspec |
|
669 |
(if (equal (magit-get "branch" branch "pushRemote") remote) |
|
670 |
(format "+refs/heads/%s:refs/remotes/%s/%s" |
|
671 |
branch remote branch) |
|
672 |
(let ((merge (magit-get "branch" branch "merge"))) |
|
673 |
(and merge |
|
674 |
(string-prefix-p "refs/heads/" merge) |
|
675 |
(setq merge (substring merge 11)) |
|
676 |
(format "+refs/heads/%s:refs/remotes/%s/%s" |
|
677 |
merge remote merge)))))) |
|
678 |
(when (member refspec refspecs) |
|
679 |
(if (and (= (length refspecs) 1) |
|
680 |
(magit-confirm 'delete-pr-remote |
|
681 |
(format "Also delete remote %s (%s)" remote |
|
682 |
"no pull-request branch remains"))) |
|
683 |
(magit-call-git "remote" "rm" remote) |
|
684 |
(magit-call-git "config" "--unset" variable |
|
685 |
(regexp-quote refspec))))))))) |
|
686 |
|
|
687 |
(defun magit-branch-unset-pushRemote (branch) |
|
688 |
(magit-set nil "branch" branch "pushRemote")) |
|
689 |
|
|
690 |
(defun magit-delete-remote-branch-sentinel (refs process event) |
|
691 |
(when (memq (process-status process) '(exit signal)) |
|
692 |
(if (= (process-exit-status process) 0) |
|
693 |
(magit-process-sentinel process event) |
|
694 |
(if-let ((rest (-filter #'magit-ref-exists-p refs))) |
|
695 |
(progn |
|
696 |
(process-put process 'inhibit-refresh t) |
|
697 |
(magit-process-sentinel process event) |
|
698 |
(setq magit-this-error nil) |
|
699 |
(message "Some remote branches no longer exist. %s" |
|
700 |
"Deleting just the local tracking refs instead...") |
|
701 |
(dolist (ref rest) |
|
702 |
(magit-call-git "update-ref" "-d" ref)) |
|
703 |
(magit-refresh) |
|
704 |
(message "Deleting local remote-tracking refs...done")) |
|
705 |
(magit-process-sentinel process event))))) |
|
706 |
|
|
707 |
;;;###autoload |
|
708 |
(defun magit-branch-rename (old new &optional force) |
|
709 |
"Rename the branch named OLD to NEW. |
|
710 |
|
|
711 |
With a prefix argument FORCE, rename even if a branch named NEW |
|
712 |
already exists. |
|
713 |
|
|
714 |
If `branch.OLD.pushRemote' is set, then unset it. Depending on |
|
715 |
the value of `magit-branch-rename-push-target' (which see) maybe |
|
716 |
set `branch.NEW.pushRemote' and maybe rename the push-target on |
|
717 |
the remote." |
|
718 |
(interactive |
|
719 |
(let ((branch (magit-read-local-branch "Rename branch"))) |
|
720 |
(list branch |
|
721 |
(magit-read-string-ns (format "Rename branch '%s' to" branch) |
|
722 |
nil 'magit-revision-history) |
|
723 |
current-prefix-arg))) |
|
724 |
(when (string-match "\\`heads/\\(.+\\)" old) |
|
725 |
(setq old (match-string 1 old))) |
|
726 |
(when (equal old new) |
|
727 |
(user-error "Old and new branch names are the same")) |
|
728 |
(magit-call-git "branch" (if force "-M" "-m") old new) |
|
729 |
(when magit-branch-rename-push-target |
|
730 |
(let ((remote (magit-get-push-remote old)) |
|
731 |
(old-specific (magit-get "branch" old "pushRemote")) |
|
732 |
(new-specific (magit-get "branch" new "pushRemote"))) |
|
733 |
(when (and old-specific (or force (not new-specific))) |
|
734 |
;; Keep the target setting branch specific, even if that is |
|
735 |
;; redundant. But if a branch by the same name existed before |
|
736 |
;; and the rename isn't forced, then do not change a leftover |
|
737 |
;; setting. Such a leftover setting may or may not conform to |
|
738 |
;; what we expect here... |
|
739 |
(magit-set old-specific "branch" new "pushRemote")) |
|
740 |
(when (and (equal (magit-get-push-remote new) remote) |
|
741 |
;; ...and if it does not, then we must abort. |
|
742 |
(not (eq magit-branch-rename-push-target 'local-only)) |
|
743 |
(or (not (memq magit-branch-rename-push-target |
|
744 |
'(forge-only github-only))) |
|
745 |
(and (require (quote forge) nil t) |
|
746 |
(fboundp 'forge--forge-remote-p) |
|
747 |
(forge--forge-remote-p remote)))) |
|
748 |
(let ((old-target (magit-get-push-branch old t)) |
|
749 |
(new-target (magit-get-push-branch new t)) |
|
750 |
(remote (magit-get-push-remote new))) |
|
751 |
(when (and old-target |
|
752 |
(not new-target) |
|
753 |
(magit-y-or-n-p (format "Also rename %S to %S on %S" |
|
754 |
old new remote))) |
|
755 |
;; Rename on (i.e. within) the remote, but only if the |
|
756 |
;; destination ref doesn't exist yet. If that ref already |
|
757 |
;; exists, then it probably is of some value and we better |
|
758 |
;; not touch it. Ignore what the local ref points at, |
|
759 |
;; i.e. if the local and the remote ref didn't point at |
|
760 |
;; the same commit before the rename then keep it that way. |
|
761 |
(magit-call-git "push" "-v" remote |
|
762 |
(format "%s:refs/heads/%s" old-target new) |
|
763 |
(format ":refs/heads/%s" old))))))) |
|
764 |
(magit-branch-unset-pushRemote old) |
|
765 |
(magit-refresh)) |
|
766 |
|
|
767 |
;;;###autoload |
|
768 |
(defun magit-branch-shelve (branch) |
|
769 |
"Shelve a BRANCH. |
|
770 |
Rename \"refs/heads/BRANCH\" to \"refs/shelved/BRANCH\", |
|
771 |
and also rename the respective reflog file." |
|
772 |
(interactive (list (magit-read-other-local-branch "Shelve branch"))) |
|
773 |
(let ((old (concat "refs/heads/" branch)) |
|
774 |
(new (concat "refs/shelved/" branch))) |
|
775 |
(magit-git "update-ref" new old "") |
|
776 |
(magit--rename-reflog-file old new) |
|
777 |
(magit-branch-unset-pushRemote branch) |
|
778 |
(magit-run-git "branch" "-D" branch))) |
|
779 |
|
|
780 |
;;;###autoload |
|
781 |
(defun magit-branch-unshelve (branch) |
|
782 |
"Unshelve a BRANCH |
|
783 |
Rename \"refs/shelved/BRANCH\" to \"refs/heads/BRANCH\", |
|
784 |
and also rename the respective reflog file." |
|
785 |
(interactive |
|
786 |
(list (magit-completing-read |
|
787 |
"Unshelve branch" |
|
788 |
(--map (substring it 8) |
|
789 |
(magit-list-refnames "refs/shelved")) |
|
790 |
nil t))) |
|
791 |
(let ((old (concat "refs/shelved/" branch)) |
|
792 |
(new (concat "refs/heads/" branch))) |
|
793 |
(magit-git "update-ref" new old "") |
|
794 |
(magit--rename-reflog-file old new) |
|
795 |
(magit-run-git "update-ref" "-d" old))) |
|
796 |
|
|
797 |
(defun magit--rename-reflog-file (old new) |
|
798 |
(let ((old (magit-git-dir (concat "logs/" old))) |
|
799 |
(new (magit-git-dir (concat "logs/" new)))) |
|
800 |
(when (file-exists-p old) |
|
801 |
(make-directory (file-name-directory new) t) |
|
802 |
(rename-file old new t)))) |
|
803 |
|
|
804 |
;;; Config Popup |
|
805 |
|
|
806 |
(defvar magit-branch-config-branch nil) |
|
807 |
|
|
808 |
;;;###autoload |
|
809 |
(defun magit-branch-config-popup (branch) |
|
810 |
"Popup console for setting branch variables." |
|
811 |
(interactive |
|
812 |
(list (if (or current-prefix-arg |
|
813 |
(and (eq magit-current-popup 'magit-branch-popup) |
|
814 |
magit-branch-popup-show-variables)) |
|
815 |
(magit-read-local-branch "Configure branch") |
|
816 |
(magit-get-current-branch)))) |
|
817 |
(let ((magit-branch-config-branch branch)) |
|
818 |
(magit-invoke-popup 'magit-branch-config-popup nil nil))) |
|
819 |
|
|
820 |
(defvar magit-branch-config-variables |
|
821 |
'((lambda () |
|
822 |
(concat |
|
823 |
(propertize "Configure " 'face 'magit-popup-heading) |
|
824 |
(propertize (magit-branch-config-branch) 'face 'magit-branch-local))) |
|
825 |
(?d "branch.%s.description" |
|
826 |
magit-edit-branch*description |
|
827 |
magit-format-branch*description) |
|
828 |
(?u "branch.%s.merge" |
|
829 |
magit-set-branch*merge/remote |
|
830 |
magit-format-branch*merge/remote) |
|
831 |
(?r "branch.%s.rebase" |
|
832 |
magit-cycle-branch*rebase |
|
833 |
magit-format-branch*rebase) |
|
834 |
(?p "branch.%s.pushRemote" |
|
835 |
magit-cycle-branch*pushRemote |
|
836 |
magit-format-branch*pushRemote) |
|
837 |
"Configure repository defaults" |
|
838 |
(?\M-r "pull.rebase" |
|
839 |
magit-cycle-pull.rebase |
|
840 |
magit-format-pull.rebase) |
|
841 |
(?\M-p "remote.pushDefault" |
|
842 |
magit-cycle-remote.pushDefault |
|
843 |
magit-format-remote.pushDefault) |
|
844 |
"Configure branch creation" |
|
845 |
(?U "branch.autoSetupMerge" |
|
846 |
magit-cycle-branch*autoSetupMerge |
|
847 |
magit-format-branch*autoSetupMerge) |
|
848 |
(?R "branch.autoSetupRebase" |
|
849 |
magit-cycle-branch*autoSetupRebase |
|
850 |
magit-format-branch*autoSetupRebase))) |
|
851 |
|
|
852 |
(defvar magit-branch-config-popup |
|
853 |
`(:man-page "git-branch" |
|
854 |
:variables ,magit-branch-config-variables |
|
855 |
:setup-function magit-branch-config-popup-setup)) |
|
856 |
|
|
857 |
(defun magit-branch-config-popup-setup (val def) |
|
858 |
(magit-popup-default-setup val def) |
|
859 |
(setq-local magit-branch-config-branch magit-branch-config-branch) |
|
860 |
(use-local-map (copy-keymap magit-popup-mode-map)) |
|
861 |
(dolist (ev (-filter #'magit-popup-event-p (magit-popup-get :variables))) |
|
862 |
(local-set-key (vector (magit-popup-event-key ev)) |
|
863 |
'magit-invoke-popup-action))) |
|
864 |
|
|
865 |
(defun magit-branch-config-branch (&optional prompt) |
|
866 |
(if prompt |
|
867 |
(or (and (not current-prefix-arg) |
|
868 |
(or magit-branch-config-branch |
|
869 |
(magit-get-current-branch))) |
|
870 |
(magit-read-local-branch prompt)) |
|
871 |
(or magit-branch-config-branch |
|
872 |
(magit-get-current-branch) |
|
873 |
"<name>"))) |
|
874 |
|
|
875 |
;;; Config Commands and Inserters |
|
876 |
|
|
877 |
;;;###autoload |
|
878 |
(defun magit-edit-branch*description (branch) |
|
879 |
"Edit the description of the current branch. |
|
880 |
With a prefix argument edit the description of another branch. |
|
881 |
|
|
882 |
The description for the branch named NAME is stored in the Git |
|
883 |
variable `branch.<name>.description'." |
|
884 |
(interactive (list (magit-branch-config-branch "Edit branch description"))) |
|
885 |
(magit-run-git-with-editor "branch" "--edit-description" branch)) |
|
886 |
|
|
887 |
(defun magit-edit-branch*description-check-buffers () |
|
888 |
(and buffer-file-name |
|
889 |
(string-match-p "/\\(BRANCH\\|EDIT\\)_DESCRIPTION\\'" buffer-file-name) |
|
890 |
(add-hook 'with-editor-post-finish-hook |
|
891 |
(lambda () |
|
892 |
(when (derived-mode-p 'magit-popup-mode) |
|
893 |
(magit-refresh-popup-buffer))) |
|
894 |
nil t))) |
|
895 |
|
|
896 |
(add-hook 'find-file-hook 'magit-edit-branch*description-check-buffers) |
|
897 |
|
|
898 |
(defun magit-format-branch*description () |
|
899 |
(let* ((branch (magit-branch-config-branch)) |
|
900 |
(width (+ (length branch) 19)) |
|
901 |
(var (format "branch.%s.description" branch))) |
|
902 |
(concat var " " (make-string (- width (length var)) ?\s) |
|
903 |
(if-let ((value (magit-get var))) |
|
904 |
(propertize (car (split-string value "\n")) |
|
905 |
'face 'magit-popup-option-value) |
|
906 |
(propertize "unset" 'face 'magit-popup-disabled-argument))))) |
|
907 |
|
|
908 |
;;;###autoload |
|
909 |
(defun magit-set-branch*merge/remote (branch upstream) |
|
910 |
"Set or unset the upstream of the current branch. |
|
911 |
With a prefix argument do so for another branch. |
|
912 |
|
|
913 |
When the branch in question already has an upstream then simply |
|
914 |
unsets it. Invoke this command again to set another upstream. |
|
915 |
|
|
916 |
Together the Git variables `branch.<name>.remote' and |
|
917 |
`branch.<name>.merge' define the upstream branch of the local |
|
918 |
branch named NAME. The value of `branch.<name>.remote' is the |
|
919 |
name of the upstream remote. The value of `branch.<name>.merge' |
|
920 |
is the full reference of the upstream branch, on the remote. |
|
921 |
|
|
922 |
Non-interactively, when UPSTREAM is non-nil, then always set it |
|
923 |
as the new upstream, regardless of whether another upstream was |
|
924 |
already set. When nil, then always unset." |
|
925 |
(interactive |
|
926 |
(let ((branch (magit-branch-config-branch "Change upstream of branch"))) |
|
927 |
(list branch (and (not (magit-get-upstream-branch branch)) |
|
928 |
(magit-read-upstream-branch branch))))) |
|
929 |
(magit-set-upstream-branch branch upstream) |
|
930 |
(magit-refresh)) |
|
931 |
|
|
932 |
(defun magit-format-branch*merge/remote () |
|
933 |
(let* ((branch (magit-branch-config-branch)) |
|
934 |
(width (+ (length branch) 20)) |
|
935 |
(varM (format "branch.%s.merge" branch)) |
|
936 |
(varR (format "branch.%s.remote" branch)) |
|
937 |
(face (if (equal (magit-get varR) ".") |
|
938 |
'magit-branch-local |
|
939 |
'magit-branch-remote))) |
|
940 |
(concat varM (make-string (- width (length varM)) ?\s) |
|
941 |
(if-let ((value (magit-get varM))) |
|
942 |
(propertize value 'face face) |
|
943 |
(propertize "unset" 'face 'magit-popup-disabled-argument)) |
|
944 |
"\n " varR (make-string (- width (length varR)) ?\s) |
|
945 |
(if-let ((value (magit-get varR))) |
|
946 |
(propertize value 'face face) |
|
947 |
(propertize "unset" 'face 'magit-popup-disabled-argument))))) |
|
948 |
|
|
949 |
;;;###autoload |
|
950 |
(defun magit-cycle-branch*rebase (branch) |
|
951 |
"Cycle the value of `branch.<name>.rebase' for the current branch. |
|
952 |
With a prefix argument cycle the value for another branch. |
|
953 |
|
|
954 |
The Git variables `branch.<name>.rebase' controls whether pulling |
|
955 |
into the branch named NAME is done by rebasing that branch onto |
|
956 |
the fetched branch or by merging that branch. |
|
957 |
|
|
958 |
When `true' then pulling is done by rebasing. |
|
959 |
When `false' then pulling is done by merging. |
|
960 |
|
|
961 |
When that variable is undefined then the value of `pull.rebase' |
|
962 |
is used instead. It defaults to `false'." |
|
963 |
(interactive (list (magit-branch-config-branch |
|
964 |
"Cycle branch.<name>.rebase for"))) |
|
965 |
(magit--set-popup-variable (format "branch.%s.rebase" branch) |
|
966 |
'("true" "false") |
|
967 |
"false" "pull.rebase")) |
|
968 |
|
|
969 |
(defun magit-format-branch*rebase () |
|
970 |
(let ((branch (magit-branch-config-branch))) |
|
971 |
(magit--format-popup-variable:choices |
|
972 |
(format "branch.%s.rebase" branch) |
|
973 |
'("true" "false") |
|
974 |
"false" "pull.rebase" |
|
975 |
(+ (length branch) 20)))) |
|
976 |
|
|
977 |
;;;###autoload |
|
978 |
(defun magit-cycle-branch*pushRemote (branch) |
|
979 |
"Cycle the value of `branch.<name>.pushRemote' for the current branch. |
|
980 |
With a prefix argument cycle the value for another branch. |
|
981 |
|
|
982 |
The Git variable `branch.<name>.pushRemote' specifies the remote |
|
983 |
that the branch named NAME is usually pushed to. The value has |
|
984 |
to be the name of an existing remote. |
|
985 |
|
|
986 |
If that variable is undefined, then the value of the Git variable |
|
987 |
`remote.pushDefault' is used instead, provided that it is defined, |
|
988 |
which by default it is not." |
|
989 |
(interactive (list (magit-branch-config-branch |
|
990 |
"Cycle branch.<name>.pushRemote for"))) |
|
991 |
(magit--set-popup-variable (format "branch.%s.pushRemote" branch) |
|
992 |
(magit-list-remotes) |
|
993 |
"remote.pushDefault")) |
|
994 |
|
|
995 |
(defun magit-format-branch*pushRemote () |
|
996 |
(let ((branch (magit-branch-config-branch))) |
|
997 |
(magit--format-popup-variable:choices |
|
998 |
(format "branch.%s.pushRemote" branch) |
|
999 |
(magit-list-remotes) |
|
1000 |
nil "remote.pushDefault" |
|
1001 |
(+ (length branch) 20)))) |
|
1002 |
|
|
1003 |
;;;###autoload |
|
1004 |
(defun magit-cycle-pull.rebase () |
|
1005 |
"Cycle the repository-local value of `pull.rebase'. |
|
1006 |
|
|
1007 |
The Git variable `pull.rebase' specifies whether pulling is done |
|
1008 |
by rebasing or by merging. It can be overwritten using the Git |
|
1009 |
variable `branch.<name>.rebase'. |
|
1010 |
|
|
1011 |
When `true' then pulling is done by rebasing. |
|
1012 |
When `false' (the default) then pulling is done by merging." |
|
1013 |
(interactive) |
|
1014 |
(magit--set-popup-variable "pull.rebase" '("true" "false") "false")) |
|
1015 |
|
|
1016 |
(defun magit-format-pull.rebase () |
|
1017 |
(magit--format-popup-variable:choices |
|
1018 |
"pull.rebase" '("true" "false") "false" nil 19)) |
|
1019 |
|
|
1020 |
;;;###autoload |
|
1021 |
(defun magit-cycle-remote.pushDefault () |
|
1022 |
"Cycle the repository-local value of `remote.pushDefault'. |
|
1023 |
|
|
1024 |
The Git variable `remote.pushDefault' specifies the remote that |
|
1025 |
local branches are usually pushed to. It can be overwritten |
|
1026 |
using the Git variable `branch.<name>.pushRemote'." |
|
1027 |
(interactive) |
|
1028 |
(magit--set-popup-variable "remote.pushDefault" (magit-list-remotes))) |
|
1029 |
|
|
1030 |
(defun magit-format-remote.pushDefault () |
|
1031 |
(magit--format-popup-variable:choices |
|
1032 |
"remote.pushDefault" (magit-list-remotes) nil nil 19)) |
|
1033 |
|
|
1034 |
;;;###autoload |
|
1035 |
(defun magit-cycle-branch*autoSetupMerge () |
|
1036 |
"Cycle the repository-local value of `branch.autoSetupMerge'. |
|
1037 |
|
|
1038 |
The Git variable `branch.autoSetupMerge' under what circumstances |
|
1039 |
creating a branch (named NAME) should result in the variables |
|
1040 |
`branch.<name>.merge' and `branch.<name>.remote' being set |
|
1041 |
according to the starting point used to create the branch. If |
|
1042 |
the starting point isn't a branch, then these variables are never |
|
1043 |
set. |
|
1044 |
|
|
1045 |
When `always' then the variables are set regardless of whether |
|
1046 |
the starting point is a local or a remote branch. |
|
1047 |
|
|
1048 |
When `true' (the default) then the variable are set when the |
|
1049 |
starting point is a remote branch, but not when it is a local |
|
1050 |
branch. |
|
1051 |
|
|
1052 |
When `false' then the variables are never set." |
|
1053 |
(interactive) |
|
1054 |
(magit--set-popup-variable "branch.autoSetupMerge" |
|
1055 |
'("always" "true" "false") "true")) |
|
1056 |
|
|
1057 |
(defun magit-format-branch*autoSetupMerge () |
|
1058 |
(magit--format-popup-variable:choices |
|
1059 |
"branch.autoSetupMerge" '("always" "true" "false") "true" nil 23)) |
|
1060 |
|
|
1061 |
;;;###autoload |
|
1062 |
(defun magit-cycle-branch*autoSetupRebase () |
|
1063 |
"Cycle the repository-local value of `branch.autoSetupRebase'. |
|
1064 |
|
|
1065 |
The Git variable `branch.autoSetupRebase' specifies whether |
|
1066 |
creating a branch (named NAME) should result in the variable |
|
1067 |
`branch.<name>.rebase' being set to `true'. |
|
1068 |
|
|
1069 |
When `always' then the variable is set regardless of whether the |
|
1070 |
starting point is a local or a remote branch. |
|
1071 |
|
|
1072 |
When `local' then the variable are set when the starting point |
|
1073 |
is a local branch, but not when it is a remote branch. |
|
1074 |
|
|
1075 |
When `remote' then the variable are set when the starting point |
|
1076 |
is a remote branch, but not when it is a local branch. |
|
1077 |
|
|
1078 |
When `never' (the default) then the variable is never set." |
|
1079 |
(interactive) |
|
1080 |
(magit--set-popup-variable "branch.autoSetupRebase" |
|
1081 |
'("always" "local" "remote" "never") "never")) |
|
1082 |
|
|
1083 |
(defun magit-format-branch*autoSetupRebase () |
|
1084 |
(magit--format-popup-variable:choices |
|
1085 |
"branch.autoSetupRebase" |
|
1086 |
'("always" "local" "remote" "never") |
|
1087 |
"never" nil 23)) |
|
1088 |
|
|
1089 |
;;; _ |
|
1090 |
(provide 'magit-branch) |
|
1091 |
;;; magit-branch.el ends here |