What difference does `.git/CHERRY_PICK_HEAD` make when committing?
Asked Answered
M

2

6

NB: This is a rewording of an earlier post of mine (now deleted). The rewording intends to give the post a different focus.


Upon running git cherry-pick, git reported that there was a conflict. I resolved the conflict, and then ran git cherry-pick --continue. At this point, $GIT_EDITOR popped up a COMMIT_EDITMSG buffer pre-populated with the cherry-picked commit's original message along with some additional info, which included the warning:

# It looks like you may be committing a cherry-pick.
# If this is not correct, please remove the file
#   .git/CHERRY_PICK_HEAD
# and try again.

I checked to see what happened if I "deleted" (actually, just temporarily renamed) the .git/CHERRY_PICK_HEAD file. The immediate outward effect this had was to remove the |CHERRY-PICKING indication from my git-aware prompt.

Other than this change in my prompt, and possibly some differences in the pre-populated information in the COMMIT_EDITMSG buffer, what difference would it make to perform the commit with or without the .git/CHERRY_PICK_HEAD file in place?


To be more precise, I'm trying to compare two scenarios here.

In the first scenario, I run

% git cherry-pick --continue

...and (disregarding the warning quoted earlier) I proceed with the commit as usual.

In the second scenario, I run

% rm .git/CHERRY_PICK_HEAD
% git commit

...and proceed with the commit as usual.

(Assume that I use the same commit message in both scenarios.)

How would the end results of these two scenarios differ?

Millimeter answered 7/2, 2017 at 12:35 Comment(0)
P
7

The answer depends on what you were doing. Using --continue finishes the sequence—but if the sequence was just the one cherry-pick, there wasn't really a sequence anyway.

Either way, however, removing the .git/CHERRY_PICK_HEAD definitely has one additional significant effect: finishing a conflicted cherry-pick re-uses the original commit's author-name-email-and-date information. You are always the committer of any new commit, but all commits have not just one person-and-timestamp: each new commit has two entries, one for "committer" (you, making the commit, just now) and one for "author" (whoever wrote the original commit, and whenever they did that). Cherry-pick preserves the authorship information from the original commit.

The sequencer

Both git cherry-pick and git revert—which are actually the same command internally; revert just "works backwards"—use what Git calls the sequencer. That is, you can pick multiple commits all at once:

git cherry-pick notthis..that thistoo

to cherry pick all commits "after" notthis, up through and including that, and also the one specific commit thistoo. You might, for instance, decide to cherry-pick every commit on feature/X that grows from develop, plus one bug fix commit fix-1234, for testing purposes:

git checkout master
git checkout -b testbranch
git cherry-pick develop..feature/X fix-1234

Anyway, the point here is that this might cherry-pick a dozen or more commits, and somewhere along the way there could be a merge conflict, that requires that git cherry-pick stop and get assistance.

Aside: the model here—the Unix/Linux command line—is that you enter a command into a command-interpreter called a shell. The shell passes control of the human-interface to the new command, which retains it until the command finishes and exits. Once the command exits, there is no trace left of the command itself: anything permanent must be saved in files.

So: If cherry-pick must stop, how will it know where to resume? The answer is by saving information in files. If you're cherry-picking a single commit, Git saves just the CHERRY_PICK_HEAD file, which records the ID of the commit being cherry-picked. If you're cherry-picking multiple commits, though, Git saves the conflicted commit as for a single commit, and saves the remaining information in a sequencing directory (whose location has moved some over time).

Running git cherry-pick --continue directs the (new, separate instance of) cherry pick command to pick up where the previous one left off. Git will first run a git commit for you, then locate the sequencing information and finish as much of the sequence as it can, stopping again at the next conflict, or finishing all the cherry-picks.

Running git commit instead, Git will notice the CHERRY_PICK_HEAD file and use that as you have seen. When the commit finishes, that command exits and does nothing with the sequencer. If there is sequencer data left behind, you can now git cherry-pick --continue: Git will notice that the commit is already done and simply continue / finish the sequencer operation.

Note that you can, instead of finishing the operation, use git cherry-pick --abort. This terminates the operation and puts things back to the way they were before you started. As of Git 2.19, you can instead use git cherry-pick --quit to stop (a la --abort) but not put things back to how they were before you started. That is, it stops any future cherry-picking operations, without undoing the ones you have done so far. (This operation was missing before 2.19.)

The git status command now notices if there is a sequence in progress, and reports that you are in the middle of whatever it is you were doing. (This is a big improvement from the Git 1.7 era, when it didn't.)

Perron answered 8/2, 2017 at 0:18 Comment(4)
Wow, that's a great one! Thank you.Millimeter
You now have git cherry-pick --quit, but it fully works only with Git 2.19 (Q3 2018): see "How to Conclude a Git Cherry-Pick?".Threadfin
Also, the presence of .git/CHERRY_PICK_HEAD makes another difference, even when cherry-picking a single commit. When git cherry-picking, git preserves the author date (and probably the author name/email) of the original commit.Hennebery
@Alex35: True - I'll rephrase the above slightly. (I have the details in the longer section but it bears mentioning at the top.)Perron
T
0

As I commented, git cherry-pick --quit also deletes .git/CHERRY_PICK_HEAD, but, in addition, the message "If this is not correct, please remove the file .git/CHERRY_PICK_HEAD" will change with Git 2.29 (Q4 2020): Accesses to two pseudorefs have been updated to properly use ref API.

See commit b8825ef, commit b6d2558, commit c8e4159, commit 3f9f1ac (21 Aug 2020) by Han-Wen Nienhuys (hanwen).
(Merged by Junio C Hamano -- gitster -- in commit e699684, 31 Aug 2020)

builtin/commit: suggest update-ref for pseudoref removal

Signed-off-by: Han-Wen Nienhuys

When pseudorefs move to a different ref storage mechanism, pseudorefs no longer can be removed with 'rm'.
Instead, suggest a "update-ref -d" command, which will work regardless of ref storage backend.

The new message will suggest: git update-ref -d CHERRY_PICK_HEAD

Threadfin answered 1/9, 2020 at 16:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.