When to cherry-pick vs backport a full branch Jump to heading

You have a fix on main and a release/2.3 line that must receive it. The question is not how to copy commits — Git offers several mechanisms — but which unit to move: a single commit, a contiguous range, or an entire branch replayed onto the release base. Get the unit wrong and you either drag unrelated changes into a stabilized release or leave the release line in a state that was never built or tested. This page is a decision guide within Cherry-Pick & Backporting, which covers the mechanics of relocating commits between lines of development.

When to use each approach Jump to heading

The choice hinges on the coherence of the change and the conflict surface it presents against the target line.

  • Cherry-pick a single commit when the change is self-contained — one bug fix, one commit, no dependency on surrounding work. This is the common hotfix case detailed in Cherry-Picking Hotfixes Across Release Branches.
  • Cherry-pick a contiguous range (A..B) when several sequential commits form one logical fix but were never squashed — for example a fix plus its fix! follow-up and a test.
  • Backport a full branch when the change is a coherent feature set — implementation, migration, tests, and any correction commits — where any partial subset leaves the release line in an untested intermediate state.

Reach for a full-branch backport, not piecemeal picks, whenever the commits have ordering dependencies: a commit that renames a symbol followed by one that uses the new name will conflict or silently break if picked out of order. The table below summarizes the decision.

SignalCherry-pick commit(s)Backport full branch
Change size1–3 independent commitsA coherent multi-commit feature
Inter-commit dependencyNoneOrdered, tightly coupled
Conflict surfaceSmall, localizedLarge but self-consistent
Intermediate build state mattersNoYes — each step must build
Traceability need-x reference per commitWhole PR / branch reference

The diagram below contrasts the two operations against a shared release base.

Cherry-pick versus full-branch backport onto a release lineTop: main branch with commits A B C D; a single commit C is copied down to the release branch as C-prime. Bottom: a feature branch of commits X Y Z is replayed as a group onto the release base using rebase --onto, producing X-prime Y-prime Z-prime.Cherry-pick one commitmainABCDreleaseRC'Backport a full branch (rebase --onto)featureXYZreplayRX'Y'Z'release base

Step-by-step recipe Jump to heading

Step 1 — Classify the change and check what is already backported Jump to heading

Before moving anything, confirm the target line does not already contain an equivalent commit. Cherry-picks produce new SHAs, so a plain git log comparison lies; compare by patch content instead:

# List commits on main that are NOT yet present (by patch) on the release line.
# '+' = missing from release; '-' = already there under a different SHA.
git cherry -v release/2.3 main

Verify the release branch is checked-out cleanly before you begin:

# Confirm a clean tree and the expected branch — cherry-pick refuses on a dirty tree
git status --short && git rev-parse --abbrev-ref HEAD

Step 2 — Cherry-pick individual commits with traceability Jump to heading

For one or a few independent commits, pick them onto the release branch and record their origin. The -x flag appends a (cherry picked from commit <sha>) trailer so anyone reading the release history can trace the change back to main:

git switch release/2.3
# -x records the source SHA in the commit message for auditability
git cherry-pick -x 9f3c1a2

Verify the trailer landed and the tree matches:

# The message should end with "(cherry picked from commit 9f3c1a2...)"
git show --no-patch --format=%B HEAD

Step 3 — Cherry-pick a contiguous range with A..B Jump to heading

When several sequential commits form one fix, pick the range rather than typing each SHA. Remember that A..B is exclusive of A — it means “commits reachable from B but not from A”:

# Pick every commit after <base> up to and including <tip>, in order.
# Use <base>^ if you also want the base commit itself included.
git cherry-pick -x 4ccd499..0f709fe

If a conflict interrupts the sequence, resolve it, stage the result, and continue — do not start a new pick:

# After editing conflicted files and running: git add <files>
git cherry-pick --continue
# Or abort the whole range and return to the pre-pick state:
git cherry-pick --abort

Verify the full range replayed and nothing was dropped:

# Count should equal the number of commits in the source range
git cherry -v release/2.3 main | grep -c '^+'

Step 4 — Find un-backported commits with git cherry Jump to heading

Over a release cycle, dozens of fixes land on main and only some are backported. git cherry audits the gap by patch-id, so it correctly ignores commits already picked under new SHAs:

# Everything on main not yet equivalent-present on release/2.3.
# Pipe through the SHAs to build a backport worklist.
git cherry release/2.3 main | grep '^+' | cut -d' ' -f2

Verify a candidate is genuinely absent before picking it, to avoid an empty-patch cherry-pick:

# An empty result means the patch is already present — skip it
git cherry release/2.3 main | grep <sha> || echo "already backported"

Step 5 — Relocate a full branch onto the release line with rebase --onto Jump to heading

When the change is a coherent feature set, replay the entire branch onto the release base with git rebase --onto. The three arguments are: the new base to replant onto, the old base to cut from (exclusive), and the tip to move:

# Move every commit in feature/report-export that is NOT on main,
# replanting them on top of release/2.3.
#   --onto <newbase> <upstream> <branch>
git rebase --onto release/2.3 main feature/report-export

This rewrites the moved commits — they get new SHAs on the new base. Confirm the intended commits (and only those) were replayed:

# Should list exactly the feature commits, now parented on release/2.3
git log --oneline release/2.3..HEAD

SAFETY WARNING: git rebase --onto rewrites commit hashes for every replayed commit. Never run it on a branch others have already pulled — collaborators’ histories will diverge and their next push will be rejected or, if forced, will clobber the rewritten line. If a shared branch was rewritten, recover the pre-rebase state with git reset --hard ORIG_HEAD (or git reflog to find the prior tip) before anyone force-pushes. For rewriting shared branches deliberately, follow the coordination rules in Safe Git Rebase -i for Shared Branches.

Step 6 — Verify equivalence and record the backport Jump to heading

After either operation, prove the release line now carries the change and still builds. Compare the effective diff of the backported work against the source:

# range-diff shows commit-by-commit how the backport differs from the original.
# An ideal backport shows only context-line shifts, no logic changes.
git range-diff main~1..main release/2.3~1..release/2.3

Then run the release line’s own test suite — a backport that builds on main can still fail against older dependencies on the release branch. Coordinate which checks run where via your branching model; see GitFlow vs GitHub Flow: A Comparison for how release lines are structured under each model.

Validation checklist Jump to heading

Before considering the backport complete, confirm each item:

Frequently asked questions Jump to heading

Does cherry-pick -x work when backporting from a private branch? Jump to heading

The -x flag appends a (cherry picked from commit <sha>) line referencing the source SHA. That reference is only useful if the source commit is reachable in shared history. When backporting from a not-yet-merged private branch, the referenced SHA may never become public — prefer picking after the source has merged into main, or add a human-readable trailer pointing to the pull request number instead so reviewers can still trace provenance.

How do I know which commits are already backported to a release branch? Jump to heading

Run git cherry -v release/2.3 main. It compares commits by patch-id rather than SHA, so commits already cherry-picked — which necessarily have different SHAs — are detected as equivalent and prefixed with a minus sign. Lines prefixed with a plus sign are commits present on main but not yet backported, giving you an exact worklist.

When should I backport a whole branch instead of individual commits? Jump to heading

Backport the whole branch when the change is a coherent set — a feature plus its tests, migrations, and follow-up corrections — where picking commits piecemeal would leave the release line in a state that never existed and was never tested. A full-branch backport via rebase --onto preserves the exact sequence and every intermediate build state, so each replayed commit is as buildable on the release line as it was on main.