Resolving Cherry-Pick Conflicts

git cherry-pick copies the changes introduced by one existing commit and replays them as a brand-new commit on your current branch. It is how you grab a single bug fix from another branch without merging everything else that branch contains. Because cherry-pick has to merge that commit’s changes into your current files, it can run into the same problem as git merge or git rebase: a conflict, where Git cannot automatically decide which version of a line to keep. This lesson explains why cherry-pick conflicts happen, what Git leaves behind in your working tree when one occurs, and the exact steps to resolve, skip, or abort your way out.

Overview: How Cherry-Pick Conflicts Happen

Every commit in Git is a snapshot. It points to a tree object describing the state of every tracked file at that moment, and that tree points to blob objects holding the actual file contents. A commit also records the SHA-1 of its parent commit. git cherry-pick <commit> does not copy that snapshot wholesale onto your branch. Instead, Git computes the diff between the target commit’s tree and its parent’s tree — essentially, “what changed in this one commit” — and tries to apply that diff on top of your current HEAD using a three-way merge, the same algorithm git merge uses.

The three inputs to that merge are: the cherry-picked commit’s parent (the common base), the cherry-picked commit itself (the change being applied), and your current HEAD commit (where the patch is being applied). If the lines the target commit touched do not overlap with lines your branch has already changed, Git applies the patch cleanly and immediately creates a new commit — with a brand-new SHA-1, a new parent (your old HEAD), and, if the content is unchanged, the same tree contents as the original but recorded as a distinct object. The commit message is copied from the original by default.

If the same lines were touched on both sides, Git cannot pick a winner automatically and stops mid-operation. At that point several things happen at once: Git writes a file named CHERRY_PICK_HEAD inside .git that records the SHA of the commit you were trying to pick; the index gets special “unmerged” entries for each conflicted path (stage 1 for the common ancestor, stage 2 for your current branch/”ours”, stage 3 for the incoming commit/”theirs”); and Git writes the conflicting file to your working tree with literal conflict markers (<<<<<<<, =======, >>>>>>>) inserted directly into the text so you can see both versions side by side. Git then pauses and waits for you — nothing is committed, and no history has been rewritten yet.

Syntax

# General form
git cherry-pick [options] ...

# Concrete example
git cherry-pick 9f8e7d6
Flag Meaning
--continue After you’ve resolved conflicts and staged the fixed files, finish the cherry-pick and create the commit.
--abort Cancel the entire cherry-pick and restore the working tree and index to exactly how they were before it started.
--skip Give up on applying the current commit (leaving your tree as-is) and, if picking a range, move on to the next commit in the sequence.
-n, --no-commit Apply the changes to the working tree and index but don’t create a commit — useful for combining several cherry-picks into one commit.
-x Append a line to the new commit message noting the original commit’s SHA, e.g. (cherry picked from commit 9f8e7d6...). Recommended for traceability.
-m <parent-number> Required when cherry-picking a merge commit; tells Git which parent (1, 2, …) to diff against as the “mainline”.
-e, --edit Open an editor to modify the commit message before committing.
-s, --signoff Add a Signed-off-by trailer to the commit message.

Examples

Example 1: A single commit that conflicts

You’re on feature/login-page and want to backport one bug-fix commit to main without merging the whole feature branch.

git log --oneline feature/login-page -3

Output:

a1b2c3d Add remember-me checkbox
9f8e7d6 Fix validation message typo
5c4b3a2 Add login form markup
git switch main
git cherry-pick 9f8e7d6

Output:

Auto-merging src/login.js
CONFLICT (content): Merge conflict in src/login.js
error: could not apply 9f8e7d6... Fix validation message typo
hint: After resolving the conflicts, mark them with
hint: "git add/rm ", then run
hint: "git cherry-pick --continue".
hint: You can instead skip this commit with "git cherry-pick --skip".
hint: To abort and get back to the state before "git cherry-pick",
hint: run "git cherry-pick --abort".

Git already applied the parts of the commit that didn’t conflict and stopped only on src/login.js. Opening that file shows Git’s conflict markers written directly into it:

<<<<<<< HEAD
  showError('Please enter a valid email address');
=======
  showError('Please enter a valid email address.');
>>>>>>> 9f8e7d6 (Fix validation message typo)

Everything between <<<<<<< HEAD and the ======= divider is your branch’s current version (“ours”); everything between the divider and >>>>>>> is the incoming commit’s version (“theirs”). Running git status confirms the state:

git status

Output:

On branch main
You are currently cherry-picking commit 9f8e7d6.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Unmerged paths:
  (use "git add ..." to mark resolution)
	both modified:   src/login.js

Edit the file by hand to keep the correct wording, delete all three marker lines, save, then stage and finish:

git add src/login.js
git cherry-pick --continue

Output:

[main 7e6d5c4] Fix validation message typo
 1 file changed, 1 insertion(+), 1 deletion(-)

--continue reads the staged tree from the index, writes a new commit object with that tree and your old HEAD as its parent, moves the main branch pointer to it, and deletes CHERRY_PICK_HEAD.

Example 2: Cherry-picking a range and skipping a bad commit

You need three commits from feature/login-page on the release/v2.3 branch, but one of them doesn’t apply because it depends on code that isn’t in the release branch.

git switch release/v2.3
git cherry-pick 5c4b3a2^..a1b2c3d

Output:

[release/v2.3 f3e2d1c] Add login form markup
Auto-merging src/login.js
CONFLICT (content): Merge conflict in src/login.js
error: could not apply 9f8e7d6... Fix validation message typo

The first commit in the range applied cleanly and was committed automatically; the second stopped on a conflict. If, after inspecting it, you decide this particular fix genuinely doesn’t belong on the release branch, skip it and let Git move on to the next commit in the range:

git cherry-pick --skip

Git discards the partially-applied changes for that one commit, clears CHERRY_PICK_HEAD for it, and continues the sequence with a1b2c3d. --skip is only safe when you’re intentionally leaving that commit’s changes out — use --continue instead if you actually want its content, just modified.

Example 3: Merge commits need -m, and you can always bail out

git cherry-pick 3d2c1b0

Output:

error: commit 3d2c1b0 is a merge but no -m option was given.
fatal: cherry-pick failed

A merge commit has two parents, so Git can’t guess which one to diff against. Tell it explicitly — parent 1 is almost always the branch you merged into:

git cherry-pick -m 1 3d2c1b0

If a cherry-pick conflict turns out to be more trouble than it’s worth, or you picked the wrong commit entirely, abandon it and get back to a clean state:

git cherry-pick --abort

This restores the index and working tree exactly as they were before the cherry-pick started and removes CHERRY_PICK_HEAD — as if you’d never run the command.

How It Works Step by Step

  1. Git resolves <commit> to a commit object and reads its tree and its parent’s tree.
  2. Git computes the diff between the commit’s tree and its parent’s tree — the exact change that commit introduced.
  3. Git performs a three-way merge between that diff, the common ancestor, and your current HEAD tree, updating the working tree and index file by file.
  4. Any path that merges cleanly is staged automatically. Any path with overlapping changes is left “unmerged”: the index gets stage-1/2/3 entries for it, and the working tree copy is rewritten with <<<<<<</=======/>>>>>>> markers.
  5. If there were no conflicts, Git immediately writes a new commit object (new tree, parent = your old HEAD, message copied from the original) and moves the current branch pointer forward. Done.
  6. If there were conflicts, Git writes CHERRY_PICK_HEAD pointing at the target commit and stops, leaving your branch pointer unmoved.
  7. You edit the files to remove the markers and keep the correct content, then git add each resolved path, which promotes it out of the unmerged state.
  8. git cherry-pick --continue checks that no unmerged paths remain, builds a tree from the index, creates the commit, advances the branch, and deletes CHERRY_PICK_HEAD.

Common Mistakes

Mistake 1: Running --continue before staging the fixed file.

git cherry-pick --continue

Output:

error: your local changes would be overwritten by cherry-pick
U src/login.js
error: Committing is not possible because you have unmerged files.
hint: Fix them up in the work tree, and then use 'git add/rm '
hint: as appropriate to mark resolution and make a commit.
fatal: Exiting because of an unresolved conflict.

Git refuses because the index still has unmerged stages for that path. Fix: edit the file, remove the markers, then run git add src/login.js before --continue.

Mistake 2: Staging and committing without actually removing the conflict markers. It’s easy to run git add on a file where you resolved most of the conflict but left one marker block untouched. Git has no way to know the markers are wrong — to Git they’re just more text — so the commit succeeds and ships broken code:

git show --stat HEAD
grep -n "<<<<<<<" src/login.js

Fix: always re-read the whole file (or run a search for <<<<<<<) before staging, and let your test suite or linter run before you push.

Mistake 3: Cherry-picking a merge commit without -m. As shown in Example 3, Git refuses outright rather than guessing which parent is the right diff base — passing the wrong parent number silently applies the wrong changes, so pick it deliberately rather than by trial and error.

Best Practices

  • Always run git status immediately after a conflicting cherry-pick to see exactly which paths are unmerged before you start editing.
  • Search resolved files for leftover <<<<<<<, =======, and >>>>>>> markers before staging — a linter or pre-commit hook that greps for them catches this automatically.
  • Use -x when cherry-picking so the resulting commit message records which original commit it came from, which helps anyone reading history later understand why the same fix appears on two branches.
  • When cherry-picking several commits, resolve and continue one at a time rather than trying to pre-plan every conflict; --skip and --abort are always available if a later commit turns out to be a mistake.
  • Run your build or test suite after finishing a cherry-pick conflict, not just after the whole batch — a bad manual resolution is easiest to catch immediately.
  • Prefer git push --force-with-lease over bare --force if a cherry-pick was applied on a branch you've already pushed and you need to amend it, since --force-with-lease fails safely if someone else has pushed in the meantime.
  • For bringing over many commits or a whole feature, consider git merge or git rebase instead — cherry-pick is best for grabbing a small, specific number of commits, not replaying an entire branch's history.

Practice Exercises

  • Create a repo, make a branch bugfix/typo with a commit that changes one line of a file, then make a conflicting change to that same line on main. Cherry-pick the bugfix/typo commit onto main, resolve the conflict by hand, and finish with --continue. Confirm with git log --oneline that main now has a new commit with a different SHA than the original.
  • Repeat the setup above, but this time run git cherry-pick --abort partway through instead of resolving. Use git status and git diff before and after the abort to confirm your working tree ends up exactly as it was before the cherry-pick started.
  • Create a merge commit on a throwaway branch (merge two branches together), then try git cherry-pick <merge-sha> on a third branch without any flags. Read the error Git gives you, then retry with the correct -m value and confirm it succeeds.

Summary

  • git cherry-pick replays a single commit's diff onto your current HEAD using a three-way merge, the same core algorithm as git merge.
  • A conflict leaves CHERRY_PICK_HEAD set, unmerged (stage 1/2/3) entries in the index, and literal <<<<<<</=======/>>>>>>> markers written into the affected files — nothing is committed until you resolve it.
  • Resolve by editing the file, removing the markers, running git add <file>, then git cherry-pick --continue.
  • Use --skip to drop the current commit's changes and move on when picking a range, or --abort to cancel entirely and restore the pre-cherry-pick state.
  • Merge commits require an explicit -m <parent-number>; Git will refuse to guess.
  • A successful cherry-pick always creates a brand-new commit object with a new SHA-1, even when its content matches the original — use -x to keep a traceable link back to the source commit.