As per the doc, among the three sequencer subcommands for cherry-pick
, we have these two which are, to me, oddly similar :
--quit
Forget about the current operation in progress. Can be used to clear the sequencer state after a failed cherry-pick or revert.
--abort
Cancel the operation and return to the pre-sequence state.
So far I've always used --abort
, and it works great. What would be a use-case where --quit
is different/preferable?
Try also with '--quit' option, which allows you to abort the current operation and further clear the sequencer state. --quit Forget about the current operation in progress. Can be used to clear the sequencer state after a failed cherry-pick or revert. --abort Cancel the operation and return to the pre-sequence state.
rebase moves commits from the current branch to another branch. cherry-pick copies commits from another branch to the current branch.
If you remember that git rebase
is a sequence of git cherry-pick
operations,1 plus a couple of useful gimmicks at the start and end, it makes more sense.
Imagine you have the following series of commits:
...--o--*--...--o <-- mainline
\
A--B--C <-- feature
You want to bring feature
up to date with mainline
so you:
git checkout feature
git rebase mainline
Git starts out by enumerating the commits reachable from feature
(C
, B
, A
, *
, ...) and those reachable from mainline
(unnamed, ..., *
, ...). It subtracts the mainline
set from the feature
set2 and uses the reverse topological order so that it now has hash IDs A
, B
, and C
listed in the thing that Git calls the sequencer. (The sequencer also records the operation, in this case rebase / cherry-pick. The sequencer is also used for multiple reverts. The sequencer can stop partway through, then be resumed with --continue
. This is why it needs to know the operation: are we continuing a cherry-pick, a revert, or a rebase?)
Git then detaches HEAD
at the commit at the tip of mainline
and runs the op, whatever it is, via the sequencer. Since the op is "rebase", each step of the sequencer is a simple one-commit cherry-pick:
(with)
...--o--*--...--o <-- mainline, HEAD
\
A--B--C <-- feature
(execute git cherry-pick A to produce)
A' <-- HEAD
/
...--o--*--...--o <-- mainline
\
A--B--C <-- feature
This can fail with a merge conflict. If so, the sequencer halts, leaving you a mess of merge conflicts in your index and work-tree. You can fix them and resume the sequencer (which will commit for you to make A'
if needed, if you did not make A'
yourself), or choose one of the two kinds of stop, "abort" or "quit". If you resume—or if things went well—we go on to (attempt to) cherry-pick B
:
A'-B' <-- HEAD
/
...--o--*--...--o <-- mainline
\
A--B--C <-- feature
Let's say that also succeeds and we go on to attempt to cherry-pick C
but this attempt fails. The sequencer stops, leaving you a mess in your index and work-tree, with the same graph we see above: A'
and B'
exist but C'
does not.
Let's say we decide to stop: that completing the cherry-pick of C
is too hard for the moment and we need to back off and do something else for a bit. You now have the two options, abort or quit.
If you choose --abort
, Git re-attaches your HEAD
to feature
, giving:
...--o--*--...--o <-- mainline
\
A--B--C <-- feature (HEAD)
Where are A'
and B'
? Well, if you know what you're doing, you can fish them out of the reflogs, or you already cleverly attached a branch or tag name to B'
before choosing --abort
. But if you choose --quit
, Git terminates the rebase without moving HEAD
, so that you wind up with:
A'-B' <-- HEAD
/
...--o--*--...--o <-- mainline
\
A--B--C <-- feature
but can get yourself a clean index and work-tree with git reset --hard
now. (Or, you can leave the mess in place.) So you don't have to be clever enough to attach a branch name before the --quit
.
That's basically all there is to it. :-) But after a long and frustrating rebase with a lot of conflicts, where you want to save what you've achieved so far and go back to non-rebase work before coming back at the rebase again later, the "quit" variant feels more satisfying somehow.
(I think what's really missing here is the option to save the remainder of the sequencer state, and restore it later. However, the sequencer state is per-worktree, so if you have an ongoing rebase task and have to interrupt it with a higher priority task, you can just add a work-tree for the higher priority task. The various bugs in added work-trees through Git 2.15 is not all that confidence-inspiring, but they do seem to behave well now. Added work-trees also paper over the other, bigger missing piece, which is the ability to save an in-progress conflicted merge and restore it later.)
1Note, however, that the old-style non-interactive git-rebase--am
still uses git format-patch
and git am
. This process does not work as well in some cases with renamed files and cannot copy a "makes no changes" commit, but does run faster. In most cases, both this and the cherry-pick style should give the same results, despite the change in underlying mechanism, especially since the cherry-pick variant defaults to not copying a "makes no changes" commit.
2The rebase also subtracts away any commits that exist in the mainline
set and have the same git patch-id
as any commits in the feature
set, and of course, by default, it subtracts away all merges.
--abort
will take you back to where you where before you started the cherry-pick operation whereas --quit
will get out of the operation and keep you at the revision you are currently on. I can't think of a use case other than you wanting to go on a different direction after you hit a revision that could not be automagically cherry-picked, which is why cherry-pick stopped in the first place, right?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With