git · level 6

Cherry-Pick

Surgical commits — hotfix workflows, release backports, and conflict mechanics.

175 XP

Cherry-Pick

git cherry-pick is the surgical tool of git history — it takes a single commit's diff and applies it as a new commit on the current branch. Rare in normal workflow, indispensable for release management.

Analogy

Cherry-pick is like photocopying one page out of a book and pasting it into a different book. The page (the diff) is the same; the printing on the original page (the SHA, position in history) is left alone. The new copy gets its own page number in the new book — same content, different identity. If the new book's surrounding pages don't match (the lines around your page assume different context), you get a paper jam (a conflict) and have to make manual edits before the copy fits.

What it actually does

Cherry-pick takes the diff of a commit (parent → commit) and applies it on top of HEAD, then makes a new commit with that diff and the original commit's message. Three things to notice:

  1. The new commit has a new SHA. Same change, but a different identity, different parent, different timestamp.
  2. The original commit is untouched. Cherry-pick doesn't move or copy commits between branches — it computes a new commit from the diff.
  3. The connection between the two is by convention only. Git itself doesn't track "this commit is a cherry-pick of that one" unless you ask it to (with -x).

The canonical use case — release backports

You support main (current development) and a long-lived release/2.4 branch in production. A bug fix lands on main as a1b2c3d. The fix is also needed on release/2.4.

You don't want to merge main into release/2.4 (that would bring along everything else on main). You want exactly that one commit:

git checkout release/2.4
git cherry-pick -x a1b2c3d
# applied as a new commit on release/2.4
git push origin release/2.4

The -x flag adds a trailer to the new commit's message:

fix(payment): guard nil pointer on zero-amount orders

(cherry picked from commit a1b2c3d4e5f6...)

That trailer is gold for future archaeology — anyone reading the history of release/2.4 can follow it back to the original commit on main.

The X-pick this commit pattern

Many teams formalise this as a workflow tag. A PR that needs to land on multiple branches gets a label:

backport-2.4
backport-2.5

Either a human or a bot (GitHub's backport-action, GitLab's "Cherry-pick" button, custom Mergify rules) cherry-picks the commit onto each release branch after it lands on main. Each backport opens its own PR for review, and conflicts are resolved per-branch.

The label name is often "X-pick" or "needs-backport" — pick whatever your team uses and apply it consistently.

Conflicts

Cherry-pick fails when the diff doesn't apply cleanly to the current branch:

$ git cherry-pick a1b2c3d
Auto-merging src/payment.go
CONFLICT (content): Merge conflict in src/payment.go
error: could not apply a1b2c3d... fix(payment): guard nil pointer
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

Three options:

# 1) Resolve and continue
# edit src/payment.go to fix the <<<< ==== >>>> markers
git add src/payment.go
git cherry-pick --continue

# 2) Skip this commit (in a multi-commit pick)
git cherry-pick --skip

# 3) Bail out, restore prior state
git cherry-pick --abort

A common reason for conflicts on backports: the surrounding code has diverged since the original commit. The fix on main may touch a function that was refactored differently in release/2.4. You either adapt the fix to the older shape, or — if the divergence is too great — write a fresh fix specifically for the older branch and skip the cherry-pick.

Cherry-pick a range

Pick multiple commits at once. The range syntax follows git's general convention (exclusive on the left, inclusive on the right):

git cherry-pick A..F          # commits after A, through F (5 commits if A..F is A B C D E F)
git cherry-pick A^..F         # include A itself (6 commits)
git cherry-pick A B C D       # explicit list, in that order

Each commit becomes its own new commit on the target branch. If conflict on commit C, you fix it, cherry-pick --continue, and git proceeds to D.

Useful flags

git cherry-pick -x <sha>           # add "cherry picked from" trailer
git cherry-pick -e <sha>           # open editor on the new commit message
git cherry-pick -n <sha>           # --no-commit; stage the change but don't commit yet (you commit later, with edits)
git cherry-pick --signoff <sha>    # add Signed-off-by line (DCO workflows)
git cherry-pick -m 1 <merge-sha>   # cherry-pick a merge commit, taking the diff from parent 1

-n is underrated — it lets you cherry-pick the change, then amend or split it before committing. Useful when the original commit needs minor adaptation for the target branch.

Cherry-picking merge commits

A merge commit has two parents. Cherry-picking it requires you to say which parent's perspective the diff is computed from:

# -m 1 → diff from first parent (the branch the merge was INTO)
# -m 2 → diff from second parent (the branch BEING merged)
git cherry-pick -m 1 <merge-sha>

Most often you want -m 1. The merge commit's diff "from main's side" is the entire feature being introduced, which is what you usually want to backport.

This is the situation where many engineers get confused — and the answer is "always think about which parent represents the perspective you want."

Anti-patterns

Cherry-pick as the primary integration mechanism. If you're cherry-picking five commits a day from main onto develop, you've got a branching strategy problem. Cherry-pick is for occasional backports, not daily integration. Daily integration is what merge/rebase are for.

Cherry-pick to "skip" a problematic commit. If commits A and B exist and you cherry-pick only B onto a fresh branch, you've forked history. Now you have to keep them in sync forever. Better: revert A on the original branch, or rebase the original branch to drop A.

Cherry-pick across radically diverged branches. If release/2.4 and main have diverged by 18 months, almost every cherry-pick will conflict. At that point, fixes for the old branch should be written for the old branch, not adapted from main.

A useful diagnostic

To find which commit something was cherry-picked from (assuming -x was used):

git log --oneline release/2.4 | head
# look at the cherry-pick trailers
git log release/2.4 --grep "cherry picked"

To find commits on main that haven't been backported to release/2.4:

git log --oneline release/2.4..main

That's a fast scan. Combined with PR labels and a backport bot, you keep the long-lived branch up to date without manual tracking.

What to internalise

  • Cherry-pick = single commit, surgical move, new SHA.
  • Use it for release backports and hotfix-to-multiple-branches.
  • -x adds the trace-back trailer; make it muscle memory.
  • Conflicts pause the operation; resolve, git add, --continue.
  • Heavy cherry-pick use signals you've outgrown your branching strategy.

Tools in the wild

4 tools
  • gitfree tier

    `git cherry-pick` ships with every install. The `-x` flag is the secret weapon.

    cli
  • Lazygitfree tier

    TUI: navigate to a commit, press `c` to cherry-pick. Zero context switch.

    cli
  • One-click cherry-pick from MR UI to a target branch.

    service
  • Auto-cherry-pick PRs to release branches via labels.

    service