Table of Contents

Introduction

If you cherry-pick commits between branches often, sooner or later one won't apply cleanly. The branch you're picking onto has changed the same lines, and Git stops with "could not apply" and a conflict to sort out. After you've fixed the file, the pick is still waiting to be committed. git cherry-pick --continue is how you tell Git you're done and it can make the commit.

In this article, we'll:

  1. Resolve a conflict from picking "Space out the header links" onto main
  2. Watch git cherry-pick --continue make the new commit and move main onto it
  3. Cover how a range of picks stops at each conflict, and --skip for a commit you don't want after all

What is git cherry-pick --continue?

git cherry-pick --continue finishes a cherry-pick that stopped on a conflict. It takes what you've staged, commits it with the original commit's message and author, and moves your branch onto the new commit. If you were picking several commits, it then carries on with the next one.

A plain git cherry-pick does all of this in one go when the change applies cleanly. --continue is the second half of the same command, run after you've done the part Git couldn't.

How the pick stopped

Our sample repo has a cherry-pick of feature's last commit onto main that stopped on a conflict in styles.css, now resolved and staged. main is on "Switch the header to grid" (ada2e79), which set styles.css to header { display: grid; }. The last commit on feature, "Space out the header links" (791fd8e), changed that same line to header { display: flex; gap: 12px; }. Running git cherry-pick 791fd8e on main can't apply that change cleanly, so Git writes both versions into styles.css between conflict markers, records the commit it was picking in .git/CHERRY_PICK_HEAD, and stops. git status says:

You are currently cherry-picking commit 791fd8e.
  (fix conflicts and run "git cherry-pick --continue")

We resolve it by keeping the grid and taking the gap:

header { display: grid; gap: 12px; }

and then stage it with git add, which is how Git learns the conflict is resolved.

Watch it happen

Here's git cherry-pick --continue, with that resolution staged:

  1. Before: HEAD is attached to main at ada2e79 "Switch the header to grid", and feature points at 791fd8e "Space out the header links". The resolved styles.css is staged but not committed yet.
  2. Git creates a new commit on main with the message of "Space out the header links" and the resolved changes from the index, with ada2e79 as its parent. It has a new id, 25da354.
  3. main moves to the new commit, and HEAD moves with it. feature and its four commits are unchanged.

Before and after

The after graph has "Space out the header links" twice, once on each branch. They're different commits with different hashes, 791fd8e on feature and 25da354 on main, and the one on main holds our resolved line rather than feature's original. Git's output:

The raw git output, if you want to read along in text

git log --oneline --graph --all

before

* 791fd8e (feature) Space out the header links
* 1117a34 Add search tests
* e5869f0 Fix typo in search box
* fc19889 Add search box
| * ada2e79 (HEAD -> main) Switch the header to grid
| * 8c02d5b Update dependencies
| * a0b2db3 Add user settings page
|/  
* 96c4fc2 Fix header layout
* ae65976 Add login page
* 3e1ffe4 Add project skeleton
* 4114b2c Initial commit

after

* 25da354 (HEAD -> main) Space out the header links
* ada2e79 Switch the header to grid
* 8c02d5b Update dependencies
* a0b2db3 Add user settings page
| * 791fd8e (feature) Space out the header links
| * 1117a34 Add search tests
| * e5869f0 Fix typo in search box
| * fc19889 Add search box
|/  
* 96c4fc2 Fix header layout
* ae65976 Add login page
* 3e1ffe4 Add project skeleton
* 4114b2c Initial commit

what git printed

[main 25da354] Space out the header links
 Date: Wed May 1 22:00:00 2024 +0000
 1 file changed, 1 insertion(+), 1 deletion(-)

Notice the Date: line. A cherry-pick keeps the original commit's author and author date, and Git mentions the date because it doesn't match when the commit was made. git status goes from M styles.css to clean.

Is it safe?

Safe git-sim pre-flight

Creates the commit with your resolution for 791fd8e Space out the header links; main moves to it.

The way back

  • Undo the new commit afterwards with: git reset --hard ORIG_HEAD

It is. It adds one commit to main and doesn't touch feature or any existing commit. As with any conflict, check that the file you staged is really the version you want. --continue commits whatever is in the staging area, conflict markers included if you left them in.

Where I hit this most is backporting a fix to an older release branch. The fix was written against code that has moved on since, so the pick conflicts in some nearby line that didn't exist yet on the old branch. I resolve it, continue, and then run the tests on that branch before anything else, since a resolution that looks right can still be wrong for the older code.

How to undo it

If you haven't pushed, drop the picked commit by moving main back one commit:

git reset --hard HEAD~1

main goes back to ada2e79 and styles.css back to the grid-only version. --hard also throws away any uncommitted edits, so check git status first.

If the picked commit has already been pushed, use git revert on it to add a commit that reverses it instead.

Picking several commits

When you cherry-pick a range of commits, like git cherry-pick 96c4fc2..feature, Git applies them one at a time, oldest first. Every time one conflicts, it stops, and you go through the same routine: fix the file, git add it, git cherry-pick --continue. Git commits that pick and moves on to the next commit in the range, until the range is done or the next conflict.

Sometimes, looking at the conflict, you realize you don't want that commit on this branch at all. Maybe main already has an equivalent change. git cherry-pick --skip drops the commit that conflicted and carries on with the rest of the range. And if the whole thing is going sideways, git cherry-pick --abort puts your branch back to where it was before the first pick.

Useful forms

  • git cherry-pick --skip leaves out the commit that conflicted and moves on.
  • git cherry-pick -x <commit> adds a "(cherry picked from commit ...)" line to the new commit's message, which makes the two easier to connect later. Add it when you start the pick, not at --continue.
  • git commit also commits a conflicted pick, but in a range you'd still need git cherry-pick --continue afterwards to move on to the next commit.

Try it on your repository

pip install git-sim
git-sim cherry-pick --continue

git-sim runs the command in a copy of your repository and draws the commit it would make, so your own stopped pick stays as it is.

Common questions

What does git cherry-pick --continue do?

It commits the change you were cherry-picking, with your conflict resolution in it, and moves your branch onto the new commit. In a range, it then goes on to the next commit.

Does the picked commit keep the original author?

Yes. The new commit keeps the original's author, author date and message. You become the committer, and the commit date is when you ran --continue. The git cherry-pick documentation lists the options that change this, like -x and --signoff.

What is the difference between --continue and --skip?

--continue commits your resolution of the commit that conflicted. --skip drops that commit entirely and moves on to the next one in the range.

Why does git cherry-pick --continue say there is nothing to commit?

Your resolution made the file identical to what main already had, so the pick would add an empty commit. If you don't need the commit, run git cherry-pick --skip. If you want it anyway, git commit --allow-empty keeps it.

Summary

In this article, we resolved the styles.css conflict from picking "Space out the header links" onto main, watched git cherry-pick --continue make the new commit and move main and HEAD onto it, and covered ranges and --skip.

Next steps

git cherry-pick covers picks that apply cleanly. git cherry-pick --abort is the way out when you'd rather not resolve the conflict.