Topic · Branching & Integration

Cherry-Pick & Patch Flow

Sometimes you want to move one change, not a whole history. Cherry-pick copies a single commit onto another branch; format-patch and am move commits between repositories that never share a network. Both are surgical tools for situations where merge and rebase are the wrong shape.

Why cherry-pick exists

Merge moves a whole branch. Rebase moves a chain. Cherry-pick moves one commit, or a hand-picked subset. The motivating situation: a bug fix lands on main as commit B, and the release branch release/v2.1 needs that exact change without the dozens of unrelated commits that surround it on main. You don't want to merge main into the release; you only want B.

git cherry-pick

From the official reference: "Given one or more existing commits, apply the change each one introduces, recording a new commit for each. This requires your working tree to be clean (no modifications from the HEAD commit)."

The result is a new commit on your current branch with the same logical change but a different SHA, because the parent is different and the author or committer timestamp is too. The content matches; the identity does not.

What cherry-pick does mechanically

The operation is two steps, in order:

  1. Compute the diff between <commit> and its parent. This is "the change <commit> introduced."
  2. Apply that diff to your current working state and record a new commit, reusing the original message.

If the same lines have evolved on the target branch, step two can produce conflicts the same way any three-way merge can. The patch is replayed, not transplanted; Git is not copying a stored object, it is reconstructing the change in a different context.

Before: B is the fix on main. release branched at A and has its own work D. main A B C the fix release A D After: cherry-pick of B onto release. New commit B' shares B's content, not its SHA. main (unchanged) A B C release A D B' same diff, new SHA
cherry-pick a single commitshell
git switch release/v2.1
git cherry-pick 4b1c9af
# release/v2.1 now ends in a new commit B' carrying the fix from 4b1c9af

Picking ranges

You can hand cherry-pick more than one commit. Two range forms come up constantly, and they differ in whether the lower bound is included.

FormMeansOrder applied
git cherry-pick A..B Every commit reachable from B but not from Aexcludes A. Oldest to newest.
git cherry-pick A^..B Same range, but starting from A's parent — includes A. Oldest to newest.
git cherry-pick A B C Three explicit commits, in the order given. As listed.

The oldest-first ordering matters. If B depends on A, you want A applied first; the range syntax does that for you automatically.

pick a range, inclusive lower boundshell
git switch release/v2.1
git cherry-pick 4b1c9af^..9d3e2c7

Conflicts, continue, abort, skip

Cherry-pick uses the same conflict mechanics as merge and rebase. If the patch does not apply cleanly, Git stops mid-operation with conflict markers in your files. The reference describes the state precisely: "The current branch and HEAD pointer stay at the last commit successfully made. The CHERRY_PICK_HEAD ref is set to point at the commit that introduced the change that is difficult to apply."

Three escape hatches:

  • git cherry-pick --continue — after you've edited the files and run git add on them, finish the current pick and proceed to the next one in the range.
  • git cherry-pick --skip — throw away the current pick (do not record it) and proceed to the next.
  • git cherry-pick --abort — cancel the whole sequence and restore the branch to the pre-cherry-pick state.
resolving a cherry-pick conflictshell
git cherry-pick 4b1c9af
# CONFLICT: src/auth.py
# edit src/auth.py to resolve markers
git add src/auth.py
git cherry-pick --continue

Two flags worth memorizing: -x and -n

-x: leave a trail

The -x flag appends a single line to the new commit's message. From the reference: "When recording the commit, append a line that says '(cherry picked from commit …)' to the original commit message in order to indicate which commit this change was cherry-picked from." That line is the audit trail when you go looking, months later, for "where did this fix originally land?"

cherry-pick with provenance lineshell
git cherry-pick -x 4b1c9af

The resulting commit message looks like:

resulting messagetext
Fix auth token expiry off-by-one

The refresh window was compared with <= instead of <, allowing one
extra second past expiry. Tighten the check.

(cherry picked from commit 4b1c9afdc6e5a3b8e9f2107c11d4a3b6e8f9c2d1)
Backport audit trail

Always use -x when backporting fixes to release branches. The provenance line lets future maintainers (and your future self) find the original commit on main, read its review thread, and confirm the change was intentional. The cost is one line in the commit message; the benefit is unambiguous traceability.

-n / --no-commit: stop short

By default cherry-pick records a commit per source commit. -n applies the change to your working tree and index but stops before committing. The reference: "This flag applies the changes necessary to cherry-pick each named commit to your working tree and the index, without making any commit."

Use it when you want to bundle several cherry-picked changes into one composite commit, or when you want to review the staged result before letting it enter history.

bundle three picks into one commitshell
git cherry-pick -n 4b1c9af
git cherry-pick -n 9d3e2c7
git cherry-pick -n a72f5e1
git diff --staged                # inspect the combined result
git commit -m "Backport auth fixes from main"

When NOT to cherry-pick

If you will eventually merge the source branch anyway, cherry-picking creates a near-duplicate commit. Git's merge machinery is good enough that it will often deduplicate via patch-id, but not always — especially if the cherry-picked version diverged during conflict resolution. You can end up with two commits in history that look like the same fix and confuse anyone reading git log.

The rule of thumb: cherry-pick is for surgical, one-off moves between branches whose histories you do not intend to combine. If you'll merge later, prefer merge or rebase from the start.

Duplicate-looking commits

A cherry-picked commit has the same diff but a different SHA. To a quick reader of git log, you have two changes; to Git's three-way merge, you have one logical change replayed in two places. This is fine when the two branches will never merge (a release branch staying alive in parallel). It is a footgun when they will (a long-running feature branch cherry-picking from main).

Backporting workflow

The canonical use case. A bug fix lands on main. Your project supports release/v2.0, release/v2.1, and release/v2.2 with security patches. You want the same fix on every supported release.

backport a fix to three release branchesshell
# the fix is on main as commit 4b1c9af
for branch in release/v2.0 release/v2.1 release/v2.2; do
  git switch "$branch"
  git cherry-pick -x 4b1c9af
  git push origin "$branch"
done
git switch main

The alternative — opening three separate PRs that re-author the fix — is more work and loses the (cherry picked from…) link. The downside of cherry-pick is exactly the duplicate-commit warning above; for parallel release branches that will never re-merge into main, that downside doesn't bite.

The patch flow alternative

Cherry-pick assumes both branches live in the same repository (or one you can fetch from). What if they don't? Imagine a fix you want to send to a project hosted on a mailing list, a fork you don't have push access to, or a repository that no longer exists except in someone's backup. Git's email-patch tooling exists for exactly this: turn commits into .patch files, move them around by any means, and reconstruct them as commits on the other end.

A commit travels from Repo A to Repo B as a .patch file, with no network between them. Repo A commit on feature format-patch 0001-fix.patch RFC 2822 mailbox email / scp / web 0001-fix.patch on Repo B's disk git am Repo B new commit Author, date, and message survive the trip. The new commit's SHA is new.

format-patch: commits to email

From the reference: "The patch produced by git format-patch is in UNIX mailbox format, with a fixed 'magic' time stamp to indicate that the file is output from format-patch rather than a real mailbox." The point of the magic timestamp is so git am can recognize the file's origin and behave accordingly.

Each commit becomes one numbered file, written into the current directory by default:

generate patch files for the last 3 commitsshell
git format-patch -3
# 0001-Fix-auth-token-expiry.patch
# 0002-Add-regression-test-for-auth-expiry.patch
# 0003-Update-changelog.patch

Two common range patterns:

single commit and branch-relative rangeshell
# exactly one commit, by SHA
git format-patch -1 4b1c9af

# every commit on the current branch that origin/main doesn't have yet
git format-patch origin/main..HEAD

# write into a directory instead of the cwd
git format-patch -o /tmp/patches origin/main..HEAD

Open one of the resulting files in any text editor. It looks like an email: From, Subject, body, then a three-dash separator, then a unified diff. That format is the entire interoperability story — any pipeline that moves email or text files can move commits.

am: email back to commits

On the receiving side, git am reads patch files (or a mailbox of them) and replays each as a commit on the current branch. From the reference: "Splits mail messages in a mailbox into commit log messages, authorship information, and patches, and applies them to the current branch."

The author name, author date, and commit message all come out of the email headers, so the original committer keeps credit:

apply a series of patchesshell
git switch feature/upstream-fixes

# apply patches by listing them
git am 0001-Fix-auth-token-expiry.patch 0002-Add-regression-test.patch

# or pipe an entire mbox in
git am < /tmp/patches.mbox

# or apply a whole directory of numbered patches
git am /tmp/patches/*.patch

If a patch doesn't apply cleanly, git am stops and waits for you, exactly like cherry-pick. Edit the conflicting files, run git add on them, then git am --continue. The other escape hatches are --skip (drop this patch and proceed) and --abort (back out the whole series).

One useful flag: --3way tells git am to fall back to a three-way merge using the blob SHAs referenced inside the patch, which can resolve conflicts that a straight apply would refuse.

apply: just the diff

git apply is the lower-level tool. It takes a diff (no email headers, no commit metadata) and patches the working tree and optionally the index. It does not create a commit. Use it when:

  • You have a raw .diff file from somewhere outside Git's ecosystem — a pastebin, a code review tool that doesn't speak format-patch, a generated patch from a static analyzer.
  • You want to inspect or massage the change before deciding to commit, perhaps assigning your own author and message.
  • You want a dry run: git apply --check patch.diff reports whether the patch would apply without touching the tree.
apply a raw diff, no commitshell
git apply --check incoming.diff           # dry-run
git apply incoming.diff                   # patch the working tree
git add -p && git commit                  # decide what to record yourself

The Pro Git book frames this clearly: "If you can, encourage your contributors to use format-patch instead of diff to generate patches for you. You should only have to use git apply for legacy patches and things like that."

Worth a brief mention: git send-email is Git's built-in tool for sending format-patch output to a mailing list. Kernel-style projects use it daily; you may never need it, but if you contribute to the Linux kernel, Postgres, or Git itself, this is the workflow.

When each tool earns its keep

Tool Moves Across repos? Preserves author? Best for
git merge A whole branch's history Same repo (or fetched) Yes Integrating a finished feature into main.
git rebase A chain of commits onto a new base Same repo Yes (rewrites timestamps and SHAs) Linearizing a feature branch before merge.
git cherry-pick One commit (or a hand-picked range) Same repo (or fetched) Yes (new SHA, original author) Backporting a fix to a release branch.
format-patch + am Commits as files Any two repos, no network needed Yes (author, date, message) Mailing-list contributions, transferring across disconnected repos.
git apply A raw diff, no metadata Any two repos No (you commit, you become the author) Applying non-Git patches; preview and edit before committing.

Common pitfalls

Pitfall 1

Cherry-picking a merge commit without -m. A merge commit has two parents, and Git can't pick "the diff" without being told which parent to diff against. Pass -m 1 to mean "treat parent 1 as the mainline" — for example, git cherry-pick -m 1 <merge-sha>. Forgetting this gives you cherry-pick: <sha> is a merge but no -m option was given.

Pitfall 2

Confusing A..B with A^..B. The first excludes A; the second includes it. If you cherry-pick a range and the earliest commit is missing, you used .. when you meant ^... This bites people backporting "this commit and everything after."

Pitfall 3

Treating cherry-pick as "copy and delete." Cherry-pick only copies. The original commit is still on its branch. If you wanted to move the change, you also need to remove it from the source — usually via git revert on the source branch, or an interactive rebase that drops it. Otherwise the fix lives in two places and you'll merge it back later as a near-duplicate.

Pitfall 4

Running git am on a file that isn't a mailbox. git am needs the RFC 2822 envelope that format-patch produces. If someone hands you a bare git diff output and you run git am, you'll get Patch format detection failed. Use git apply for raw diffs and reserve git am for format-patch output.

Worked examples

Example 1: backport a hotfix to two release branches

You've just merged a security fix into main as commit 4b1c9af. The fix needs to ship on release/v2.1 and release/v2.2 immediately.

backport with audit trailshell
git switch release/v2.2
git cherry-pick -x 4b1c9af
git push origin release/v2.2

git switch release/v2.1
git cherry-pick -x 4b1c9af
git push origin release/v2.1

git switch main

The -x flag adds (cherry picked from commit 4b1c9af…) to each new commit's message. Six months later, when someone asks "where did this come from?" they have an exact link back to the canonical fix on main.

Example 2: contribute a fix to a project that lives on a mailing list

You've cloned the project, written a fix on a local branch fix-utf8, and committed it. The project accepts contributions only by email.

format and sendshell
git switch fix-utf8

# one commit ahead of origin/main; produce one patch file
git format-patch origin/main..HEAD
# -> 0001-Fix-UTF-8-decoding-when-BOM-is-present.patch

# inspect the file in your editor; the top half is the email envelope,
# the bottom half is the diff. Then mail it however the project wants.
git send-email --to=dev@example.org 0001-Fix-UTF-8-decoding-when-BOM-is-present.patch

On the maintainer's side, they save the email to disk and run git am, which rebuilds your commit with your name, date, and message intact.

Example 3: apply a foreign diff you can't pull from

A colleague hands you refactor.diff — output of git diff, with no headers and no commit metadata. You want to apply it, review the changes, and commit them under your own name.

apply, review, commitshell
git apply --check refactor.diff       # would it apply cleanly?
git apply refactor.diff                # patch the working tree
git status                             # see what changed
git diff                               # eyeball the changes
git add -p                             # stage selectively
git commit -m "Refactor session handling per discussion"

Because git apply doesn't create a commit, you control the author, date, and message. That's the right tool when the diff has no provenance worth preserving.

Example 4: bundle multiple cherry-picks into one composite commit

You're consolidating three small fixes from main into release/v2.1 as a single squashed commit, because the release maintainer prefers one entry in the changelog per backport batch.

squash three picks into one commitshell
git switch release/v2.1

git cherry-pick -n 4b1c9af
git cherry-pick -n 9d3e2c7
git cherry-pick -n a72f5e1

git diff --staged                          # review the combined patch
git commit -m "Backport auth fixes from main (4b1c9af, 9d3e2c7, a72f5e1)"

The -n flag tells cherry-pick to stop short of committing. Three picks accumulate in the index, then one git commit turns them into a single coherent change. Note that you lose the per-commit provenance line -x would have given you — that's why the SHAs are written into the commit message manually.

Sources & further reading

  • git-cherry-pick Reference Git project · official docs

    The authoritative reference for cherry-pick: every flag, range syntax, conflict-resolution states, and the precise meaning of -x and -n.

  • git-format-patch Reference Git project · official docs

    Specifies the mailbox-format output, the magic timestamp that distinguishes a generated patch from a real email, and the full range syntax for selecting commits.

  • git-am Reference Git project · official docs

    Reverse direction of format-patch: how mailbox messages become commits, including --continue, --abort, --skip, and the --3way fallback.

  • git-apply Reference Git project · official docs

    The lower-level patch-application tool. Use when you have a raw diff without commit metadata, or when you want a dry-run check before touching the tree.

  • Pro Git, 5.3: Maintaining a Project Textbook Scott Chacon and Ben Straub

    The full format-patch / am workflow as practiced on real open-source projects, including why maintainers should prefer format-patch over raw diff for contributor submissions.

  • git cherry-pick Tutorial Atlassian · Git tutorials

    Reader-friendly walkthrough of cherry-pick with the same hotfix and backport motivations covered here. Good companion when you want a second voice on the same material.