Cherry-Pick
Surgical commits — hotfix workflows, release backports, and conflict mechanics.
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:
- The new commit has a new SHA. Same change, but a different identity, different parent, different timestamp.
- The original commit is untouched. Cherry-pick doesn't move or copy commits between branches — it computes a new commit from the diff.
- 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.
-xadds 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- cligitfree tier
`git cherry-pick` ships with every install. The `-x` flag is the secret weapon.
- cliLazygitfree tier
TUI: navigate to a commit, press `c` to cherry-pick. Zero context switch.
- serviceGitLab patch-managementfree tier
One-click cherry-pick from MR UI to a target branch.
- serviceGitHub backport botsfree tier
Auto-cherry-pick PRs to release branches via labels.