commit | author | age
|
5cb5f7
|
1 |
;;; magit-sequence.el --- history manipulation in Magit -*- lexical-binding: t -*- |
C |
2 |
|
|
3 |
;; Copyright (C) 2011-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 |
;; Support for Git commands that replay commits and help the user make |
|
27 |
;; changes along the way. Supports `cherry-pick', `revert', `rebase', |
|
28 |
;; `rebase--interactive' and `am'. |
|
29 |
|
|
30 |
;;; Code: |
|
31 |
|
|
32 |
(eval-when-compile |
|
33 |
(require 'subr-x)) |
|
34 |
|
|
35 |
(require 'magit) |
|
36 |
|
|
37 |
;;; Options |
|
38 |
;;;; Faces |
|
39 |
|
|
40 |
(defface magit-sequence-pick |
|
41 |
'((t :inherit default)) |
|
42 |
"Face used in sequence sections." |
|
43 |
:group 'magit-faces) |
|
44 |
|
|
45 |
(defface magit-sequence-stop |
|
46 |
'((((class color) (background light)) :foreground "DarkOliveGreen4") |
|
47 |
(((class color) (background dark)) :foreground "DarkSeaGreen2")) |
|
48 |
"Face used in sequence sections." |
|
49 |
:group 'magit-faces) |
|
50 |
|
|
51 |
(defface magit-sequence-part |
|
52 |
'((((class color) (background light)) :foreground "Goldenrod4") |
|
53 |
(((class color) (background dark)) :foreground "LightGoldenrod2")) |
|
54 |
"Face used in sequence sections." |
|
55 |
:group 'magit-faces) |
|
56 |
|
|
57 |
(defface magit-sequence-head |
|
58 |
'((((class color) (background light)) :foreground "SkyBlue4") |
|
59 |
(((class color) (background dark)) :foreground "LightSkyBlue1")) |
|
60 |
"Face used in sequence sections." |
|
61 |
:group 'magit-faces) |
|
62 |
|
|
63 |
(defface magit-sequence-drop |
|
64 |
'((((class color) (background light)) :foreground "IndianRed") |
|
65 |
(((class color) (background dark)) :foreground "IndianRed")) |
|
66 |
"Face used in sequence sections." |
|
67 |
:group 'magit-faces) |
|
68 |
|
|
69 |
(defface magit-sequence-done |
|
70 |
'((t :inherit magit-hash)) |
|
71 |
"Face used in sequence sections." |
|
72 |
:group 'magit-faces) |
|
73 |
|
|
74 |
(defface magit-sequence-onto |
|
75 |
'((t :inherit magit-sequence-done)) |
|
76 |
"Face used in sequence sections." |
|
77 |
:group 'magit-faces) |
|
78 |
|
|
79 |
(defface magit-sequence-exec |
|
80 |
'((t :inherit magit-hash)) |
|
81 |
"Face used in sequence sections." |
|
82 |
:group 'magit-faces) |
|
83 |
|
|
84 |
;;; Common |
|
85 |
|
|
86 |
;;;###autoload |
|
87 |
(defun magit-sequencer-continue () |
|
88 |
"Resume the current cherry-pick or revert sequence." |
|
89 |
(interactive) |
|
90 |
(if (magit-sequencer-in-progress-p) |
|
91 |
(if (magit-anything-unstaged-p t) |
|
92 |
(user-error "Cannot continue due to unstaged changes") |
|
93 |
(magit-run-git-sequencer |
|
94 |
(if (magit-revert-in-progress-p) "revert" "cherry-pick") "--continue")) |
|
95 |
(user-error "No cherry-pick or revert in progress"))) |
|
96 |
|
|
97 |
;;;###autoload |
|
98 |
(defun magit-sequencer-skip () |
|
99 |
"Skip the stopped at commit during a cherry-pick or revert sequence." |
|
100 |
(interactive) |
|
101 |
(if (magit-sequencer-in-progress-p) |
|
102 |
(progn (magit-call-git "reset" "--hard") |
|
103 |
(magit-sequencer-continue)) |
|
104 |
(user-error "No cherry-pick or revert in progress"))) |
|
105 |
|
|
106 |
;;;###autoload |
|
107 |
(defun magit-sequencer-abort () |
|
108 |
"Abort the current cherry-pick or revert sequence. |
|
109 |
This discards all changes made since the sequence started." |
|
110 |
(interactive) |
|
111 |
(if (magit-sequencer-in-progress-p) |
|
112 |
(magit-run-git-sequencer |
|
113 |
(if (magit-revert-in-progress-p) "revert" "cherry-pick") "--abort") |
|
114 |
(user-error "No cherry-pick or revert in progress"))) |
|
115 |
|
|
116 |
(defun magit-sequencer-in-progress-p () |
|
117 |
(or (magit-cherry-pick-in-progress-p) |
|
118 |
(magit-revert-in-progress-p))) |
|
119 |
|
|
120 |
;;; Cherry-Pick |
|
121 |
|
|
122 |
(defvar magit-perl-executable "perl" |
|
123 |
"The Perl executable.") |
|
124 |
|
|
125 |
;;;###autoload (autoload 'magit-cherry-pick-popup "magit-sequence" nil t) |
|
126 |
(magit-define-popup magit-cherry-pick-popup |
|
127 |
"Popup console for cherry-pick commands." |
|
128 |
:man-page "git-cherry-pick" |
|
129 |
:switches '((?s "Add Signed-off-by lines" "--signoff") |
|
130 |
(?e "Edit commit messages" "--edit") |
|
131 |
(?x "Reference cherry in commit message" "-x") |
|
132 |
(?F "Attempt fast-forward" "--ff")) |
|
133 |
:options '((?s "Strategy" "--strategy=") |
|
134 |
(?m "Replay merge relative to parent" "--mainline=")) |
|
135 |
:actions '("Apply here" |
|
136 |
(?A "Pick" magit-cherry-copy) |
|
137 |
(?a "Apply" magit-cherry-apply) |
|
138 |
(?h "Harvest" magit-cherry-harvest) |
|
139 |
"Apply elsewhere" |
|
140 |
(?d "Donate" magit-cherry-donate) |
|
141 |
(?n "Spinout" magit-cherry-spinout) |
|
142 |
(?s "Spinoff" magit-cherry-spinoff)) |
|
143 |
:sequence-actions '((?A "Continue" magit-sequencer-continue) |
|
144 |
(?s "Skip" magit-sequencer-skip) |
|
145 |
(?a "Abort" magit-sequencer-abort)) |
|
146 |
:sequence-predicate 'magit-sequencer-in-progress-p |
|
147 |
:default-arguments '("--ff")) |
|
148 |
|
|
149 |
(defun magit-cherry-pick-read-args (prompt) |
|
150 |
(list (or (nreverse (magit-region-values 'commit)) |
|
151 |
(magit-read-other-branch-or-commit prompt)) |
|
152 |
(magit-cherry-pick-arguments))) |
|
153 |
|
|
154 |
(defun magit--cherry-move-read-args (verb away fn) |
|
155 |
(declare (indent defun)) |
|
156 |
(let ((commits (or (nreverse (magit-region-values 'commit)) |
|
157 |
(list (funcall (if away |
|
158 |
'magit-read-branch-or-commit |
|
159 |
'magit-read-other-branch-or-commit) |
|
160 |
(format "%s cherry" (capitalize verb)))))) |
|
161 |
(current (magit-get-current-branch))) |
|
162 |
(unless current |
|
163 |
(user-error "Cannot %s cherries while HEAD is detached" verb)) |
|
164 |
(let ((reachable (magit-rev-ancestor-p (car commits) current)) |
|
165 |
(msg "Cannot %s cherries that %s reachable from HEAD")) |
|
166 |
(pcase (list away reachable) |
|
167 |
(`(nil t) (user-error msg verb "are")) |
|
168 |
(`(t nil) (user-error msg verb "are not")))) |
|
169 |
`(,commits |
|
170 |
,@(funcall fn commits) |
|
171 |
,(magit-cherry-pick-arguments)))) |
|
172 |
|
|
173 |
(defun magit--cherry-spinoff-read-args (verb) |
|
174 |
(magit--cherry-move-read-args verb t |
|
175 |
(lambda (commits) |
|
176 |
(butlast (magit-branch-read-args |
|
177 |
(format "Create branch from %s cherries" commits)))))) |
|
178 |
|
|
179 |
;;;###autoload |
|
180 |
(defun magit-cherry-copy (commits &optional args) |
|
181 |
"Copy COMMITS from another branch onto the current branch. |
|
182 |
Prompt for a commit, defaulting to the commit at point. If |
|
183 |
the region selects multiple commits, then pick all of them, |
|
184 |
without prompting." |
|
185 |
(interactive (magit-cherry-pick-read-args "Cherry-pick")) |
|
186 |
(magit--cherry-pick commits args)) |
|
187 |
|
|
188 |
;;;###autoload |
|
189 |
(defun magit-cherry-apply (commits &optional args) |
|
190 |
"Apply the changes in COMMITS but do not commit them. |
|
191 |
Prompt for a commit, defaulting to the commit at point. If |
|
192 |
the region selects multiple commits, then apply all of them, |
|
193 |
without prompting." |
|
194 |
(interactive (magit-cherry-pick-read-args "Apply changes from commit")) |
|
195 |
(magit--cherry-pick commits (cons "--no-commit" (remove "--ff" args)))) |
|
196 |
|
|
197 |
;;;###autoload |
|
198 |
(defun magit-cherry-harvest (commits branch &optional args) |
|
199 |
"Move COMMITS from another BRANCH onto the current branch. |
|
200 |
Remove the COMMITS from BRANCH and stay on the current branch. |
|
201 |
If a conflict occurs, then you have to fix that and finish the |
|
202 |
process manually." |
|
203 |
(interactive |
|
204 |
(magit--cherry-move-read-args "harvest" nil |
|
205 |
(lambda (commits) |
|
206 |
(list (let ((branches (magit-list-containing-branches (car commits)))) |
|
207 |
(pcase (length branches) |
|
208 |
(0 nil) |
|
209 |
(1 (car branches)) |
|
210 |
(_ (magit-completing-read |
|
211 |
(format "Remove %s cherries from branch" (length commits)) |
|
212 |
branches nil t)))))))) |
|
213 |
(magit--cherry-move commits branch (magit-get-current-branch) args nil t)) |
|
214 |
|
|
215 |
;;;###autoload |
|
216 |
(defun magit-cherry-donate (commits branch &optional args) |
|
217 |
"Move COMMITS from the current branch onto another existing BRANCH. |
|
218 |
Remove COMMITS from the current branch and stay on that branch. |
|
219 |
If a conflict occurs, then you have to fix that and finish the |
|
220 |
process manually." |
|
221 |
(interactive |
|
222 |
(magit--cherry-move-read-args "donate" t |
|
223 |
(lambda (commits) |
|
224 |
(list (magit-read-other-branch (format "Move %s cherries to branch" |
|
225 |
(length commits))))))) |
|
226 |
(magit--cherry-move commits (magit-get-current-branch) branch args)) |
|
227 |
|
|
228 |
;;;###autoload |
|
229 |
(defun magit-cherry-spinout (commits branch start-point &optional args) |
|
230 |
"Move COMMITS from the current branch onto a new BRANCH. |
|
231 |
Remove COMMITS from the current branch and stay on that branch. |
|
232 |
If a conflict occurs, then you have to fix that and finish the |
|
233 |
process manually." |
|
234 |
(interactive (magit--cherry-spinoff-read-args "spinout")) |
|
235 |
(magit--cherry-move commits (magit-get-current-branch) branch args |
|
236 |
start-point)) |
|
237 |
|
|
238 |
;;;###autoload |
|
239 |
(defun magit-cherry-spinoff (commits branch start-point &optional args) |
|
240 |
"Move COMMITS from the current branch onto a new BRANCH. |
|
241 |
Remove COMMITS from the current branch and checkout BRANCH. |
|
242 |
If a conflict occurs, then you have to fix that and finish |
|
243 |
the process manually." |
|
244 |
(interactive (magit--cherry-spinoff-read-args "spinoff")) |
|
245 |
(magit--cherry-move commits (magit-get-current-branch) branch args |
|
246 |
start-point t)) |
|
247 |
|
|
248 |
(defun magit--cherry-move (commits src dst args |
|
249 |
&optional start-point checkout-dst) |
|
250 |
(let ((current (magit-get-current-branch))) |
|
251 |
(unless (magit-branch-p dst) |
|
252 |
(let ((magit-process-raise-error t)) |
|
253 |
(magit-call-git "branch" dst start-point)) |
|
254 |
(--when-let (magit-get-indirect-upstream-branch start-point) |
|
255 |
(magit-call-git "branch" "--set-upstream-to" it dst))) |
|
256 |
(unless (equal dst current) |
|
257 |
(let ((magit-process-raise-error t)) |
|
258 |
(magit-call-git "checkout" dst))) |
|
259 |
(if (not src) ; harvest only |
|
260 |
(magit--cherry-pick commits args) |
|
261 |
(let ((tip (car (last commits))) |
|
262 |
(keep (concat (car commits) "^"))) |
|
263 |
(magit--cherry-pick commits args) |
|
264 |
(set-process-sentinel |
|
265 |
magit-this-process |
|
266 |
(lambda (process event) |
|
267 |
(when (memq (process-status process) '(exit signal)) |
|
268 |
(if (> (process-exit-status process) 0) |
|
269 |
(magit-process-sentinel process event) |
|
270 |
(process-put process 'inhibit-refresh t) |
|
271 |
(magit-process-sentinel process event) |
|
272 |
(cond |
|
273 |
((magit-rev-equal tip src) |
|
274 |
(magit-call-git "update-ref" |
|
275 |
"-m" (format "reset: moving to %s" keep) |
|
276 |
(magit-ref-fullname src) |
|
277 |
keep tip) |
|
278 |
(if (not checkout-dst) |
|
279 |
(magit-run-git "checkout" src) |
|
280 |
(magit-refresh))) |
|
281 |
(t |
|
282 |
(magit-git "checkout" src) |
|
283 |
(let ((process-environment process-environment)) |
|
284 |
(push (format "%s=%s -i -ne '/^pick (%s)/ or print'" |
|
285 |
"GIT_SEQUENCE_EDITOR" |
|
286 |
magit-perl-executable |
|
287 |
(mapconcat #'magit-rev-abbrev commits "|")) |
|
288 |
process-environment) |
|
289 |
(magit-run-git-sequencer "rebase" "-i" keep)) |
|
290 |
(when checkout-dst |
|
291 |
(set-process-sentinel |
|
292 |
magit-this-process |
|
293 |
(lambda (process event) |
|
294 |
(when (memq (process-status process) '(exit signal)) |
|
295 |
(if (> (process-exit-status process) 0) |
|
296 |
(magit-process-sentinel process event) |
|
297 |
(process-put process 'inhibit-refresh t) |
|
298 |
(magit-process-sentinel process event) |
|
299 |
(magit-run-git "checkout" dst)))))))))))))))) |
|
300 |
|
|
301 |
(defun magit--cherry-pick (commits args &optional revert) |
|
302 |
(let ((command (if revert "revert" "cherry-pick"))) |
|
303 |
(when (stringp commits) |
|
304 |
(setq commits (if (string-match-p "\\.\\." commits) |
|
305 |
(split-string commits "\\.\\.") |
|
306 |
(list commits)))) |
|
307 |
(magit-run-git-sequencer |
|
308 |
(if revert "revert" "cherry-pick") |
|
309 |
(pcase-let ((`(,merge ,non-merge) |
|
310 |
(-separate 'magit-merge-commit-p commits))) |
|
311 |
(cond |
|
312 |
((not merge) |
|
313 |
(--remove (string-prefix-p "--mainline=" it) args)) |
|
314 |
(non-merge |
|
315 |
(user-error "Cannot %s merge and non-merge commits at once" |
|
316 |
command)) |
|
317 |
((--first (string-prefix-p "--mainline=" it) args) |
|
318 |
args) |
|
319 |
(t |
|
320 |
(cons (format "--mainline=%s" |
|
321 |
(read-number "Replay merges relative to parent: ")) |
|
322 |
args)))) |
|
323 |
commits))) |
|
324 |
|
|
325 |
(defun magit-cherry-pick-in-progress-p () |
|
326 |
;; .git/sequencer/todo does not exist when there is only one commit left. |
|
327 |
(file-exists-p (magit-git-dir "CHERRY_PICK_HEAD"))) |
|
328 |
|
|
329 |
;;; Revert |
|
330 |
|
|
331 |
;;;###autoload (autoload 'magit-revert-popup "magit-sequence" nil t) |
|
332 |
(magit-define-popup magit-revert-popup |
|
333 |
"Popup console for revert commands." |
|
334 |
:man-page "git-revert" |
|
335 |
:switches '((?s "Add Signed-off-by lines" "--signoff") |
|
336 |
(?e "Edit commit message" "--edit") |
|
337 |
(?E "Don't edit commit message" "--no-edit")) |
|
338 |
:options '((?s "Strategy" "--strategy=") |
|
339 |
(?S "Sign using gpg" "--gpg-sign=" magit-read-gpg-secret-key) |
|
340 |
(?m "Replay merge relative to parent" "--mainline=")) |
|
341 |
:actions '((?V "Revert commit(s)" magit-revert-and-commit) |
|
342 |
(?v "Revert changes" magit-revert-no-commit)) |
|
343 |
:sequence-actions '((?V "Continue" magit-sequencer-continue) |
|
344 |
(?s "Skip" magit-sequencer-skip) |
|
345 |
(?a "Abort" magit-sequencer-abort)) |
|
346 |
:sequence-predicate 'magit-sequencer-in-progress-p |
|
347 |
:default-arguments '("--edit")) |
|
348 |
|
|
349 |
(defun magit-revert-read-args (prompt) |
|
350 |
(list (or (magit-region-values 'commit) |
|
351 |
(magit-read-branch-or-commit prompt)) |
|
352 |
(magit-revert-arguments))) |
|
353 |
|
|
354 |
;;;###autoload |
|
355 |
(defun magit-revert-and-commit (commit &optional args) |
|
356 |
"Revert COMMIT by creating a new commit. |
|
357 |
Prompt for a commit, defaulting to the commit at point. If |
|
358 |
the region selects multiple commits, then revert all of them, |
|
359 |
without prompting." |
|
360 |
(interactive (magit-revert-read-args "Revert commit")) |
|
361 |
(magit--cherry-pick commit args t)) |
|
362 |
|
|
363 |
;;;###autoload |
|
364 |
(defun magit-revert-no-commit (commit &optional args) |
|
365 |
"Revert COMMIT by applying it in reverse to the worktree. |
|
366 |
Prompt for a commit, defaulting to the commit at point. If |
|
367 |
the region selects multiple commits, then revert all of them, |
|
368 |
without prompting." |
|
369 |
(interactive (magit-revert-read-args "Revert changes")) |
|
370 |
(magit--cherry-pick commit (cons "--no-commit" args) t)) |
|
371 |
|
|
372 |
(defun magit-revert-in-progress-p () |
|
373 |
;; .git/sequencer/todo does not exist when there is only one commit left. |
|
374 |
(file-exists-p (magit-git-dir "REVERT_HEAD"))) |
|
375 |
|
|
376 |
;;; Patch |
|
377 |
|
|
378 |
;;;###autoload (autoload 'magit-am-popup "magit-sequence" nil t) |
|
379 |
(magit-define-popup magit-am-popup |
|
380 |
"Popup console for mailbox commands." |
|
381 |
:man-page "git-am" |
|
382 |
:switches '((?3 "Fall back on 3way merge" "--3way") |
|
383 |
(?s "Add Signed-off-by lines" "--signoff") |
|
384 |
(?c "Remove text before scissors line" "--scissors") |
|
385 |
(?k "Inhibit removal of email cruft" "--keep") |
|
386 |
(?b "Limit removal of email cruft" "--keep-non-patch") |
|
387 |
(?d "Use author date as committer date" |
|
388 |
"--committer-date-is-author-date") |
|
389 |
(?D "Use committer date as author date" "--ignore-date")) |
|
390 |
:options '((?p "Remove leading slashes from paths" "-p" |
|
391 |
magit-read-number-string)) |
|
392 |
:actions '((?m "Apply maildir" magit-am-apply-maildir) |
|
393 |
(?w "Apply patches" magit-am-apply-patches) |
|
394 |
(?a "Apply plain patch" magit-patch-apply-popup)) |
|
395 |
:default-arguments '("--3way") |
|
396 |
:default-actions 'magit-am-apply-patches |
|
397 |
:max-action-columns 1 |
|
398 |
:sequence-actions '((?w "Continue" magit-am-continue) |
|
399 |
(?s "Skip" magit-am-skip) |
|
400 |
(?a "Abort" magit-am-abort)) |
|
401 |
:sequence-predicate 'magit-am-in-progress-p) |
|
402 |
|
|
403 |
;;;###autoload |
|
404 |
(defun magit-am-apply-patches (&optional files args) |
|
405 |
"Apply the patches FILES." |
|
406 |
(interactive (list (or (magit-region-values 'file) |
|
407 |
(list (let ((default (magit-file-at-point))) |
|
408 |
(read-file-name |
|
409 |
(if default |
|
410 |
(format "Apply patch (%s): " default) |
|
411 |
"Apply patch: ") |
|
412 |
nil default)))) |
|
413 |
(magit-am-arguments))) |
|
414 |
(magit-run-git-sequencer "am" args "--" |
|
415 |
(--map (magit-convert-filename-for-git |
|
416 |
(expand-file-name it)) |
|
417 |
files))) |
|
418 |
|
|
419 |
;;;###autoload |
|
420 |
(defun magit-am-apply-maildir (&optional maildir args) |
|
421 |
"Apply the patches from MAILDIR." |
|
422 |
(interactive (list (read-file-name "Apply mbox or Maildir: ") |
|
423 |
(magit-am-arguments))) |
|
424 |
(magit-run-git-sequencer "am" args (magit-convert-filename-for-git |
|
425 |
(expand-file-name maildir)))) |
|
426 |
|
|
427 |
;;;###autoload |
|
428 |
(defun magit-am-continue () |
|
429 |
"Resume the current patch applying sequence." |
|
430 |
(interactive) |
|
431 |
(if (magit-am-in-progress-p) |
|
432 |
(if (magit-anything-unstaged-p t) |
|
433 |
(error "Cannot continue due to unstaged changes") |
|
434 |
(magit-run-git-sequencer "am" "--continue")) |
|
435 |
(user-error "Not applying any patches"))) |
|
436 |
|
|
437 |
;;;###autoload |
|
438 |
(defun magit-am-skip () |
|
439 |
"Skip the stopped at patch during a patch applying sequence." |
|
440 |
(interactive) |
|
441 |
(if (magit-am-in-progress-p) |
|
442 |
(magit-run-git-sequencer "am" "--skip") |
|
443 |
(user-error "Not applying any patches"))) |
|
444 |
|
|
445 |
;;;###autoload |
|
446 |
(defun magit-am-abort () |
|
447 |
"Abort the current patch applying sequence. |
|
448 |
This discards all changes made since the sequence started." |
|
449 |
(interactive) |
|
450 |
(if (magit-am-in-progress-p) |
|
451 |
(magit-run-git "am" "--abort") |
|
452 |
(user-error "Not applying any patches"))) |
|
453 |
|
|
454 |
(defun magit-am-in-progress-p () |
|
455 |
(file-exists-p (magit-git-dir "rebase-apply/applying"))) |
|
456 |
|
|
457 |
;;; Rebase |
|
458 |
|
|
459 |
;;;###autoload (autoload 'magit-rebase-popup "magit-sequence" nil t) |
|
460 |
(magit-define-popup magit-rebase-popup |
|
461 |
"Key menu for rebasing." |
|
462 |
:man-page "git-rebase" |
|
463 |
:switches '((?k "Keep empty commits" "--keep-empty") |
|
464 |
(?p "Preserve merges" "--preserve-merges") |
|
465 |
(?c "Lie about committer date" "--committer-date-is-author-date") |
|
466 |
(?a "Autosquash" "--autosquash") |
|
467 |
(?A "Autostash" "--autostash") |
|
468 |
(?i "Interactive" "--interactive") |
|
469 |
(?h "Disable hooks" "--no-verify")) |
|
470 |
:actions '((lambda () |
|
471 |
(concat (propertize "Rebase " 'face 'magit-popup-heading) |
|
472 |
(propertize (or (magit-get-current-branch) "HEAD") |
|
473 |
'face 'magit-branch-local) |
|
474 |
(propertize " onto" 'face 'magit-popup-heading))) |
|
475 |
(?p (lambda () |
|
476 |
(--when-let (magit-get-push-branch) (concat it "\n"))) |
|
477 |
magit-rebase-onto-pushremote) |
|
478 |
(?u (lambda () |
|
479 |
(--when-let (magit-get-upstream-branch) (concat it "\n"))) |
|
480 |
magit-rebase-onto-upstream) |
|
481 |
(?e "elsewhere" magit-rebase-branch) |
|
482 |
"Rebase" |
|
483 |
(?i "interactively" magit-rebase-interactive) |
|
484 |
(?m "to modify a commit" magit-rebase-edit-commit) |
|
485 |
(?s "a subset" magit-rebase-subset) |
|
486 |
(?w "to reword a commit" magit-rebase-reword-commit) nil |
|
487 |
(?k "to remove a commit" magit-rebase-remove-commit) nil |
|
488 |
(?f "to autosquash" magit-rebase-autosquash)) |
|
489 |
:sequence-actions '((?r "Continue" magit-rebase-continue) |
|
490 |
(?s "Skip" magit-rebase-skip) |
|
491 |
(?e "Edit" magit-rebase-edit) |
|
492 |
(?a "Abort" magit-rebase-abort)) |
|
493 |
:sequence-predicate 'magit-rebase-in-progress-p |
|
494 |
:max-action-columns 2) |
|
495 |
|
|
496 |
(defun magit-git-rebase (target args) |
|
497 |
(magit-run-git-sequencer "rebase" target args)) |
|
498 |
|
|
499 |
;;;###autoload |
|
500 |
(defun magit-rebase-onto-pushremote (args) |
|
501 |
"Rebase the current branch onto `branch.<name>.pushRemote'. |
|
502 |
If that variable is unset, then rebase onto `remote.pushDefault'." |
|
503 |
(interactive (list (magit-rebase-arguments))) |
|
504 |
(--if-let (magit-get-current-branch) |
|
505 |
(if-let ((remote (magit-get-push-remote it))) |
|
506 |
(if (member remote (magit-list-remotes)) |
|
507 |
(magit-git-rebase (concat remote "/" it) args) |
|
508 |
(user-error "Remote `%s' doesn't exist" remote)) |
|
509 |
(user-error "No push-remote is configured for %s" it)) |
|
510 |
(user-error "No branch is checked out"))) |
|
511 |
|
|
512 |
;;;###autoload |
|
513 |
(defun magit-rebase-onto-upstream (args) |
|
514 |
"Rebase the current branch onto its upstream branch." |
|
515 |
(interactive (list (magit-rebase-arguments))) |
|
516 |
(--if-let (magit-get-current-branch) |
|
517 |
(if-let ((target (magit-get-upstream-branch it))) |
|
518 |
(magit-git-rebase target args) |
|
519 |
(user-error "No upstream is configured for %s" it)) |
|
520 |
(user-error "No branch is checked out"))) |
|
521 |
|
|
522 |
;;;###autoload |
|
523 |
(defun magit-rebase-branch (target args) |
|
524 |
"Rebase the current branch onto a branch read in the minibuffer. |
|
525 |
All commits that are reachable from `HEAD' but not from the |
|
526 |
selected branch TARGET are being rebased." |
|
527 |
(interactive (list (magit-read-other-branch-or-commit "Rebase onto") |
|
528 |
(magit-rebase-arguments))) |
|
529 |
(message "Rebasing...") |
|
530 |
(magit-git-rebase target args) |
|
531 |
(message "Rebasing...done")) |
|
532 |
|
|
533 |
;;;###autoload |
|
534 |
(defun magit-rebase-subset (newbase start args) |
|
535 |
"Rebase a subset of the current branch's history onto a new base. |
|
536 |
Rebase commits from START to `HEAD' onto NEWBASE. |
|
537 |
START has to be selected from a list of recent commits." |
|
538 |
(interactive (list (magit-read-other-branch-or-commit |
|
539 |
"Rebase subset onto" nil |
|
540 |
(magit-get-upstream-branch)) |
|
541 |
nil |
|
542 |
(magit-rebase-arguments))) |
|
543 |
(if start |
|
544 |
(progn (message "Rebasing...") |
|
545 |
(magit-run-git-sequencer "rebase" "--onto" newbase start args) |
|
546 |
(message "Rebasing...done")) |
|
547 |
(magit-log-select |
|
548 |
`(lambda (commit) |
|
549 |
(magit-rebase-subset ,newbase (concat commit "^") (list ,@args))) |
|
550 |
(concat "Type %p on a commit to rebase it " |
|
551 |
"and commits above it onto " newbase ",")))) |
|
552 |
|
|
553 |
(defun magit-rebase-interactive-1 |
|
554 |
(commit args message &optional editor delay-edit-confirm noassert confirm) |
|
555 |
(declare (indent 2)) |
|
556 |
(when commit |
|
557 |
(if (eq commit :merge-base) |
|
558 |
(setq commit (--if-let (magit-get-upstream-branch) |
|
559 |
(magit-git-string "merge-base" it "HEAD") |
|
560 |
nil)) |
|
561 |
(unless (magit-rev-ancestor-p commit "HEAD") |
|
562 |
(user-error "%s isn't an ancestor of HEAD" commit)) |
|
563 |
(if (magit-commit-parents commit) |
|
564 |
(setq commit (concat commit "^")) |
|
565 |
(setq args (cons "--root" args))))) |
|
566 |
(when (and commit (not noassert)) |
|
567 |
(setq commit (magit-rebase-interactive-assert commit delay-edit-confirm))) |
|
568 |
(if (and commit (not confirm)) |
|
569 |
(let ((process-environment process-environment)) |
|
570 |
(when editor |
|
571 |
(push (concat "GIT_SEQUENCE_EDITOR=" editor) process-environment)) |
|
572 |
(magit-run-git-sequencer "rebase" "-i" args |
|
573 |
(unless (member "--root" args) commit))) |
|
574 |
(magit-log-select |
|
575 |
`(lambda (commit) |
|
576 |
(magit-rebase-interactive-1 commit (list ,@args) |
|
577 |
,message ,editor ,delay-edit-confirm ,noassert)) |
|
578 |
message))) |
|
579 |
|
|
580 |
(defvar magit--rebase-published-symbol nil) |
|
581 |
(defvar magit--rebase-public-edit-confirmed nil) |
|
582 |
|
|
583 |
(defun magit-rebase-interactive-assert (since &optional delay-edit-confirm) |
|
584 |
(let* ((commit (if (string-suffix-p "^" since) |
|
585 |
;; If SINCE is "REV^", then the user selected |
|
586 |
;; "REV", which is the first commit that will |
|
587 |
;; be replaced. (from^..to] <=> [from..to]. |
|
588 |
(substring since 0 -1) |
|
589 |
;; The "--root" argument is being used. |
|
590 |
since)) |
|
591 |
(branches (magit-list-publishing-branches commit))) |
|
592 |
(setq magit--rebase-public-edit-confirmed |
|
593 |
(delete (magit-toplevel) magit--rebase-public-edit-confirmed)) |
|
594 |
(when (and branches |
|
595 |
(or (not delay-edit-confirm) |
|
596 |
;; The user might have stopped at a published commit |
|
597 |
;; merely to add new commits *after* it. Try not to |
|
598 |
;; ask users whether they really want to edit public |
|
599 |
;; commits, when they don't actually intend to do so. |
|
600 |
(not (--all-p (magit-rev-equal it commit) branches)))) |
|
601 |
(let ((m1 "Some of these commits have already been published to ") |
|
602 |
(m2 ".\nDo you really want to modify them")) |
|
603 |
(magit-confirm (or magit--rebase-published-symbol 'rebase-published) |
|
604 |
(concat m1 "%s" m2) |
|
605 |
(concat m1 "%i public branches" m2) |
|
606 |
nil branches)) |
|
607 |
(push (magit-toplevel) magit--rebase-public-edit-confirmed))) |
|
608 |
(if (magit-git-lines "rev-list" "--merges" (concat since "..HEAD")) |
|
609 |
(magit-read-char-case "Proceed despite merge in rebase range? " nil |
|
610 |
(?c "[c]ontinue" since) |
|
611 |
(?s "[s]elect other" nil) |
|
612 |
(?a "[a]bort" (user-error "Quit"))) |
|
613 |
since)) |
|
614 |
|
|
615 |
;;;###autoload |
|
616 |
(defun magit-rebase-interactive (commit args) |
|
617 |
"Start an interactive rebase sequence." |
|
618 |
(interactive (list (magit-commit-at-point) |
|
619 |
(magit-rebase-arguments))) |
|
620 |
(magit-rebase-interactive-1 commit args |
|
621 |
"Type %p on a commit to rebase it and all commits above it," |
|
622 |
nil t)) |
|
623 |
|
|
624 |
;;;###autoload |
|
625 |
(defun magit-rebase-autosquash (args) |
|
626 |
"Combine squash and fixup commits with their intended targets." |
|
627 |
(interactive (list (magit-rebase-arguments))) |
|
628 |
(magit-rebase-interactive-1 :merge-base (cons "--autosquash" args) |
|
629 |
"Type %p on a commit to squash into it and then rebase as necessary," |
|
630 |
"true" nil t)) |
|
631 |
|
|
632 |
;;;###autoload |
|
633 |
(defun magit-rebase-edit-commit (commit args) |
|
634 |
"Edit a single older commit using rebase." |
|
635 |
(interactive (list (magit-commit-at-point) |
|
636 |
(magit-rebase-arguments))) |
|
637 |
(magit-rebase-interactive-1 commit args |
|
638 |
"Type %p on a commit to edit it," |
|
639 |
(concat magit-perl-executable |
|
640 |
" -i -p -e '++$x if not $x and s/^pick/edit/'") |
|
641 |
t)) |
|
642 |
|
|
643 |
;;;###autoload |
|
644 |
(defun magit-rebase-reword-commit (commit args) |
|
645 |
"Reword a single older commit using rebase." |
|
646 |
(interactive (list (magit-commit-at-point) |
|
647 |
(magit-rebase-arguments))) |
|
648 |
(magit-rebase-interactive-1 commit args |
|
649 |
"Type %p on a commit to reword its message," |
|
650 |
(concat magit-perl-executable |
|
651 |
" -i -p -e '++$x if not $x and s/^pick/reword/'"))) |
|
652 |
|
|
653 |
;;;###autoload |
|
654 |
(defun magit-rebase-remove-commit (commit args) |
|
655 |
"Remove a single older commit using rebase." |
|
656 |
(interactive (list (magit-commit-at-point) |
|
657 |
(magit-rebase-arguments))) |
|
658 |
(magit-rebase-interactive-1 commit args |
|
659 |
"Type %p on a commit to remove it," |
|
660 |
(concat magit-perl-executable |
|
661 |
" -i -p -e '++$x if not $x and s/^pick/# pick/'") |
|
662 |
nil nil t)) |
|
663 |
|
|
664 |
;;;###autoload |
|
665 |
(defun magit-rebase-continue (&optional noedit) |
|
666 |
"Restart the current rebasing operation. |
|
667 |
In some cases this pops up a commit message buffer for you do |
|
668 |
edit. With a prefix argument the old message is reused as-is." |
|
669 |
(interactive "P") |
|
670 |
(if (magit-rebase-in-progress-p) |
|
671 |
(if (magit-anything-unstaged-p t) |
|
672 |
(user-error "Cannot continue rebase with unstaged changes") |
|
673 |
(when (and (magit-anything-staged-p) |
|
674 |
(file-exists-p (magit-git-dir "rebase-merge")) |
|
675 |
(not (member (magit-toplevel) |
|
676 |
magit--rebase-public-edit-confirmed))) |
|
677 |
(magit-commit-amend-assert)) |
|
678 |
(if noedit |
|
679 |
(let ((process-environment process-environment)) |
|
680 |
(push "GIT_EDITOR=true" process-environment) |
|
681 |
(magit-run-git-async (magit--rebase-resume-command) "--continue") |
|
682 |
(set-process-sentinel magit-this-process |
|
683 |
#'magit-sequencer-process-sentinel) |
|
684 |
magit-this-process) |
|
685 |
(magit-run-git-sequencer (magit--rebase-resume-command) "--continue"))) |
|
686 |
(user-error "No rebase in progress"))) |
|
687 |
|
|
688 |
;;;###autoload |
|
689 |
(defun magit-rebase-skip () |
|
690 |
"Skip the current commit and restart the current rebase operation." |
|
691 |
(interactive) |
|
692 |
(unless (magit-rebase-in-progress-p) |
|
693 |
(user-error "No rebase in progress")) |
|
694 |
(magit-run-git-sequencer (magit--rebase-resume-command) "--skip")) |
|
695 |
|
|
696 |
;;;###autoload |
|
697 |
(defun magit-rebase-edit () |
|
698 |
"Edit the todo list of the current rebase operation." |
|
699 |
(interactive) |
|
700 |
(unless (magit-rebase-in-progress-p) |
|
701 |
(user-error "No rebase in progress")) |
|
702 |
(magit-run-git-sequencer "rebase" "--edit-todo")) |
|
703 |
|
|
704 |
;;;###autoload |
|
705 |
(defun magit-rebase-abort () |
|
706 |
"Abort the current rebase operation, restoring the original branch." |
|
707 |
(interactive) |
|
708 |
(unless (magit-rebase-in-progress-p) |
|
709 |
(user-error "No rebase in progress")) |
|
710 |
(magit-confirm 'abort-rebase "Abort this rebase") |
|
711 |
(magit-run-git (magit--rebase-resume-command) "--abort")) |
|
712 |
|
|
713 |
(defun magit-rebase-in-progress-p () |
|
714 |
"Return t if a rebase is in progress." |
|
715 |
(or (file-exists-p (magit-git-dir "rebase-merge")) |
|
716 |
(file-exists-p (magit-git-dir "rebase-apply/onto")))) |
|
717 |
|
|
718 |
(defun magit--rebase-resume-command () |
|
719 |
(if (file-exists-p (magit-git-dir "rebase-recursive")) "rbr" "rebase")) |
|
720 |
|
|
721 |
;;; Sections |
|
722 |
|
|
723 |
(defun magit-insert-sequencer-sequence () |
|
724 |
"Insert section for the on-going cherry-pick or revert sequence. |
|
725 |
If no such sequence is in progress, do nothing." |
|
726 |
(let ((picking (magit-cherry-pick-in-progress-p))) |
|
727 |
(when (or picking (magit-revert-in-progress-p)) |
|
728 |
(magit-insert-section (sequence) |
|
729 |
(magit-insert-heading (if picking "Cherry Picking" "Reverting")) |
|
730 |
(when-let ((lines |
|
731 |
(cdr (magit-file-lines (magit-git-dir "sequencer/todo"))))) |
|
732 |
(dolist (line (nreverse lines)) |
|
733 |
(when (string-match |
|
734 |
"^\\(pick\\|revert\\) \\([^ ]+\\) \\(.*\\)$" line) |
|
735 |
(magit-bind-match-strings (cmd hash msg) line |
|
736 |
(magit-insert-section (commit hash) |
|
737 |
(insert (propertize cmd 'face 'magit-sequence-pick) |
|
738 |
" " (propertize hash 'face 'magit-hash) |
|
739 |
" " msg "\n")))))) |
|
740 |
(magit-sequence-insert-sequence |
|
741 |
(magit-file-line (magit-git-dir (if picking |
|
742 |
"CHERRY_PICK_HEAD" |
|
743 |
"REVERT_HEAD"))) |
|
744 |
(magit-file-line (magit-git-dir "sequencer/head"))) |
|
745 |
(insert "\n"))))) |
|
746 |
|
|
747 |
(defun magit-insert-am-sequence () |
|
748 |
"Insert section for the on-going patch applying sequence. |
|
749 |
If no such sequence is in progress, do nothing." |
|
750 |
(when (magit-am-in-progress-p) |
|
751 |
(magit-insert-section (rebase-sequence) |
|
752 |
(magit-insert-heading "Applying patches") |
|
753 |
(let ((patches (nreverse (magit-rebase-patches))) |
|
754 |
patch commit) |
|
755 |
(while patches |
|
756 |
(setq patch (pop patches)) |
|
757 |
(setq commit (magit-rev-verify-commit |
|
758 |
(cadr (split-string (magit-file-line patch))))) |
|
759 |
(cond ((and commit patches) |
|
760 |
(magit-sequence-insert-commit |
|
761 |
"pick" commit 'magit-sequence-pick)) |
|
762 |
(patches |
|
763 |
(magit-sequence-insert-am-patch |
|
764 |
"pick" patch 'magit-sequence-pick)) |
|
765 |
(commit |
|
766 |
(magit-sequence-insert-sequence commit "ORIG_HEAD")) |
|
767 |
(t |
|
768 |
(magit-sequence-insert-am-patch |
|
769 |
"stop" patch 'magit-sequence-stop) |
|
770 |
(magit-sequence-insert-sequence nil "ORIG_HEAD"))))) |
|
771 |
(insert ?\n)))) |
|
772 |
|
|
773 |
(defun magit-sequence-insert-am-patch (type patch face) |
|
774 |
(magit-insert-section (file patch) |
|
775 |
(let ((title |
|
776 |
(with-temp-buffer |
|
777 |
(insert-file-contents patch nil nil 4096) |
|
778 |
(unless (re-search-forward "^Subject: " nil t) |
|
779 |
(goto-char (point-min))) |
|
780 |
(buffer-substring (point) (line-end-position))))) |
|
781 |
(insert (propertize type 'face face) |
|
782 |
?\s (propertize (file-name-nondirectory patch) 'face 'magit-hash) |
|
783 |
?\s title |
|
784 |
?\n)))) |
|
785 |
|
|
786 |
(defun magit-insert-rebase-sequence () |
|
787 |
"Insert section for the on-going rebase sequence. |
|
788 |
If no such sequence is in progress, do nothing." |
|
789 |
(when (magit-rebase-in-progress-p) |
|
790 |
(let* ((interactive (file-directory-p (magit-git-dir "rebase-merge"))) |
|
791 |
(dir (if interactive "rebase-merge/" "rebase-apply/")) |
|
792 |
(name (-> (concat dir "head-name") magit-git-dir magit-file-line)) |
|
793 |
(onto (-> (concat dir "onto") magit-git-dir magit-file-line)) |
|
794 |
(onto (or (magit-rev-name onto name) |
|
795 |
(magit-rev-name onto "refs/heads/*") onto)) |
|
796 |
(name (or (magit-rev-name name "refs/heads/*") name))) |
|
797 |
(magit-insert-section (rebase-sequence) |
|
798 |
(magit-insert-heading (format "Rebasing %s onto %s" name onto)) |
|
799 |
(if interactive |
|
800 |
(magit-rebase-insert-merge-sequence onto) |
|
801 |
(magit-rebase-insert-apply-sequence onto)) |
|
802 |
(insert ?\n))))) |
|
803 |
|
|
804 |
(defun magit-rebase-insert-merge-sequence (onto) |
|
805 |
(let (exec) |
|
806 |
(dolist (line (nreverse |
|
807 |
(magit-file-lines |
|
808 |
(magit-git-dir "rebase-merge/git-rebase-todo")))) |
|
809 |
(cond ((string-prefix-p "exec" line) |
|
810 |
(setq exec (substring line 5))) |
|
811 |
((string-match (format "^\\([^%c ]+\\) \\([^ ]+\\) .*$" |
|
812 |
(string-to-char |
|
813 |
(or (magit-get "core.commentChar") "#"))) |
|
814 |
line) |
|
815 |
(magit-bind-match-strings (action hash) line |
|
816 |
(unless (equal action "exec") |
|
817 |
(magit-sequence-insert-commit |
|
818 |
action hash 'magit-sequence-pick exec))) |
|
819 |
(setq exec nil))))) |
|
820 |
(magit-sequence-insert-sequence |
|
821 |
(magit-file-line (magit-git-dir "rebase-merge/stopped-sha")) |
|
822 |
onto |
|
823 |
(--when-let (magit-file-lines (magit-git-dir "rebase-merge/done")) |
|
824 |
(cadr (split-string (car (last it))))))) |
|
825 |
|
|
826 |
(defun magit-rebase-insert-apply-sequence (onto) |
|
827 |
(let ((rewritten |
|
828 |
(--map (car (split-string it)) |
|
829 |
(magit-file-lines (magit-git-dir "rebase-apply/rewritten")))) |
|
830 |
(stop (magit-file-line (magit-git-dir "rebase-apply/original-commit")))) |
|
831 |
(dolist (patch (nreverse (cdr (magit-rebase-patches)))) |
|
832 |
(let ((hash (cadr (split-string (magit-file-line patch))))) |
|
833 |
(unless (or (member hash rewritten) |
|
834 |
(equal hash stop)) |
|
835 |
(magit-sequence-insert-commit "pick" hash 'magit-sequence-pick))))) |
|
836 |
(magit-sequence-insert-sequence |
|
837 |
(magit-file-line (magit-git-dir "rebase-apply/original-commit")) |
|
838 |
onto)) |
|
839 |
|
|
840 |
(defun magit-rebase-patches () |
|
841 |
(directory-files (magit-git-dir "rebase-apply") t "^[0-9]\\{4\\}$")) |
|
842 |
|
|
843 |
(defun magit-sequence-insert-sequence (stop onto &optional orig) |
|
844 |
(let ((head (magit-rev-parse "HEAD")) done) |
|
845 |
(setq onto (if onto (magit-rev-parse onto) head)) |
|
846 |
(setq done (magit-git-lines "log" "--format=%H" (concat onto "..HEAD"))) |
|
847 |
(when (and stop (not (member stop done))) |
|
848 |
(let ((id (magit-patch-id stop))) |
|
849 |
(--if-let (--first (equal (magit-patch-id it) id) done) |
|
850 |
(setq stop it) |
|
851 |
(cond |
|
852 |
((--first (magit-rev-equal it stop) done) |
|
853 |
;; The commit's testament has been executed. |
|
854 |
(magit-sequence-insert-commit "void" stop 'magit-sequence-drop)) |
|
855 |
;; The faith of the commit is still undecided... |
|
856 |
((magit-anything-unmerged-p) |
|
857 |
;; ...and time travel isn't for the faint of heart. |
|
858 |
(magit-sequence-insert-commit "join" stop 'magit-sequence-part)) |
|
859 |
((magit-anything-modified-p t) |
|
860 |
;; ...and the dust hasn't settled yet... |
|
861 |
(magit-sequence-insert-commit |
|
862 |
(let* ((magit--refresh-cache nil) |
|
863 |
(staged (magit-commit-tree "oO" nil "HEAD")) |
|
864 |
(unstaged (magit-commit-worktree "oO" "--reset"))) |
|
865 |
(cond |
|
866 |
;; ...but we could end up at the same tree just by committing. |
|
867 |
((or (magit-rev-equal staged stop) |
|
868 |
(magit-rev-equal unstaged stop)) "goal") |
|
869 |
;; ...but the changes are still there, untainted. |
|
870 |
((or (equal (magit-patch-id staged) id) |
|
871 |
(equal (magit-patch-id unstaged) id)) "same") |
|
872 |
;; ...and some changes are gone and/or others were added. |
|
873 |
(t "work"))) |
|
874 |
stop 'magit-sequence-part)) |
|
875 |
;; The commit is definitely gone... |
|
876 |
((--first (magit-rev-equal it stop) done) |
|
877 |
;; ...but all of its changes are still in effect. |
|
878 |
(magit-sequence-insert-commit "poof" stop 'magit-sequence-drop)) |
|
879 |
(t |
|
880 |
;; ...and some changes are gone and/or other changes were added. |
|
881 |
(magit-sequence-insert-commit "gone" stop 'magit-sequence-drop))) |
|
882 |
(setq stop nil)))) |
|
883 |
(dolist (rev done) |
|
884 |
(apply 'magit-sequence-insert-commit |
|
885 |
(cond ((equal rev stop) |
|
886 |
;; ...but its reincarnation lives on. |
|
887 |
;; Or it didn't die in the first place. |
|
888 |
(list (if (and (equal rev head) |
|
889 |
(equal (magit-patch-id rev) |
|
890 |
(magit-patch-id orig))) |
|
891 |
"stop" ; We haven't done anything yet. |
|
892 |
"like") ; There are new commits. |
|
893 |
rev (if (equal rev head) |
|
894 |
'magit-sequence-head |
|
895 |
'magit-sequence-stop))) |
|
896 |
((equal rev head) |
|
897 |
(list "done" rev 'magit-sequence-head)) |
|
898 |
(t |
|
899 |
(list "done" rev 'magit-sequence-done))))) |
|
900 |
(magit-sequence-insert-commit "onto" onto |
|
901 |
(if (equal onto head) |
|
902 |
'magit-sequence-head |
|
903 |
'magit-sequence-onto)))) |
|
904 |
|
|
905 |
(defun magit-sequence-insert-commit (type hash face &optional exec) |
|
906 |
(magit-insert-section (commit hash) |
|
907 |
(magit-insert-heading |
|
908 |
(propertize type 'face face) "\s" |
|
909 |
(magit-format-rev-summary hash) "\n") |
|
910 |
(when exec |
|
911 |
(insert (propertize "exec" 'face 'magit-sequence-onto) "\s" exec "\n")))) |
|
912 |
|
|
913 |
;;; _ |
|
914 |
(provide 'magit-sequence) |
|
915 |
;;; magit-sequence.el ends here |