fordon_greeman / voidfiles

Branch: main
Switch branches/tags
main
theidioticdev add emacs 2079ec5 23h ago
1161 lines · 47.1 KB Raw Blame History
1 ;;; magit-sequence.el --- History manipulation in Magit -*- lexical-binding:t -*-
2
3 ;; Copyright (C) 2008-2026 The Magit Project Contributors
4
5 ;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
6 ;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
7
8 ;; SPDX-License-Identifier: GPL-3.0-or-later
9
10 ;; Magit is free software: you can redistribute it and/or modify it
11 ;; under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
14 ;;
15 ;; Magit is distributed in the hope that it will be useful, but WITHOUT
16 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
18 ;; License for more details.
19 ;;
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with Magit. If not, see <https://www.gnu.org/licenses/>.
22
23 ;;; Commentary:
24
25 ;; Support for Git commands that replay commits and help the user make
26 ;; changes along the way. Supports `cherry-pick', `revert', `rebase',
27 ;; `rebase--interactive' and `am'.
28
29 ;;; Code:
30
31 (eval-when-compile (require 'subr-x)) ; For macros thread-{first,last}.
32
33 (require 'magit)
34
35 ;; For `magit-rebase--todo'.
36 (declare-function git-rebase-current-line "git-rebase" (&optional batch))
37
38 (eval-and-compile ;declare slot names
39 (cl-pushnew 'abbrev eieio--known-slot-names)
40 (cl-pushnew 'action eieio--known-slot-names)
41 (cl-pushnew 'action-options eieio--known-slot-names)
42 (cl-pushnew 'action-type eieio--known-slot-names)
43 (cl-pushnew 'target eieio--known-slot-names)
44 (cl-pushnew 'trailer eieio--known-slot-names))
45
46 ;;; Options
47 ;;;; Faces
48
49 (defface magit-sequence-pick
50 '((t :inherit default))
51 "Face used in sequence sections."
52 :group 'magit-faces)
53
54 (defface magit-sequence-stop
55 '((((class color) (background light)) :foreground "DarkOliveGreen4")
56 (((class color) (background dark)) :foreground "DarkSeaGreen2"))
57 "Face used in sequence sections."
58 :group 'magit-faces)
59
60 (defface magit-sequence-part
61 '((((class color) (background light)) :foreground "Goldenrod4")
62 (((class color) (background dark)) :foreground "LightGoldenrod2"))
63 "Face used in sequence sections."
64 :group 'magit-faces)
65
66 (defface magit-sequence-head
67 '((((class color) (background light)) :foreground "SkyBlue4")
68 (((class color) (background dark)) :foreground "LightSkyBlue1"))
69 "Face used in sequence sections."
70 :group 'magit-faces)
71
72 (defface magit-sequence-drop
73 '((((class color) (background light)) :foreground "IndianRed")
74 (((class color) (background dark)) :foreground "IndianRed"))
75 "Face used in sequence sections."
76 :group 'magit-faces)
77
78 (defface magit-sequence-done
79 '((t :inherit magit-hash))
80 "Face used in sequence sections."
81 :group 'magit-faces)
82
83 (defface magit-sequence-onto
84 '((t :inherit magit-sequence-done))
85 "Face used in sequence sections."
86 :group 'magit-faces)
87
88 (defface magit-sequence-exec
89 '((t :inherit magit-hash))
90 "Face used in sequence sections."
91 :group 'magit-faces)
92
93 ;;; Common
94
95 ;;;###autoload
96 (defun magit-sequencer-continue ()
97 "Resume the current cherry-pick or revert sequence."
98 (interactive)
99 (cond
100 ((not (magit-sequencer-in-progress-p))
101 (user-error "No cherry-pick or revert in progress"))
102 ((magit-anything-unmerged-p)
103 (user-error "Cannot continue due to unresolved conflicts"))
104 ((magit-run-git-sequencer
105 (if (magit-revert-in-progress-p) "revert" "cherry-pick") "--continue"))))
106
107 ;;;###autoload
108 (defun magit-sequencer-skip ()
109 "Skip the stopped at commit during a cherry-pick or revert sequence."
110 (interactive)
111 (unless (magit-sequencer-in-progress-p)
112 (user-error "No cherry-pick or revert in progress"))
113 (magit-call-git "reset" "--hard")
114 (magit-sequencer-continue))
115
116 ;;;###autoload
117 (defun magit-sequencer-abort ()
118 "Abort the current cherry-pick or revert sequence.
119 This discards all changes made since the sequence started."
120 (interactive)
121 (cond
122 ((not (magit-sequencer-in-progress-p))
123 (user-error "No cherry-pick or revert in progress"))
124 ((magit-revert-in-progress-p)
125 (magit-confirm 'abort-revert "Really abort revert")
126 (magit-run-git-sequencer "revert" "--abort"))
127 ((magit-confirm 'abort-cherry-pick "Really abort cherry-pick")
128 (magit-run-git-sequencer "cherry-pick" "--abort"))))
129
130 (defun magit-sequencer-in-progress-p ()
131 (or (magit-cherry-pick-in-progress-p)
132 (magit-revert-in-progress-p)))
133
134 ;;; Cherry-Pick
135
136 (defvar magit-perl-executable "perl"
137 "The Perl executable.")
138
139 ;;;###autoload(autoload 'magit-cherry-pick "magit-sequence" nil t)
140 (transient-define-prefix magit-cherry-pick ()
141 "Apply or transplant commits."
142 :man-page "git-cherry-pick"
143 :value '("--ff")
144 :incompatible '(("--ff" "-x"))
145 ["Arguments"
146 :if-not magit-sequencer-in-progress-p
147 (magit-cherry-pick:--mainline)
148 ("=s" magit-merge:--strategy)
149 ("-F" "Attempt fast-forward" "--ff")
150 ("-x" "Reference cherry in commit message" "-x")
151 ("-e" "Edit commit messages" ("-e" "--edit"))
152 (magit:--gpg-sign)
153 (magit:--signoff)]
154 [:if-not magit-sequencer-in-progress-p
155 ["Apply here"
156 ("A" "Pick" magit-cherry-copy)
157 ("a" "Apply" magit-cherry-apply)
158 ("h" "Harvest" magit-cherry-harvest)
159 ("m" "Squash" magit-merge-squash)]
160 ["Apply elsewhere"
161 ("d" "Donate" magit-cherry-donate)
162 ("n" "Spinout" magit-cherry-spinout)
163 ("s" "Spinoff" magit-cherry-spinoff)]]
164 ["Actions"
165 :if magit-sequencer-in-progress-p
166 ("A" "Continue" magit-sequencer-continue)
167 ("s" "Skip" magit-sequencer-skip)
168 ("a" "Abort" magit-sequencer-abort)])
169
170 (transient-define-argument magit-cherry-pick:--mainline ()
171 :description "Replay merge relative to parent"
172 :class 'transient-option
173 :shortarg "-m"
174 :argument "--mainline="
175 :reader #'transient-read-number-N+)
176
177 (defun magit-cherry-pick-read-args (prompt)
178 (list (or (nreverse (magit-region-values 'commit))
179 (magit-read-other-branch-or-commit prompt))
180 (transient-args 'magit-cherry-pick)))
181
182 (defun magit--cherry-move-read-args (verb away fn &optional allow-detached)
183 (declare (indent defun))
184 (let ((commits (or (nreverse (magit-region-values 'commit))
185 (list (funcall (if away
186 #'magit-read-branch-or-commit
187 #'magit-read-other-branch-or-commit)
188 (format "%s cherry" (capitalize verb))))))
189 (current (or (magit-get-current-branch)
190 (and allow-detached (magit-rev-parse "HEAD")))))
191 (unless current
192 (user-error "Cannot %s cherries while HEAD is detached" verb))
193 (let ((reachable (magit-rev-ancestor-p (car commits) current))
194 (msg "Cannot %s cherries that %s reachable from HEAD"))
195 (pcase (list away reachable)
196 ('(nil t) (user-error msg verb "are"))
197 ('(t nil) (user-error msg verb "are not"))))
198 `(,commits
199 ,@(funcall fn commits)
200 ,(transient-args 'magit-cherry-pick))))
201
202 (defun magit--cherry-spinoff-read-args (verb)
203 (magit--cherry-move-read-args verb t
204 (lambda (commits)
205 (magit-branch-read-args
206 (format "Create branch from %s cherries" (length commits))
207 (magit-get-upstream-branch)))))
208
209 ;;;###autoload
210 (defun magit-cherry-copy (commits &optional args)
211 "Copy COMMITS from another branch onto the current branch.
212 Prompt for a commit, defaulting to the commit at point. If
213 the region selects multiple commits, then pick all of them,
214 without prompting."
215 (interactive (magit-cherry-pick-read-args "Cherry-pick"))
216 (magit--cherry-pick commits args))
217
218 ;;;###autoload
219 (defun magit-cherry-apply (commits &optional args)
220 "Apply the changes in COMMITS but do not commit them.
221 Prompt for a commit, defaulting to the commit at point. If
222 the region selects multiple commits, then apply all of them,
223 without prompting."
224 (interactive (magit-cherry-pick-read-args "Apply changes from commit"))
225 (magit--cherry-pick commits (cons "--no-commit" (remove "--ff" args))))
226
227 ;;;###autoload
228 (defun magit-cherry-harvest (commits branch &optional args)
229 "Move COMMITS from another BRANCH onto the current branch.
230 Remove the COMMITS from BRANCH and stay on the current branch.
231 If a conflict occurs, then you have to fix that and finish the
232 process manually."
233 (interactive
234 (magit--cherry-move-read-args "harvest" nil
235 (lambda (commits)
236 (list (let ((branches (magit-list-containing-branches (car commits))))
237 (pcase (length branches)
238 (0 nil)
239 (1 (car branches))
240 (_ (magit-completing-read
241 (let ((len (length commits)))
242 (if (= len 1)
243 "Remove 1 cherry from branch"
244 (format "Remove %s cherries from branch" len)))
245 branches nil t))))))))
246 (magit--cherry-move commits branch (magit-get-current-branch) args nil t))
247
248 ;;;###autoload
249 (defun magit-cherry-donate (commits branch &optional args)
250 "Move COMMITS from the current branch onto another existing BRANCH.
251 Remove COMMITS from the current branch and stay on that branch.
252 If a conflict occurs, then you have to fix that and finish the
253 process manually. `HEAD' is allowed to be detached initially."
254 (interactive
255 (magit--cherry-move-read-args "donate" t
256 (lambda (commits)
257 (list (magit-read-other-branch
258 (let ((len (length commits)))
259 (if (= len 1)
260 "Move 1 cherry to branch"
261 (format "Move %s cherries to branch" len))))))
262 'allow-detached))
263 (magit--cherry-move commits
264 (or (magit-get-current-branch)
265 (magit-rev-parse "HEAD"))
266 branch args))
267
268 ;;;###autoload
269 (defun magit-cherry-spinout (commits branch start-point &optional args)
270 "Move COMMITS from the current branch onto a new BRANCH.
271 Remove COMMITS from the current branch and stay on that branch.
272 If a conflict occurs, then you have to fix that and finish the
273 process manually."
274 (interactive (magit--cherry-spinoff-read-args "spinout"))
275 (magit--cherry-move commits (magit-get-current-branch) branch args
276 start-point))
277
278 ;;;###autoload
279 (defun magit-cherry-spinoff (commits branch start-point &optional args)
280 "Move COMMITS from the current branch onto a new BRANCH.
281 Remove COMMITS from the current branch and checkout BRANCH.
282 If a conflict occurs, then you have to fix that and finish
283 the process manually."
284 (interactive (magit--cherry-spinoff-read-args "spinoff"))
285 (magit--cherry-move commits (magit-get-current-branch) branch args
286 start-point t))
287
288 (defun magit--cherry-move (commits src dst args
289 &optional start-point checkout-dst)
290 (let ((current (magit-get-current-branch)))
291 (unless (magit-branch-p dst)
292 (let ((magit-process-raise-error t))
293 (magit-call-git "branch" dst start-point))
294 (when$ (magit-get-indirect-upstream-branch start-point)
295 (magit-call-git "branch" "--set-upstream-to" $ dst)))
296 (unless (equal dst current)
297 (let ((magit-process-raise-error t))
298 (magit-call-git "checkout" dst)))
299 (if (not src) ; harvest only
300 (magit--cherry-pick commits args)
301 (let ((tip (car (last commits)))
302 (keep (concat (car commits) "^")))
303 (magit--cherry-pick commits args)
304 (set-process-sentinel
305 magit-this-process
306 (lambda (process event)
307 (when (memq (process-status process) '(exit signal))
308 (if (> (process-exit-status process) 0)
309 (magit-process-sentinel process event)
310 (process-put process 'inhibit-refresh t)
311 (magit-process-sentinel process event)
312 (cond
313 ((magit-rev-equal tip src)
314 (magit-call-git "update-ref"
315 "-m" (format "reset: moving to %s" keep)
316 (magit-ref-fullname src)
317 keep tip)
318 (if (not checkout-dst)
319 (magit-run-git "checkout" src)
320 (magit-refresh)))
321 (t
322 (magit-git "checkout" src)
323 (with-environment-variables
324 (("GIT_SEQUENCE_EDITOR"
325 (format "%s -i -ne '/^pick (%s)/ or print'"
326 magit-perl-executable
327 (mapconcat #'magit-rev-abbrev commits "|"))))
328 (magit-run-git-sequencer "rebase" "-i" keep))
329 (when checkout-dst
330 (set-process-sentinel
331 magit-this-process
332 (lambda (process event)
333 (when (memq (process-status process) '(exit signal))
334 (if (> (process-exit-status process) 0)
335 (magit-process-sentinel process event)
336 (process-put process 'inhibit-refresh t)
337 (magit-process-sentinel process event)
338 (magit-run-git "checkout" dst))))))))))))))))
339
340 (defun magit--cherry-pick (commits args &optional revert)
341 (let ((command (if revert "revert" "cherry-pick")))
342 (when (stringp commits)
343 (setq commits (if (string-search ".." commits)
344 (split-string commits "\\.\\.")
345 (list commits))))
346 (magit-run-git-sequencer
347 (if revert "revert" "cherry-pick")
348 (let ((merges (seq-filter #'magit-merge-commit-p commits)))
349 (cond
350 ((not merges)
351 (seq-remove (##string-prefix-p "--mainline=" %) args))
352 ((cl-set-difference commits merges :test #'equal)
353 (user-error "Cannot %s merge and non-merge commits at once"
354 command))
355 ((seq-find (##string-prefix-p "--mainline=" %) args)
356 args)
357 ((cons (format "--mainline=%s"
358 (read-number "Replay merges relative to parent: "))
359 args))))
360 commits)))
361
362 (defun magit-cherry-pick-in-progress-p ()
363 ;; .git/sequencer/todo does not exist when there is only one commit left.
364 (let ((dir (magit-gitdir)))
365 (or (file-exists-p (expand-file-name "CHERRY_PICK_HEAD" dir))
366 ;; And CHERRY_PICK_HEAD does not exist when a conflict happens
367 ;; while picking a series of commits with --no-commit.
368 (and$ (magit-file-line (expand-file-name "sequencer/todo" dir))
369 (string-prefix-p "pick" $)))))
370
371 ;;; Revert
372
373 ;;;###autoload(autoload 'magit-revert "magit-sequence" nil t)
374 (transient-define-prefix magit-revert ()
375 "Revert existing commits, with or without creating new commits."
376 :man-page "git-revert"
377 :value '("--edit")
378 ["Arguments"
379 :if-not magit-sequencer-in-progress-p
380 (magit-cherry-pick:--mainline)
381 ("-e" "Edit commit message" ("-e" "--edit"))
382 ("-E" "Don't edit commit message" "--no-edit")
383 ("=s" magit-merge:--strategy)
384 (magit:--gpg-sign)
385 (magit:--signoff)]
386 ["Actions"
387 :if-not magit-sequencer-in-progress-p
388 ("V" "Revert commit(s)" magit-revert-and-commit)
389 ("v" "Revert changes" magit-revert-no-commit)]
390 ["Actions"
391 :if magit-sequencer-in-progress-p
392 ("V" "Continue" magit-sequencer-continue)
393 ("s" "Skip" magit-sequencer-skip)
394 ("a" "Abort" magit-sequencer-abort)])
395
396 (defun magit-revert-read-args (prompt)
397 (list (or (magit-region-values 'commit)
398 (magit-read-branch-or-commit prompt))
399 (transient-args 'magit-revert)))
400
401 ;;;###autoload
402 (defun magit-revert-and-commit (commit &optional args)
403 "Revert COMMIT by creating a new commit.
404 Prompt for a commit, defaulting to the commit at point. If
405 the region selects multiple commits, then revert all of them,
406 without prompting."
407 (interactive (magit-revert-read-args "Revert commit"))
408 (magit--cherry-pick commit args t))
409
410 ;;;###autoload
411 (defun magit-revert-no-commit (commit &optional args)
412 "Revert COMMIT by applying it in reverse to the worktree.
413 Prompt for a commit, defaulting to the commit at point. If
414 the region selects multiple commits, then revert all of them,
415 without prompting."
416 (interactive (magit-revert-read-args "Revert changes"))
417 (magit--cherry-pick commit (cons "--no-commit" args) t))
418
419 (defun magit-revert-in-progress-p ()
420 ;; .git/sequencer/todo does not exist when there is only one commit left.
421 (let ((dir (magit-gitdir)))
422 (or (file-exists-p (expand-file-name "REVERT_HEAD" dir))
423 ;; And REVERT_HEAD does not exist when a conflict happens
424 ;; while reverting a series of commits with --no-commit.
425 (and$ (magit-file-line (expand-file-name "sequencer/todo" dir))
426 (string-prefix-p "revert" $)))))
427
428 ;;; Patch
429
430 ;;;###autoload(autoload 'magit-am "magit-sequence" nil t)
431 (transient-define-prefix magit-am ()
432 "Apply patches received by email."
433 :man-page "git-am"
434 :value '("--3way")
435 ["Arguments"
436 :if-not magit-am-in-progress-p
437 ("-3" "Fall back on 3way merge" ("-3" "--3way"))
438 ("-R" "Reject only failed hunks" "--reject")
439 (magit-apply:-p)
440 ("-c" "Remove text before scissors line" ("-c" "--scissors"))
441 ("-k" "Inhibit removal of email cruft" ("-k" "--keep"))
442 ("-b" "Limit removal of email cruft" "--keep-non-patch")
443 ("-d" "Use author date as committer date" "--committer-date-is-author-date")
444 ("-t" "Use current time as author date" "--ignore-date")
445 (magit:--gpg-sign)
446 (magit:--signoff)]
447 ["Apply"
448 :if-not magit-am-in-progress-p
449 ("m" "maildir" magit-am-apply-maildir)
450 ("w" "patches" magit-am-apply-patches)
451 ("a" "plain patch" magit-patch-apply)]
452 ["Actions"
453 :if magit-am-in-progress-p
454 ("w" "Continue" magit-am-continue)
455 ("s" "Skip" magit-am-skip)
456 ("a" "Abort" magit-am-abort)])
457
458 (defun magit-am-arguments ()
459 (transient-args 'magit-am))
460
461 (transient-define-argument magit-apply:-p ()
462 :description "Remove leading slashes from paths"
463 :class 'transient-option
464 :argument "-p"
465 :allow-empty t
466 :reader #'transient-read-number-N+)
467
468 ;;;###autoload
469 (defun magit-am-apply-patches (&optional files args)
470 "Apply the patches FILES."
471 (interactive (list (or (magit-region-values 'file)
472 (list (let ((default (magit-file-at-point)))
473 (read-file-name
474 (if default
475 (format "Apply patch (%s): " default)
476 "Apply patch: ")
477 nil default))))
478 (magit-am-arguments)))
479 (magit-run-git-sequencer "am" args "--"
480 (mapcar (##magit-convert-filename-for-git
481 (expand-file-name %))
482 files)))
483
484 ;;;###autoload
485 (defun magit-am-apply-maildir (&optional maildir args)
486 "Apply the patches from MAILDIR."
487 (interactive (list (read-file-name "Apply mbox or Maildir: ")
488 (magit-am-arguments)))
489 (magit-run-git-sequencer "am" args (magit-convert-filename-for-git
490 (expand-file-name maildir))))
491
492 ;;;###autoload
493 (defun magit-am-continue ()
494 "Resume the current patch applying sequence."
495 (interactive)
496 (cond
497 ((not (magit-am-in-progress-p))
498 (user-error "Not applying any patches"))
499 ((magit-anything-unstaged-p t)
500 (user-error "Cannot continue due to unstaged changes"))
501 ((magit-run-git-sequencer "am" "--continue"))))
502
503 ;;;###autoload
504 (defun magit-am-skip ()
505 "Skip the stopped at patch during a patch applying sequence."
506 (interactive)
507 (unless (magit-am-in-progress-p)
508 (user-error "Not applying any patches"))
509 (magit-run-git-sequencer "am" "--skip"))
510
511 ;;;###autoload
512 (defun magit-am-abort ()
513 "Abort the current patch applying sequence.
514 This discards all changes made since the sequence started."
515 (interactive)
516 (unless (magit-am-in-progress-p)
517 (user-error "Not applying any patches"))
518 (magit-run-git "am" "--abort"))
519
520 (defun magit-am-in-progress-p ()
521 (file-exists-p (expand-file-name "rebase-apply/applying" (magit-gitdir))))
522
523 ;;; Rebase
524
525 ;;;###autoload(autoload 'magit-rebase "magit-sequence" nil t)
526 (transient-define-prefix magit-rebase ()
527 "Transplant commits and/or modify existing commits."
528 :man-page "git-rebase"
529 :value '("--autostash")
530 ["Arguments"
531 :if-not magit-rebase-in-progress-p
532 ("-k" "Keep empty commits" "--keep-empty")
533 ("-p" "Preserve merges" ("-p" "--preserve-merges")
534 :if (##magit-git-version< "2.33.0"))
535 ("-r" "Rebase merges" ("-r" "--rebase-merges=")
536 magit-rebase-merges-select-mode)
537 ("-u" "Update branches" "--update-refs"
538 :if (##magit-git-version>= "2.38.0"))
539 (7 magit-merge:--strategy)
540 (7 magit-merge:--strategy-option)
541 (7 "=X" magit-diff:--diff-algorithm :argument "-Xdiff-algorithm=")
542 (7 "-f" "Force rebase" ("-f" "--force-rebase"))
543 ("-d" "Use author date as committer date" "--committer-date-is-author-date")
544 ("-t" "Use current time as author date" "--ignore-date")
545 ("-a" "Autosquash" "--autosquash")
546 ("-A" "Autostash" "--autostash")
547 ("-i" "Interactive" ("-i" "--interactive"))
548 ("-h" "Disable hooks" "--no-verify")
549 (7 magit-rebase:--exec)
550 (magit:--gpg-sign)
551 (magit:--signoff)]
552 [:if-not magit-rebase-in-progress-p
553 :description (##format (propertize "Rebase %s onto" 'face 'transient-heading)
554 (propertize (or (magit-get-current-branch) "HEAD")
555 'face 'magit-branch-local))
556 ("p" magit-rebase-onto-pushremote)
557 ("u" magit-rebase-onto-upstream)
558 ("e" "elsewhere" magit-rebase-branch)]
559 ["Rebase"
560 :if-not magit-rebase-in-progress-p
561 [("i" "interactively" magit-rebase-interactive)
562 ("s" "a subset" magit-rebase-subset)]
563 [("m" "to modify a commit" magit-rebase-edit-commit)
564 ("w" "to reword a commit" magit-rebase-reword-commit)
565 ("k" "to remove a commit" magit-rebase-remove-commit)]
566 [("f" "to autosquash" magit-rebase-autosquash)
567 (6 "t" "to change dates" magit-reshelve-since)]]
568 ["Actions"
569 :if magit-rebase-in-progress-p
570 ("r" "Continue" magit-rebase-continue)
571 ("s" "Skip" magit-rebase-skip)
572 ("e" "Edit" magit-rebase-edit)
573 ("a" "Abort" magit-rebase-abort)])
574
575 (transient-define-argument magit-rebase:--exec ()
576 :description "Run command after commits"
577 :class 'transient-option
578 :shortarg "-x"
579 :argument "--exec="
580 :reader #'read-shell-command)
581
582 (defun magit-rebase-merges-select-mode (&rest _ignore)
583 (magit-read-char-case nil t
584 (?n "[n]o-rebase-cousins" "no-rebase-cousins")
585 (?r "[r]ebase-cousins" "rebase-cousins")))
586
587 (defun magit-rebase-arguments ()
588 (transient-args 'magit-rebase))
589
590 (defun magit-git-rebase (target args)
591 (magit-run-git-sequencer "rebase" args target))
592
593 ;;;###autoload(autoload 'magit-rebase-onto-pushremote "magit-sequence" nil t)
594 (transient-define-suffix magit-rebase-onto-pushremote (args)
595 "Rebase the current branch onto its push-remote branch.
596
597 With a prefix argument or when the push-remote is either not
598 configured or unusable, then let the user first configure the
599 push-remote."
600 :if #'magit-get-current-branch
601 :description #'magit-pull--pushbranch-description
602 (interactive (list (magit-rebase-arguments)))
603 (pcase-let ((`(,branch ,remote)
604 (magit--select-push-remote "rebase onto that")))
605 (magit-git-rebase (concat remote "/" branch) args)))
606
607 ;;;###autoload(autoload 'magit-rebase-onto-upstream "magit-sequence" nil t)
608 (transient-define-suffix magit-rebase-onto-upstream (args)
609 "Rebase the current branch onto its upstream branch.
610
611 With a prefix argument or when the upstream is either not
612 configured or unusable, then let the user first configure
613 the upstream."
614 :if #'magit-get-current-branch
615 :description #'magit-rebase--upstream-description
616 (interactive (list (magit-rebase-arguments)))
617 (let* ((branch (or (magit-get-current-branch)
618 (user-error "No branch is checked out")))
619 (upstream (magit-get-upstream-branch branch)))
620 (when (or current-prefix-arg (not upstream))
621 (setq upstream
622 (magit-read-upstream-branch
623 branch (format "Set upstream of %s and rebase onto that" branch)))
624 (magit-set-upstream-branch branch upstream))
625 (magit-git-rebase upstream args)))
626
627 (defun magit-rebase--upstream-description ()
628 (and-let ((branch (magit-get-current-branch)))
629 (or (magit-get-upstream-branch branch)
630 (let ((remote (magit-get "branch" branch "remote"))
631 (merge (magit-get "branch" branch "merge"))
632 (u (magit--propertize-face "@{upstream}" 'bold)))
633 (cond
634 ((magit--unnamed-upstream-p remote merge)
635 (concat u ", replacing unnamed"))
636 ((magit--valid-upstream-p remote merge)
637 (concat u ", replacing non-existent"))
638 ((or remote merge)
639 (concat u ", replacing invalid"))
640 ((concat u ", setting that")))))))
641
642 ;;;###autoload
643 (defun magit-rebase-branch (target args)
644 "Rebase the current branch onto a branch read in the minibuffer.
645 All commits that are reachable from `HEAD' but not from the
646 selected branch TARGET are being rebased."
647 (interactive (list (magit-read-other-branch-or-commit "Rebase onto")
648 (magit-rebase-arguments)))
649 (message "Rebasing...")
650 (magit-git-rebase target args)
651 (message "Rebasing...done"))
652
653 ;;;###autoload
654 (defun magit-rebase-subset (newbase start args)
655 "Rebase a subset of the current branch's history onto a new base.
656 Rebase commits from START to `HEAD' onto NEWBASE.
657 START has to be selected from a list of recent commits."
658 (interactive (list (magit-read-other-branch-or-commit
659 "Rebase subset onto" nil
660 (magit-get-upstream-branch))
661 nil
662 (magit-rebase-arguments)))
663 (cond (start
664 (message "Rebasing...")
665 (magit-run-git-sequencer "rebase" "--onto" newbase start args)
666 (message "Rebasing...done"))
667 ((magit-log-select
668 `(lambda (commit)
669 (magit-rebase-subset ,newbase (concat commit "^") (list ,@args)))
670 (concat "Type %p on a commit to rebase it "
671 "and commits above it onto " newbase ",")))))
672
673 (defun magit-rebase-interactive-1
674 ( commit args message
675 &optional editor delay-edit-confirm noassert confirm exact)
676 (declare (indent 2))
677 (cond ((not commit))
678 ((not (magit-rev-ancestor-p commit "HEAD"))
679 (user-error "%s isn't an ancestor of HEAD" commit))
680 ((not (magit-commit-parents commit))
681 (setq args (cons "--root" args)))
682 ((not exact)
683 (setq commit (concat commit "^"))))
684 (when (and commit (not noassert))
685 (setq commit (magit-rebase-interactive-assert
686 commit delay-edit-confirm
687 (seq-some (##string-prefix-p "--rebase-merges" %) args))))
688 (if (and commit (not confirm))
689 ;; Use a copy because the old value may contain GIT_SEQUENCE_EDITOR.
690 (let ((process-environment (copy-sequence process-environment)))
691 (when editor
692 (setenv "GIT_SEQUENCE_EDITOR"
693 (if (functionp editor)
694 (funcall editor commit)
695 editor)))
696 (magit-run-git-sequencer "rebase" "-i" args
697 (and (not (member "--root" args)) commit)))
698 (magit-log-select
699 `(lambda (commit)
700 ;; In some cases (currently just magit-rebase-remove-commit), "-c
701 ;; commentChar=#" is added to the global arguments for git. Ensure
702 ;; that the same happens when we chose the commit via
703 ;; magit-log-select, below.
704 (let ((magit-git-global-arguments (list ,@magit-git-global-arguments)))
705 (magit-rebase-interactive-1 commit (list ,@args)
706 ,message ,editor ,delay-edit-confirm ,noassert)))
707 message)))
708
709 (defvar magit--rebase-published-symbol nil)
710 (defvar magit--rebase-public-edit-confirmed nil)
711
712 (defun magit-rebase-interactive-assert
713 (since &optional delay-edit-confirm rebase-merges)
714 (let* ((commit (magit-rebase--target-commit since))
715 (branches (magit-list-publishing-branches commit)))
716 (setq magit--rebase-public-edit-confirmed
717 (delete (magit-toplevel) magit--rebase-public-edit-confirmed))
718 (when (and branches
719 (or (not delay-edit-confirm)
720 ;; The user might have stopped at a published commit
721 ;; merely to add new commits *after* it. Try not to
722 ;; ask users whether they really want to edit public
723 ;; commits, when they don't actually intend to do so.
724 (not (all (##magit-rev-equal % commit) branches))))
725 (let ((m1 "Some of these commits have already been published to ")
726 (m2 ".\nDo you really want to modify them"))
727 (magit-confirm (or magit--rebase-published-symbol 'rebase-published)
728 (concat m1 "%s" m2)
729 (concat m1 "%d public branches" m2)
730 nil branches))
731 (push (magit-toplevel) magit--rebase-public-edit-confirmed)))
732 (if (and (magit-git-lines "rev-list" "--merges" (concat since "..HEAD"))
733 (not rebase-merges))
734 (magit-read-char-case "Proceed despite merge in rebase range? " nil
735 (?c "[c]ontinue" since)
736 (?s "[s]elect other" nil)
737 (?a "[a]bort" (user-error "Quit")))
738 since))
739
740 (defun magit-rebase--target-commit (since)
741 (if (string-suffix-p "^" since)
742 ;; If SINCE is "REV^", then the user selected
743 ;; "REV", which is the first commit that will
744 ;; be replaced. (from^..to] <=> [from..to]
745 (substring since 0 -1)
746 ;; The "--root" argument is being used.
747 since))
748
749 (defvar magit-rebase-interactive-include-selected t)
750
751 ;;;###autoload
752 (defun magit-rebase-interactive (commit args)
753 "Start an interactive rebase sequence."
754 (interactive (list (magit-commit-at-point)
755 (magit-rebase-arguments)))
756 (magit-rebase-interactive-1 commit args
757 "Type %p on a commit to rebase it and all commits above it,"
758 nil t nil nil
759 (not magit-rebase-interactive-include-selected)))
760
761 ;;;###autoload
762 (defun magit-rebase-autosquash (select args)
763 "Combine squash and fixup commits with their intended targets.
764 By default only squash into commits that are not reachable from
765 the upstream branch. If no upstream is configured or with a prefix
766 argument, prompt for the first commit to potentially squash into."
767 (interactive (list current-prefix-arg
768 (magit-rebase-arguments)))
769 (magit-rebase-interactive-1
770 (and-let ((_(not select))
771 (upstream (magit-get-upstream-branch)))
772 (magit-merge-base upstream "HEAD"))
773 (nconc (list "--autosquash" "--keep-empty") args)
774 "Type %p on a commit to squash into it and then rebase as necessary,"
775 "true" nil t nil t))
776
777 ;;;###autoload
778 (defun magit-rebase-edit-commit (commit args)
779 "Edit a single older commit using rebase."
780 (interactive (list (magit-commit-at-point)
781 (magit-rebase-arguments)))
782 (magit-rebase-interactive-1 commit args
783 "Type %p on a commit to edit it,"
784 (apply-partially #'magit-rebase--perl-editor 'edit)
785 t))
786
787 ;;;###autoload
788 (defun magit-rebase-reword-commit (commit args)
789 "Reword a single older commit using rebase."
790 (interactive (list (magit-commit-at-point)
791 (magit-rebase-arguments)))
792 (magit-rebase-interactive-1 commit args
793 "Type %p on a commit to reword its message,"
794 (apply-partially #'magit-rebase--perl-editor 'reword)))
795
796 ;;;###autoload
797 (defun magit-rebase-remove-commit (commit args)
798 "Remove a single older commit using rebase."
799 (interactive (list (magit-commit-at-point)
800 (magit-rebase-arguments)))
801 ;; magit-rebase--perl-editor assumes that the comment character is "#".
802 (let ((magit-git-global-arguments
803 (nconc (list "-c" "core.commentChar=#")
804 magit-git-global-arguments)))
805 (magit-rebase-interactive-1 commit args
806 "Type %p on a commit to remove it,"
807 (apply-partially #'magit-rebase--perl-editor 'remove)
808 nil nil t)))
809
810 (defun magit-rebase--perl-editor (action since)
811 (let ((commit (magit-rev-abbrev (magit-rebase--target-commit since))))
812 (format "%s -i -p -e '++$x if not $x and s/^pick %s/%s %s/'"
813 magit-perl-executable
814 commit
815 (cl-case action
816 (edit "edit")
817 (remove "noop\n# pick")
818 (reword "reword")
819 (t (error "Unknown action: %s" action)))
820 commit)))
821
822 ;;;###autoload
823 (defun magit-rebase-continue (&optional noedit)
824 "Restart the current rebasing operation.
825 In some cases this pops up a commit message buffer for you do
826 edit. With a prefix argument the old message is reused as-is."
827 (interactive "P")
828 (cond
829 ((not (magit-rebase-in-progress-p))
830 (user-error "No rebase in progress"))
831 ((magit-anything-unstaged-p t)
832 (user-error "Cannot continue rebase with unstaged changes"))
833 (t
834 (let ((dir (magit-gitdir)))
835 (when (and (magit-anything-staged-p)
836 (file-exists-p (expand-file-name "rebase-merge" dir))
837 (not (member (magit-toplevel)
838 magit--rebase-public-edit-confirmed)))
839 (magit-commit-amend-assert
840 (magit-file-line (expand-file-name "rebase-merge/orig-head" dir)))))
841 (if noedit
842 (with-environment-variables (("GIT_EDITOR" "true"))
843 (magit-run-git-async (magit--rebase-resume-command) "--continue")
844 (set-process-sentinel magit-this-process
845 #'magit-sequencer-process-sentinel)
846 magit-this-process)
847 (magit-run-git-sequencer (magit--rebase-resume-command) "--continue")))))
848
849 ;;;###autoload
850 (defun magit-rebase-skip ()
851 "Skip the current commit and restart the current rebase operation."
852 (interactive)
853 (unless (magit-rebase-in-progress-p)
854 (user-error "No rebase in progress"))
855 (magit-run-git-sequencer (magit--rebase-resume-command) "--skip"))
856
857 ;;;###autoload
858 (defun magit-rebase-edit ()
859 "Edit the todo list of the current rebase operation."
860 (interactive)
861 (unless (magit-rebase-in-progress-p)
862 (user-error "No rebase in progress"))
863 (magit-run-git-sequencer "rebase" "--edit-todo"))
864
865 ;;;###autoload
866 (defun magit-rebase-abort ()
867 "Abort the current rebase operation, restoring the original branch."
868 (interactive)
869 (unless (magit-rebase-in-progress-p)
870 (user-error "No rebase in progress"))
871 (magit-confirm 'abort-rebase "Abort this rebase")
872 (magit-run-git (magit--rebase-resume-command) "--abort"))
873
874 (defun magit-rebase-in-progress-p ()
875 "Return t if a rebase is in progress."
876 (let ((dir (magit-gitdir)))
877 (or (file-exists-p (expand-file-name "rebase-merge" dir))
878 (file-exists-p (expand-file-name "rebase-apply/onto" dir)))))
879
880 (defun magit--rebase-resume-command ()
881 (if (file-exists-p (expand-file-name "rebase-recursive" (magit-gitdir)))
882 "rbr"
883 "rebase"))
884
885 (defun magit-rebase--get-state-lines (file)
886 (and (magit-rebase-in-progress-p)
887 (let ((dir (magit-gitdir)))
888 (magit-file-line
889 (expand-file-name
890 (concat (if (file-directory-p (expand-file-name "rebase-merge" dir))
891 "rebase-merge/"
892 "rebase-apply/")
893 file)
894 dir)))))
895
896 ;;; Sections
897
898 (defun magit-insert-sequencer-sequence ()
899 "Insert section for the on-going cherry-pick or revert sequence.
900 If no such sequence is in progress, do nothing."
901 (let ((picking (magit-cherry-pick-in-progress-p)))
902 (when (or picking (magit-revert-in-progress-p))
903 (let ((dir (magit-gitdir)))
904 (magit-insert-section (sequence)
905 (magit-insert-heading (if picking "Cherry Picking" "Reverting"))
906 (when-let ((lines (cdr (magit-file-lines
907 (expand-file-name "sequencer/todo" dir)))))
908 (dolist (line (nreverse lines))
909 (when (string-match
910 "^\\(pick\\|revert\\) \\([^ ]+\\) \\(.*\\)$" line)
911 (magit-bind-match-strings (cmd hash msg) line
912 (magit-insert-section (commit hash)
913 (insert (propertize cmd 'font-lock-face 'magit-sequence-pick)
914 " " (propertize hash 'font-lock-face 'magit-hash)
915 " " msg "\n"))))))
916 (magit-sequence-insert-sequence
917 (magit-file-line
918 (expand-file-name (if picking "CHERRY_PICK_HEAD" "REVERT_HEAD")
919 dir))
920 (magit-file-line (expand-file-name "sequencer/head" dir)))
921 (insert "\n"))))))
922
923 (defun magit-insert-am-sequence ()
924 "Insert section for the on-going patch applying sequence.
925 If no such sequence is in progress, do nothing."
926 (when (magit-am-in-progress-p)
927 (magit-insert-section (rebase-sequence)
928 (magit-insert-heading "Applying patches")
929 (let* ((patches (nreverse (magit-rebase-patches)))
930 (dir (expand-file-name "rebase-apply" (magit-gitdir)))
931 (i (string-to-number
932 (magit-file-line (expand-file-name "last" dir))))
933 (cur (string-to-number
934 (magit-file-line (expand-file-name "next" dir))))
935 patch commit)
936 (while (and patches (>= i cur))
937 (setq patch (pop patches))
938 (setq commit
939 (magit-commit-oid (cadr (split-string (magit-file-line patch)))
940 t))
941 (cond ((and commit (= i cur))
942 (magit-sequence-insert-commit
943 "stop" commit 'magit-sequence-stop))
944 ((= i cur)
945 (magit-sequence-insert-am-patch
946 "stop" patch 'magit-sequence-stop))
947 (commit
948 (magit-sequence-insert-commit
949 "pick" commit 'magit-sequence-pick))
950 ((magit-sequence-insert-am-patch
951 "pick" patch 'magit-sequence-pick)))
952 (decf i)))
953 (magit-sequence-insert-sequence nil "ORIG_HEAD")
954 (insert ?\n))))
955
956 (defun magit-sequence-insert-am-patch (type patch face)
957 (magit-insert-section (file patch)
958 (let ((title
959 (with-temp-buffer
960 (insert-file-contents patch nil nil 4096)
961 (unless (re-search-forward "^Subject: " nil t)
962 (goto-char (point-min)))
963 (buffer-substring (point) (line-end-position)))))
964 (insert (propertize type 'font-lock-face face)
965 ?\s (propertize (file-name-nondirectory patch)
966 'font-lock-face 'magit-hash)
967 ?\s title
968 ?\n))))
969
970 (defun magit-insert-rebase-sequence ()
971 "Insert section for the on-going rebase sequence.
972 If no such sequence is in progress, do nothing."
973 (when (magit-rebase-in-progress-p)
974 (let* ((gitdir (magit-gitdir))
975 (mergep (file-directory-p (expand-file-name "rebase-merge" gitdir)))
976 (dir (if mergep "rebase-merge/" "rebase-apply/"))
977 (name (thread-first (concat dir "head-name")
978 (expand-file-name gitdir)
979 magit-file-line))
980 (onto (thread-first (concat dir "onto")
981 (expand-file-name gitdir)
982 magit-file-line))
983 (onto (or (magit-rev-name onto name)
984 (magit-rev-name onto "refs/heads/*") onto))
985 (name (or (magit-rev-name name "refs/heads/*") name)))
986 (magit-insert-section (rebase-sequence)
987 (magit-insert-heading (format "Rebasing %s onto %s" name onto))
988 (if mergep
989 (magit-rebase-insert-merge-sequence onto)
990 (magit-rebase-insert-apply-sequence onto))
991 (insert ?\n)))))
992
993 (defun magit-rebase--todo ()
994 "Return `git-rebase-action' instances for remaining rebase actions.
995 These are ordered the same way they'll be sorted in the status
996 buffer (i.e., the reverse of how they will be applied)."
997 (let ((comment-start (or (magit-get "core.commentChar") "#"))
998 (commits ())
999 (actions ()))
1000 (with-temp-buffer
1001 (insert-file-contents
1002 (expand-file-name "rebase-merge/git-rebase-todo" (magit-gitdir)))
1003 (while (not (eobp))
1004 (when-let ((obj (git-rebase-current-line t)))
1005 (push obj actions)
1006 (when (memq (oref obj action-type) '(commit merge))
1007 (push obj commits)))
1008 (forward-line)))
1009 (let ((abbrevs
1010 (and commits
1011 (magit-git-lines
1012 "log" "--no-walk=unsorted" "--format=%h"
1013 (mapcar (lambda (obj)
1014 (if (eq (oref obj action-type) 'merge)
1015 (let ((options (oref obj action-options)))
1016 (and (string-match "-[cC] \\([^ ]+\\)" options)
1017 (match-str 1 options)))
1018 (oref obj target)))
1019 commits)))))
1020 (cl-assert (equal (length commits) (length abbrevs)))
1021 (while-let ((obj (pop commits))
1022 (val (pop abbrevs)))
1023 (oset obj abbrev val)))
1024 actions))
1025
1026 (defun magit-rebase-insert-merge-sequence (onto)
1027 (dolist (obj (magit-rebase--todo))
1028 (with-slots (action-type action action-options target abbrev trailer) obj
1029 (pcase action-type
1030 ((or 'commit (and 'merge (guard abbrev)))
1031 (magit-sequence-insert-commit action target 'magit-sequence-pick
1032 abbrev trailer))
1033 ((guard action) (magit-sequence-insert-step action target)))))
1034 (let ((dir (magit-gitdir)))
1035 (magit-sequence-insert-sequence
1036 (magit-file-line (expand-file-name "rebase-merge/stopped-sha" dir))
1037 onto
1038 (and$ (magit-file-lines (expand-file-name "rebase-merge/done" dir))
1039 (cadr (split-string (car (last $))))))))
1040
1041 (defun magit-rebase-insert-apply-sequence (onto)
1042 (let* ((dir (magit-gitdir))
1043 (rewritten
1044 (mapcar (##car (split-string %))
1045 (magit-file-lines
1046 (expand-file-name "rebase-apply/rewritten" dir))))
1047 (stop (magit-file-line
1048 (expand-file-name "rebase-apply/original-commit" dir))))
1049 (dolist (patch (nreverse (cdr (magit-rebase-patches))))
1050 (let ((hash (cadr (split-string (magit-file-line patch)))))
1051 (unless (or (member hash rewritten)
1052 (equal hash stop))
1053 (magit-sequence-insert-commit "pick" hash 'magit-sequence-pick))))
1054 (magit-sequence-insert-sequence
1055 (magit-file-line (expand-file-name "rebase-apply/original-commit" dir))
1056 onto)))
1057
1058 (defun magit-rebase-patches ()
1059 (directory-files (expand-file-name "rebase-apply" (magit-gitdir))
1060 t "\\`[0-9]\\{4\\}\\'"))
1061
1062 (defun magit-sequence-insert-sequence (stop onto &optional orig)
1063 (let ((head (magit-rev-parse "HEAD")) done)
1064 (setq onto (if onto (magit-rev-parse onto) head))
1065 (setq done (mapcar (##split-string % "\0")
1066 (magit-git-lines "log" "--format=%H%x00%h%x00%s"
1067 (concat onto "..HEAD"))))
1068 (when (and stop (not (assoc (magit-rev-parse stop) done)))
1069 (let ((id (magit-patch-id stop)))
1070 (if-let ((matched (car (assoc (##equal (magit-patch-id %) id) done))))
1071 (setq stop matched)
1072 (cond
1073 ((assoc (##magit-rev-equal % stop) done)
1074 ;; The commit's testament has been executed.
1075 (magit-sequence-insert-commit "void" stop 'magit-sequence-drop))
1076 ;; The faith of the commit is still undecided...
1077 ((magit-anything-unmerged-p)
1078 ;; ...and time travel isn't for the faint of heart.
1079 (magit-sequence-insert-commit "join" stop 'magit-sequence-part))
1080 ((magit-anything-modified-p t)
1081 ;; ...and the dust hasn't settled yet...
1082 (magit-sequence-insert-commit
1083 (let* ((magit--refresh-cache nil)
1084 (staged (magit-commit-tree "oO" nil "HEAD"))
1085 (unstaged (magit-commit-worktree "oO" "--reset")))
1086 (cond
1087 ;; ...but we could end up at the same tree just by committing.
1088 ((or (magit-rev-equal staged stop)
1089 (magit-rev-equal unstaged stop))
1090 "goal")
1091 ;; ...but the changes are still there, untainted.
1092 ((or (equal (magit-patch-id staged) id)
1093 (equal (magit-patch-id unstaged) id))
1094 "same")
1095 ;; ...and some changes are gone and/or others were added.
1096 ("work")))
1097 stop 'magit-sequence-part))
1098 ;; The commit is definitely gone...
1099 ((assoc (##magit-rev-equal % stop) done)
1100 ;; ...but all of its changes are still in effect.
1101 (magit-sequence-insert-commit "poof" stop 'magit-sequence-drop))
1102 ;; ...and some changes are gone and/or other changes were added.
1103 ((magit-sequence-insert-commit "gone" stop 'magit-sequence-drop)))
1104 (setq stop nil))))
1105 (pcase-dolist (`(,rev ,abbrev ,msg) done)
1106 (apply #'magit-sequence-insert-commit
1107 (cond ((equal rev stop)
1108 ;; ...but its reincarnation lives on.
1109 ;; Or it didn't die in the first place.
1110 (list (if (and (equal rev head)
1111 (equal (magit-patch-id rev)
1112 (magit-patch-id orig)))
1113 "stop" ; We haven't done anything yet.
1114 "like") ; There are new commits.
1115 rev (if (equal rev head)
1116 'magit-sequence-head
1117 'magit-sequence-stop)
1118 abbrev msg))
1119 ((equal rev head)
1120 (list "done" rev 'magit-sequence-head abbrev msg))
1121 ((list "done" rev 'magit-sequence-done abbrev msg)))))
1122 (magit-sequence-insert-commit "onto" onto
1123 (if (equal onto head)
1124 'magit-sequence-head
1125 'magit-sequence-onto))))
1126
1127 (defun magit-sequence-insert-commit (type hash face &optional abbrev msg)
1128 (magit-insert-section (commit hash)
1129 (magit-insert-heading
1130 (magit--propertize-face type face) " "
1131 (if abbrev
1132 (concat (magit--propertize-face abbrev 'magit-hash) " " msg "\n")
1133 (concat (magit-format-rev-summary hash) "\n")))))
1134
1135 (defun magit-sequence-insert-step (type target)
1136 (magit-insert-section (rebase-step (cons type target))
1137 (magit-insert-heading
1138 (propertize type 'font-lock-face 'magit-sequence-pick)
1139 (and target
1140 (concat "\s"
1141 (propertize target 'font-lock-face 'git-rebase-label))))))
1142
1143 ;;; _
1144 (provide 'magit-sequence)
1145 ;; Local Variables:
1146 ;; read-symbol-shorthands: (
1147 ;; ("and$" . "cond-let--and$")
1148 ;; ("thread$" . "cond-let--thread$")
1149 ;; ("when$" . "cond-let--when$")
1150 ;; ("and-let*" . "cond-let--and-let*")
1151 ;; ("and-let" . "cond-let--and-let")
1152 ;; ("if-let*" . "cond-let--if-let*")
1153 ;; ("if-let" . "cond-let--if-let")
1154 ;; ("when-let*" . "cond-let--when-let*")
1155 ;; ("when-let" . "cond-let--when-let")
1156 ;; ("while-let*" . "cond-let--while-let*")
1157 ;; ("while-let" . "cond-let--while-let")
1158 ;; ("match-string" . "match-string")
1159 ;; ("match-str" . "match-string-no-properties"))
1160 ;; End:
1161 ;;; magit-sequence.el ends here