git cherry-pick
git cherry-pick takes the changes introduced by one or more existing commits and re-applies them as brand-new commits on your current branch. It’s the tool you reach for when you need one specific fix from another branch — not everything that branch contains. Instead of merging or rebasing an entire branch’s history, cherry-pick lets you surgically copy a single change, which is exactly what you want for hotfixes, backports, and salvaging work from a branch you’re about to discard.
Overview / How it works
Every commit in Git is an object identified by a SHA-1 (or SHA-256, on newer repos) content hash. A commit object stores a pointer to a tree (a snapshot of the project’s files and directories at that point), a pointer to its parent commit(s), the author and committer metadata, and the commit message. A tree in turn points to blobs (raw file contents) and to other trees for subdirectories. A branch like main is nothing more than a small text file containing a commit hash — a movable pointer. When you commit, the branch pointer moves forward to the new commit; when you check out a branch, HEAD is updated to point at that branch.
git cherry-pick does not move or copy the original commit object. Instead, for each commit you name, Git computes the diff between that commit and its own parent (the patch it introduced), then applies that patch on top of your current HEAD. If the patch applies cleanly, Git writes a brand-new commit object with the same message (by default) and the same authorship information, but with a different parent and therefore a completely different SHA. The original commit is untouched and still exists wherever it was; you now have a second, independent commit with the same content changes living in a different place in history.
This distinguishes cherry-pick sharply from git merge and git rebase. A merge brings in an entire branch’s history and creates a merge commit with two parents. A rebase replays a whole sequence of commits from one branch onto another, rewriting all of them. Cherry-pick operates on one commit — or an explicit list/range of commits — at a time, and each picked commit becomes its own standalone commit on the target branch.
Syntax
git cherry-pick "<commit-hash>"
git cherry-pick "<commit-hash-1>" "<commit-hash-2>"
git cherry-pick -x "<commit-hash>"
git cherry-pick -n "<commit-hash>"
git cherry-pick --continue
git cherry-pick --abort
git cherry-pick --skip
| Flag | Meaning |
|---|---|
-e, --edit |
Open an editor to modify the commit message before finalizing the new commit. |
-n, --no-commit |
Apply the changes to the index and working tree, but don’t create a commit — useful for combining several picks into one commit or inspecting before committing. |
-x |
Append a line like (cherry picked from commit <hash>) to the new commit message, documenting where the change came from. Recommended when picking from a public/shared branch. |
-m <n>, --mainline <n> |
Required when cherry-picking a merge commit — tells Git which parent (1, 2, …) to diff against. |
-s, --signoff |
Add a Signed-off-by: trailer to the new commit message. |
--continue |
After manually resolving a conflict and staging the fix, resume an in-progress cherry-pick (or sequence of picks). |
--skip |
Skip the current commit in a multi-commit cherry-pick and move to the next one. |
--abort |
Cancel the cherry-pick entirely and restore the branch to the state it was in before it started. |
The commit argument can be a full or short SHA, a branch name (meaning the tip commit of that branch), or a range such as A^..B, which picks every commit reachable from B but not from A‘s parent — in other words, A through B inclusive, applied in order.
Examples
Example 1: Pick a single bug fix onto main
Suppose a teammate pushed a login-form fix to feature/login-page, but it needs to ship on main right now, before the rest of that feature is ready. First, find the commit:
git log --oneline feature/login-page -5
Output:
a1b2c3d fix: validate empty password field on login form
7e6d5c4 feat: add remember-me checkbox to login form
3c2b1a0 feat: scaffold new login page layout
f0e9d8c chore: add login page route
1234567 Initial commit
Now switch to main and cherry-pick just that one commit:
git switch main
git cherry-pick a1b2c3d
Output:
[main 9f8e7d6] fix: validate empty password field on login form
Date: Mon Aug 3 10:15:22 2026 -0700
1 file changed, 4 insertions(+), 1 deletion(-)
main now has a new commit, 9f8e7d6, containing the exact same file changes as a1b2c3d, with the same message and author. The two commits are different objects with different hashes — 9f8e7d6‘s parent is wherever main was pointing, not 7e6d5c4 — but their content diffs are identical.
Example 2: Pick a range of commits, recording their origin
Three related commits on feature/reporting-export fix a bug that also affects the currently-shipping release/2.4 branch. Rather than merging the whole feature branch, cherry-pick just that range with -x so the history documents where each commit came from:
git switch release/2.4
git cherry-pick -x 4a5b6c7^..9d8e7f6
Output:
[release/2.4 c1d2e3f] fix: correct CSV column ordering in export
Date: Mon Aug 3 10:20:11 2026 -0700
1 file changed, 6 insertions(+), 2 deletions(-)
[release/2.4 d4e5f6a] fix: handle empty date range in export filter
Date: Mon Aug 3 10:20:11 2026 -0700
1 file changed, 9 insertions(+), 1 deletion(-)
[release/2.4 e7f8a9b] test: add coverage for empty date range export
Date: Mon Aug 3 10:20:11 2026 -0700
1 file changed, 18 insertions(+)
Because the range is 4a5b6c7^..9d8e7f6, Git includes 4a5b6c7 itself (the caret walks back to its parent) through 9d8e7f6, applying all three commits in their original order. Each new commit’s message will end with a line like (cherry picked from commit 4a5b6c7...) thanks to -x, so anyone reading release/2.4‘s log later can trace these fixes back to the feature branch.
Example 3: A cherry-pick that conflicts
Cherry-picking applies a patch, and patches can fail to apply cleanly if the target branch has diverged. Picking a rate-limit header fix from hotfix/rate-limit onto main:
git cherry-pick e4f5a6b
Output:
Auto-merging src/middleware/rateLimiter.js
CONFLICT (content): Merge conflict in src/middleware/rateLimiter.js
error: could not apply e4f5a6b... fix: update rate-limit header name
hint: After resolving the conflicts, mark the corrected paths
hint: with 'git add ' or 'git rm '
hint: and commit the result with '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 has applied everything it could and left conflict markers in src/middleware/rateLimiter.js. Open the file, resolve the markers by hand, then stage and continue:
git add src/middleware/rateLimiter.js
git cherry-pick --continue
Output:
[main 6b7c8d9] fix: update rate-limit header name
--continue only proceeds once every conflicted file has been staged; if you decide the pick isn’t worth the trouble, git cherry-pick --abort unwinds everything and restores main exactly as it was before you started.
How it works step by step
- Git reads the commit object you named and its parent, and computes the diff (patch) between them — this is the same diff
git show <commit>would print. - Git applies that patch to your current index and working tree, the same three-way merge machinery used by
git mergeis used here, comparing the patch’s base, your current tree, and the incoming changes. - If every hunk applies cleanly, Git stages the results and writes a new commit object whose parent is your current
HEADcommit, copying the original author, date, and message (unless you pass-eor-n). - Your branch pointer (and
HEAD, since it points at the branch) moves forward to this new commit — the original commit you picked from is never touched or moved. - If a hunk can’t apply cleanly, Git stops mid-operation, leaves conflict markers in the affected file(s), and records enough state (in
.git/CHERRY_PICK_HEAD) that--continue,--skip, or--abortknow what to do next.
Common Mistakes
Mistake 1: Cherry-picking a merge commit without -m
git cherry-pick 8a9b0c1
Output:
error: commit 8a9b0c1 is a merge but no -m option was given.
fatal: cherry-pick failed
A merge commit has two parents, so Git can’t tell which parent to diff against to produce a patch. Tell it explicitly with -m 1 (usually the branch you were on when you merged) or -m 2:
git cherry-pick -m 1 8a9b0c1
In practice, cherry-picking merge commits is rarely what you want at all — reach for it only when you specifically need just that merge’s combined effect.
Mistake 2: Treating cherry-pick as a substitute for merging a whole branch
Cherry-picking commit after commit from a long-lived feature branch instead of eventually merging it creates duplicate commits with different hashes but the same content. When the feature branch is finally merged, Git can’t recognize the picked commits as “already applied,” so you can end up with the same change appearing twice in history, or unexpected conflicts during the real merge. Use cherry-pick for isolated, occasional backports — not as your everyday integration strategy.
Mistake 3: Committing with unresolved conflict markers
After a conflicted cherry-pick, it’s easy to run git add on a file that still contains <<<<<<</=======/>>>>>>> markers without actually removing them, then --continue without noticing. Always review the file (or run your test suite) before staging, and double-check with git diff --staged that no marker lines remain.
Mistake 4: Not knowing how to bail out
Some developers try to manually undo a messy in-progress cherry-pick file by file. If it’s not going well, just run:
git cherry-pick --abort
This cleanly restores your branch and working tree to their pre-cherry-pick state.
Best Practices
- Reserve cherry-pick for small, self-contained changes — a single bug fix or a short, related run of commits — not entire features.
- Use
-xwhen picking from a shared or public branch so the resulting commit message documents its origin for anyone reading the log later. - Use
-n/--no-commitwhen you want to combine several picked commits into a single squashed commit, or when you need to tweak the result before committing. - Run your tests after cherry-picking — a patch that applies cleanly can still be semantically wrong on a branch whose surrounding code has diverged.
- Prefer
git mergeorgit rebasefor bringing in a whole branch’s work; cherry-pick doesn’t record that a merge happened, so tools and teammates can’t easily tell the commits are related unless you use-xor explain in the message. - Write commit messages that follow Conventional Commits (
fix:,feat:,chore:, …) even for the commit you’re picking from — a clear message makes it far easier to justify the pick later. - If a pick conflicts, resolve deliberately: read both sides of the conflict, don’t just pick one side blindly.
Practice Exercises
- Create a repo with two branches,
mainandfeature/notifications. On the feature branch, make a commit that fixes a typo in an existing file. Cherry-pick just that commit ontomainand confirm withgit log --oneline mainthat a new commit (different hash, same message) now exists there. - Deliberately create a conflict: edit the same line of the same file differently on two branches, commit on both, then cherry-pick one branch’s commit onto the other. Resolve the conflict, stage it, and finish with
git cherry-pick --continue. Then try the same setup again but finish with--abortinstead, and confirm your branch is back to its starting state. - Make three small commits in a row on a scratch branch. Cherry-pick all three onto another branch using
-nso nothing is committed automatically, then create a single combined commit from the staged result. Compare the file contents to what you’d get from picking each commit separately.
Summary
git cherry-pick <commit>applies the diff introduced by an existing commit onto your current branch and creates a brand-new commit — the original commit is left untouched.- Because the new commit has a different parent, it gets a different SHA even though its content changes match the original.
- Cherry-pick can take multiple commits or a range (
A^..B), applying them in order. -xrecords the source commit in the new message;-nstages without committing;-mis required for merge commits.- Conflicts pause the operation; resolve, stage, and run
--continue, or bail out entirely with--abort. - Use cherry-pick for surgical, occasional backports and hotfixes — not as a routine replacement for merging or rebasing whole branches.
