Skip to content

Cherry-Pick

Cherry-pick is a surgical tool for transplanting individual commits between branches. Unlike merge or rebase which operate on entire branch histories, cherry-pick extracts the diff from a specific commit and applies it as a new commit on the current branch. This is invaluable for backporting bug fixes to release branches or applying specific features without merging entire development lines. The new commit has a different SHA because it has a different parent, even though the code change is identical.

git cherry-pick applies the diff introduced by a specific commit onto the current branch as a brand-new commit. It does not move or copy the original commit object — it computes the patch That the source commit introduced (relative to its parent), then creates a new commit on the target Branch with that same patch applied.

This distinction is critical. The original commit and the cherry-picked commit share the same Author, message, and diff, but they have different SHAs because they have different parent Commits and () different committer timestamps. They are distinct objects in the DAG.

OperationWhat it doesCommit topology change
mergeCreates a merge commit joining two branch historiesNew merge commit, preserves both histories
rebaseReplays a sequence of commits onto a new baseRewrites all replayed commits with new SHAs
cherry-pickApplies one or more specific commits onto the current branchNew commits with new SHAs, no merge commit

Merge preserves the full topological relationship between branches. Rebase rewrites an entire Sequence linearly. Cherry-pick is surgical — it extracts individual commits without regard to branch Topology.

Internally, cherry-pick operates identically to a single-step rebase. git cherry-pick <sha> is Roughly equivalent to git rebase --onto HEAD <sha>^ <sha>. The difference is one of intent: rebase Is for moving an entire branch, cherry-pick is for selecting individual commits.

Terminal window
## Check out the target branch
$ git checkout main
## Apply a specific commit from another branch
$ git cherry-pick a3f2b1c
# The commit a3f2b1c is now applied as a new commit on main
$ git log --oneline -3
f7e8d9c (HEAD -> main) Fix authentication timeout
b1c2d3e Merge pull request #42
a4b5c6d Add rate limiting middleware

The new commit has a different SHA but the same author, author date, and commit message as the Original.

Terminal window
# Cherry-pick multiple commits in order
$ git cherry-pick a3f2b1c b4c5d6e e7f8a9b
# Cherry-pick a range (inclusive of both endpoints)
$ git cherry-pick A..B
# Note: A..B means "all commits reachable from B but not from A"
# This EXCLUDES commit A itself
# To include A, use the three-dot syntax or specify it explicitly
$ git cherry-pick A^..B
# This includes A and everything up to B

The difference between A..B and A^..B is a common source of confusion:

SyntaxMeaningIncludes A?
A..BCommits reachable from B, not reachable from ANo
A^..BCommits from A”s parent through BYes
Terminal window
# Practical example: cherry-pick everything from commit X through Y, inclusive
$ git cherry-pick X^..Y

Picking from Another Branch Without Checking Out

Section titled “Picking from Another Branch Without Checking Out”
Terminal window
# Cherry-pick using branch reference instead of SHA
$ git cherry-pick feature/login-fix
# Cherry-pick a range from another branch
$ git cherry-pick feature/branch~3..feature/branch
Terminal window
# Edit the commit message during cherry-pick
$ git cherry-pick -e a3f2b1c
# Cherry-pick but keep the original committer info as well
# (by default, cherry-pick preserves the original author but sets committer to you)
$ git cherry-pick -x a3f2b1c
# This appends "(cherry picked from commit a3f2b1c)" to the message
# Sign the cherry-picked commit
$ git cherry-pick -S a3f2b1c
# Cherry-pick without committing (apply changes to working tree only)
$ git cherry-pick --no-commit a3f2b1c

The -x flag is worth understanding in depth. When you use -xGit appends a trailer to the Commit message:

Fix authentication timeout
(cherry picked from commit a3f2b1c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a)

This creates a traceable link back to the original commit, which is valuable for auditing and for Understanding the provenance of changes. Many organizations require -x in their contribution Guidelines.

Terminal window
# Revert the changes introduced by a cherry-picked commit
$ git revert <new-sha>
# Or revert the original commit on its original branch
$ git revert a3f2b1c

When the target branch has changes that conflict with the patch being applied, cherry-pick halts and Requires manual resolution. The mechanics are identical to merge conflict resolution, but the state File is different.

During conflict resolution, Git writes the SHA of the commit being cherry-picked to .git/CHERRY_PICK_HEAD:

Terminal window
# During a conflict:
$ cat .git/CHERRY_PICK_HEAD
a3f2b1c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a

This file exists for the same reason MERGE_HEAD exists during a merge — it tells Git (and any Tools reading the repository) that an operation is in progress and which commit is being applied. Git also sets .git/MERGE_MSG with the commit message from the cherry-picked commit, so the message Is preserved through conflict resolution.

Terminal window
# 1. Cherry-pick encounters a conflict
$ git cherry-pick a3f2b1c
error: could not apply a3f2b1c... Fix authentication timeout
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <path>' or 'git rm <path>'
# 2. Examine conflicts
$ git status
both modified: src/auth/handler.go
both modified: src/auth/middleware.go
# 3. Resolve conflicts in each file
$ vim src/auth/handler.go src/auth/middleware.go
# 4. Mark files as resolved
$ git add src/auth/handler.go src/auth/middleware.go
# 5. Continue the cherry-pick
$ git cherry-pick --continue
Terminal window
# Abort: restore the branch to its state before the cherry-pick
$ git cherry-pick --abort
# Deletes CHERRY_PICK_HEAD, resets HEAD and index to pre-cherry-pick state
# Quit: keep any successful cherry-picks, abort the current one
# Only meaningful when cherry-picking multiple commits
$ git cherry-pick --quit
# Useful when cherry-picking a range and you want to keep the ones that succeeded

The difference between --abort and --quit matters when cherry-picking multiple commits. If you Are cherry-picking A, B, C and the conflict occurs at B:

  • --abort: rolls back everything, including the successful cherry-pick of A.
  • --quit: keeps A’s cherry-pick on the branch, drops the operation entirely (B and C are not applied).
Terminal window
# Skip the current commit (keep the branch as-is, move to the next)
$ git cherry-pick --skip
# Only available in Git 2.35+

For older Git versions, you can achieve the same effect by resetting the index and continuing:

Terminal window
# Reset the index to HEAD (discard the failed patch)
$ git reset HEAD
# Continue to the next commit in the cherry-pick sequence
$ git cherry-pick --continue

Cherry-picking a merge commit is fundamentally different from cherry-picking a regular commit Because a merge commit has two parents. Git does not know which parent’s changes you want — the Diff of a merge commit is relative to the first parent, but that may not be what you intend.

Terminal window
# Cherry-pick a merge commit, using parent 1 as the base for the diff
$ git cherry-pick --mainline 1 a3f2b1c
# Cherry-pick a merge commit, using parent 2 as the base for the diff
$ git cherry-pick --mainline 2 a3f2b1c

The --mainline flag tells Git which parent to use as the base when computing the diff. This Determines what changes the merge commit “introduces”:

  • --mainline 1 (default): The diff is computed as the changes between the merge commit and its first parent. This captures everything that was different in the merged branch.
  • --mainline 2: The diff is computed against the second parent, which captures the changes from the first parent’s branch that the merge did not include.

Cherry-picking a merge commit almost never does what you want because:

  1. Merge commits are semantic, not functional. The value of a merge commit is in the topology it creates — it joins two histories. The actual diff of a merge commit relative to either parent is often the empty set (if the merge was a clean fast-forward equivalent) or a large combined diff that includes conflict resolutions.

  2. Conflict resolutions are not portable. If the merge commit required manual conflict resolution, those resolutions are specific to the state of both branches at the time of the merge. Applying them to a different branch context will produce incorrect results.

  3. The diff may be empty or enormous. If the merge was a trivial merge (no conflicts, fast-forward equivalent), the diff against the first parent is empty. If the merge was complex, the diff may include unintended changes.

The correct approach is to cherry-pick the individual commits from the merged branch, not the merge Commit itself.

Terminal window
# WRONG: cherry-picking a merge commit
$ git cherry-pick a3f2b1c # a3f2b1c is a merge commit
# RIGHT: cherry-pick the individual commits from the merged branch
$ git log --oneline --first-parent a3f2b1c^..a3f2b1c
# Identify the commits you want, then cherry-pick them individually
$ git cherry-pick b1c2d3e c4d5e6f
FactorUse Cherry-PickUse Rebase
ScopeOne or a few specific commitsEntire branch or large sequence
IntentSelectively bring changes across branchesMove a branch to a new base
Commit orderYou control which commits and in what orderPreserves original order of the branch
History cleanlinessCan create a messy DAG with scattered commitsProduces a clean linear history
Merge conflictsResolve per commitResolve once per conflicting commit
TraceabilityNeeds -x for provenance trackingBranch topology provides context
Terminal window
# Hotfix: a critical bug fix on release/v2.1 needs to go to main
$ git checkout main
$ git cherry-pick release/v2.1
# Backport: a security fix from main to a maintenance branch
$ git checkout release/v1.9
$ git cherry-pick a3f2b1c
# Selective: you need one commit from a large PR, not the whole thing
$ git checkout feature/api
$ git cherry-pick abc1234
Terminal window
# Your feature branch has fallen behind main and you want to integrate
$ git checkout feature/auth
$ git rebase main
# You want to squash or reorder commits before merging
$ git rebase -i main

Rebase is the correct tool when you want to maintain the full history of a branch. Cherry-pick is Correct when you need to extract specific commits without pulling in the entire branch history.

Rebase Can Replace Cherry-Pick (But Not Always)

Section titled “Rebase Can Replace Cherry-Pick (But Not Always)”

Technically, you can achieve cherry-pick’s effect with rebase:

Terminal window
# These are approximately equivalent:
$ git cherry-pick a3f2b1c
$ git rebase --onto HEAD a3f2b1c^ a3f2b1c

But rebase operates on the entire state of the branch. If you are on main and want one commit from feature``git rebase --onto requires you to specify the correct base and range, which is more Error-prone than a simple cherry-pick.

Cherry-pick creates a new commit with a different SHA but the same changes. If the same commit is Later merged through normal branch topology, Git does not detect the duplication:

Terminal window
# On main:
$ git cherry-pick a3f2b1c # Creates new commit d4e5f6a with same changes
# Later, someone merges the feature branch that contains a3f2b1c:
$ git merge feature/login
# a3f2b1c's changes are now in main TWICE: once via cherry-pick (d4e5f6a),
# once via merge (a3f2b1c). Git does not deduplicate.

This is particularly insidious because the duplicate changes may not cause an immediate conflict — Git applies them cleanly since the second application is a no-op for most files. But the history now Contains two commits making the same change, which confuses code review, bisect, and blame.

Cherry-pick severs the topological relationship between the source commit and its new location. The Cherry-picked commit has no parent link to the original commit or its ancestors:

Before cherry-pick:
main: A --- B --- C
\
feature: D --- E --- F
After cherry-pick of E onto main:
main: A --- B --- C --- E'
\
feature: D --- E --- F

E' on main and E on feature have no relationship in the DAG. git log --graph shows them as Unrelated commits. There is no merge base connecting them. This makes it difficult to answer “is This change already in main?” without manual inspection.

Cherry-picked commits break git bisect because bisect relies on a clean linear history where each Commit represents a discrete state change. When the same logical change exists in multiple commits Across different branches:

  1. Bisect may identify the cherry-picked commit as the culprit instead of the original.
  2. The original commit may be in a range that bisect never examines.
  3. If the cherry-picked commit has a slightly different context (due to conflict resolution during the pick), bisect may land on a commit that does not actually compile.

git blame traces line changes to their most recent commit on the current branch. If a fix was Cherry-picked, blame shows the cherry-picked commit (new SHA) rather than the original commit. This Severs the link between the code and its original context — the PR, the code review, and the Discussion around the original commit.

Terminal window
# On feature branch, commit a3f2b1c fixed a bug in line 42
# That commit was cherry-picked to main as d4e5f6a
$ git blame src/auth/handler.go -L 42,42
d4e5f6a (Author Name 2025-06-01 42) return nil, err
# d4e5f6a is the cherry-picked SHA — the original a3f2b1c is not shown
# To find the original, you need the -x flag or manual searching

Commits exist within a context of surrounding changes. A commit that introduces a function may Depend on commits that introduce its callers, its types, or its tests. Cherry-picking a commit in Isolation may produce code that does not compile or has broken references.

Terminal window
# Commit A: adds type UserSession
# Commit B: adds function GetSession(user UserSession) error
# Commit C: adds tests for GetSession
# Cherry-picking B without A produces a compile error (undefined type)
$ git cherry-pick B
error: undefined: UserSession

The canonical cherry-pick workflow is the hotfix: a critical bug is discovered in production (on the Release branch), fixed there, and the fix needs to be applied to main and potentially other active Branches.

Terminal window
# 1. Fix the bug on the release branch
$ git checkout release/v2.1
# Make the fix, commit it
$ git commit -m "fix: resolve authentication timeout under high load"
# SHA: a3f2b1c
# 2. Cherry-pick to main
$ git checkout main
$ git cherry-pick -x a3f2b1c
# New commit on main: d4e5f6a
# 3. Cherry-pick to other active release branches
$ git checkout release/v2.2
$ git cherry-pick -x a3f2b1c
$ git checkout release/v3.0
$ git cherry-pick -x a3f2b1c
# 4. Push all branches
$ git push origin main release/v2.1 release/v2.2 release/v3.0

Using -x here is critical — it records the provenance of each cherry-picked commit, so anyone Reading the log can trace the fix back to its origin.

When a feature spans multiple commits and needs to be backported to a maintenance branch:

Terminal window
# Identify the range of commits to backport
$ git log --oneline main~5..main
f7e8d9a feat: add session encryption
e1f2a3b fix: handle empty session tokens
b4c5d6e refactor: extract session validation
# Cherry-pick the entire range (oldest first)
$ git checkout release/v1.9
$ git cherry-pick b4c5d6e^..f7e8d9a
# Or specify the commits individually for more control
$ git cherry-pick b4c5d6e e1f2a3b f7e8d9a

When a PR contains multiple commits but you only need one:

Terminal window
# Fetch the PR branch
$ git fetch origin pull/123/head:pr-123
# Inspect the commits
$ git log --oneline pr-123
a1b2c3d feat: add OAuth2 support
d4e5f6a chore: update dependencies
f7e8a9b fix: correct token validation # This is the one you need
# Cherry-pick just that commit
$ git checkout main
$ git cherry-pick f7e8a9b
# Clean up
$ git branch -D pr-123
Terminal window
# Add the upstream remote if not already present
$ git remote add upstream https://github.com/upstream/repo.git
# Fetch upstream branches
$ git fetch upstream
# Cherry-pick a specific commit from upstream's main
$ git cherry-pick upstream/main # latest commit on upstream/main
# Or cherry-pick a specific SHA
$ git cherry-pick abc1234

If a cherry-picked fix causes problems on the target branch:

Terminal window
# Revert the cherry-picked commit on main
$ git checkout main
$ git revert d4e5f6a # The cherry-picked commit's SHA on main
# Note: reverting on main does NOT revert the original on the release branch
# The original commit a3f2b1c on release/v2.1 is untouched

When you cherry-pick a signed commit, the new commit is not automatically signed with the Original author’s key. The signature is part of the commit object, and the new commit is a different Object:

Terminal window
# Original signed commit on feature branch
$ git verify-commit a3f2b1c
Good "gpg" signature from "Developer <dev@example.com>"
# Cherry-pick to main
$ git checkout main
$ git cherry-pick a3f2b1c
# The new commit is NOT signed (unless commit.gpgsign is true)
$ git verify-commit d4e5f6a
error: no signature found

If commit.gpgsign = true is set in your configuration, Git signs the cherry-picked commit with your key, not the original author’s key. This is correct behavior — you are the committer, and The signature proves that you (the committer) applied this change, not that the original author did.

Cherry-pick always preserves the original author name, email, and author date. It sets the committer To you with the current timestamp:

Terminal window
$ git log --format=fuller d4e5f6a
commit d4e5f6a...
Author: Developer <dev@example.com>
AuthorDate: Mon Jun 2 10:00:00 2025
Commit: You <you@example.com>
CommitterDate: Tue Jun 3 14:30:00 2025
Fix authentication timeout

If you want to override the author (e.g., when cherry-picking your own commits), you can use Environment variables:

Terminal window
# Cherry-pick and override the committer date to match the author date
$ GIT_COMMITTER_DATE="$(git log -1 --format=%aI a3f2b1c)" git cherry-pick a3f2b1c
# Cherry-pick and set a different author
$ git cherry-pick --author="You <you@example.com>" a3f2b1c

Cherry-pick operates on commits, not tags. If you cherry-pick a commit that a signed tag points to, The tag is not transferred:

Terminal window
# Tag points to a commit on the release branch
$ git tag -v v2.1.1
Good signature from "Release Manager <release@example.com>"
object a3f2b1c...
# Cherry-pick the commit to main
$ git checkout main
$ git cherry-pick a3f2b1c
# The tag v2.1.1 still points to a3f2b1c on the release branch
# The cherry-picked commit d4e5f6a on main has no tag

Cherry-Picking into a Branch That Already Has the Commit

Section titled “Cherry-Picking into a Branch That Already Has the Commit”

If you cherry-pick a commit into a branch that already contains it (e.g., through a previous merge), Git applies the patch, finds no changes, and creates an empty commit:

Terminal window
$ git cherry-pick a3f2b1c
# On branch main
# nothing to commit, working tree clean
# The previous cherry-pick (now empty) is not committed

Git detects this and skips the commit with a message indicating it was already applied. However, if The commit was modified by a previous merge (conflict resolution changed the content), the Cherry-pick may produce unexpected results — a non-empty commit with changes that duplicate or Conflict with existing code.

A revert commit undoes the changes of its parent. Cherry-picking a revert to a different branch Undoes changes that may not exist there, or undoes changes that were applied differently:

Terminal window
# On release branch: commit A introduces a feature, commit B reverts it
# Cherry-picking B to main undoes A's changes on main,
# but A on main may have different surrounding code
$ git cherry-pick B # Dangerous: the revert context may not match

When cherry-picking multiple commits, the order must be topologically correct. Picking a commit Before its dependencies (commits it builds on) will produce conflicts or broken code:

Terminal window
# Commit B depends on commit A (B calls a function introduced in A)
# WRONG order:
$ git cherry-pick B # Fails: undefined function from A
$ git cherry-pick A # Now A is applied after B
# CORRECT order:
$ git cherry-pick A
$ git cherry-pick B

Git does not enforce topological ordering during cherry-pick. It applies commits in the order you Specify them. This is unlike rebase, which preserves the original commit order.

Without -xCherry-picked commits have no traceable link to their origin. In a large team, this Makes it impossible to answer “where did this change come from?” without manually searching the History of all branches. Always use -x when cherry-picking in a shared repository.

You should not start a cherry-pick while a rebase is in progress. Both operations manipulate the Branch tip and use overlapping state files. If you need to cherry-pick during a rebase, complete or Abort the rebase first.

Cherry-pick is a pragmatic tool, but overuse creates hidden dependencies between branches. If branch A relies on cherry-picks from branch B, and branch B is later rewritten or deleted, the provenance Of those changes is lost. Over time, the DAG becomes a web of disconnected commits with no clear Lineage. Prefer merge or rebase for structural changes; reserve cherry-pick for true hotfixes and Backports.

Not Communicating Cherry-Picks to the Team

Section titled “Not Communicating Cherry-Picks to the Team”

Cherry-picks are invisible in the branch topology. A merge shows up in git log --graph. A Cherry-pick does not. If you cherry-pick a fix to main, notify the team — especially the author of The original commit, who may not realize their fix is being applied elsewhere and may re-merge the Same changes.

This topic covers the core concepts of cherry-pick, including underlying theory, practical implementation, and key applications.

Key concepts include:

  • Git fundamentals (add, commit, push, pull)
  • branching and merging strategies
  • resolving merge conflicts
  • rebasing and cherry-picking
  • Git workflows (GitFlow, trunk-based)

Understanding these concepts thoroughly is essential for both examinations and practical programming, and requires both theoretical knowledge and hands-on practice.

Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.


  • Remote Operations shows how cherry-picked commits are pushed to shared branches after selective application.
  • Branching provides the branch context in which cherry-pick operations target specific commits.
  • Conflict Resolution addresses the merge conflicts that can arise when cherry-picked commits overlap with other changes.