Cherry-picking a merge commit with --mainline Jump to heading
Teams that merge pull requests with merge commits often want to backport a whole pull request, not its individual commits. The natural move — git cherry-pick <merge-sha> — fails with “is a merge but no -m option was given”. A merge commit has two parents, so “the change this commit introduced” is ambiguous: relative to which parent? The -m (or --mainline) option answers that question, and choosing the wrong number backports the inverse of what you meant: everything main had that the feature branch lacked. This page explains the parent numbering, the safe default, and when picking the commits individually is the better choice. It belongs to cherry-pick and backporting.
When to use this approach Jump to heading
- Pull requests land on main as merge commits, and you need to backport one as a unit.
- The pull request contains several commits that only make sense together.
- You want the backport to be one commit that is easy to find and revert on the release branch.
- If pull requests are squash-merged, there is no merge commit and an ordinary cherry-pick of the squash commit works; see squash vs merge vs rebase decision matrix.
Step 1 — Identify the parents of the merge commit Jump to heading
A merge commit made by git merge feature on main has main’s previous tip as parent 1 and the feature branch’s tip as parent 2. Forge merge buttons follow the same convention.
git log -1 --format='%h %s%nparents: %p' 9c2f4d1
git log --oneline -1 9c2f4d1^1 # main before the merge
git log --oneline -1 9c2f4d1^2 # the pull request's head Step 2 — Check what each choice would apply Jump to heading
Before cherry-picking, look at the diff relative to each parent. Relative to parent 1, you see the pull request’s changes. Relative to parent 2, you see everything main had gained since the pull request branched — usually large and almost never what you want.
git diff --stat 9c2f4d1^1 9c2f4d1 # -m 1: the pull request's changes
git diff --stat 9c2f4d1^2 9c2f4d1 # -m 2: main's changes the PR did not have Step 3 — Cherry-pick with mainline 1 Jump to heading
Apply the merge relative to its first parent, with -x so the backport records its source.
git switch release/2.4
git cherry-pick -x -m 1 9c2f4d1
git log -1 --format='%h %s%n%b' # one ordinary commit, with "(cherry picked from commit 9c2f4d1…)" The result is a single non-merge commit on the release branch containing the whole pull request’s change. It does not record the pull request’s individual commits, which keeps the release branch’s history simple.
# Verification: the backport's diff matches the pull request's diff
git diff 9c2f4d1^1 9c2f4d1 > /tmp/pr.diff
git show --format= HEAD > /tmp/backport.diff
diff /tmp/pr.diff /tmp/backport.diff && echo "identical change" Step 4 — Handle conflicts the same way as any cherry-pick Jump to heading
Conflicts work exactly as for an ordinary cherry-pick. The base in the markers is parent 1 of the merge commit — main just before the pull request landed — which may differ significantly from the release branch. The analysis in resolving cherry-pick conflicts on older branches applies unchanged.
git status --short | grep '^UU'
# resolve, then
git add -A && git cherry-pick --continue Step 5 — Know when to pick the individual commits instead Jump to heading
Picking the merge flattens the pull request into one commit. That is usually what a release branch wants, but not always.
# List the pull request's own commits, excluding any merges of main into it
git log --oneline --no-merges 9c2f4d1^1..9c2f4d1^2
# Pick a subset, oldest first
git cherry-pick -x a11f0e2 b72c9d4 Step 6 — Name the pull request in the backport Jump to heading
A flattened backport loses the pull request’s commit-by-commit story, so put the context back into the message. Edit the cherry-pick’s message to name the original pull request and summarise what it contained; reviewers of the release branch then see what they are approving without opening main’s history.
git cherry-pick -x -m 1 --edit 9c2f4d1
# Subject: [2.4] Fix credit note rounding (backport of #812)
# Body: list the pull request's commits, one line each:
# - extract rounding helper
# - round credit note totals half-even
# - handle zero-value credit notes # Generate that list rather than typing it
git log --format=' - %s' --reverse --no-merges 9c2f4d1^1..9c2f4d1^2 The -x trailer still names the merge commit, which links back to the pull request on main for anyone who needs the full detail. Together, the summary and the trailer make the single backport commit as informative as the original series, without carrying its history.
Validation checklist Jump to heading
Frequently Asked Questions Jump to heading
Can I make -m 1 the default? Jump to heading
Git does not have a configuration option for it, deliberately, because the right parent depends on how the merge was made. A small alias that always passes -m 1 is common in teams where every merge commit is made on main.
Does cherry-picking a merge break future merges between the branches? Jump to heading
No more than any cherry-pick. The release branch gains a new commit with the same change, and if release is later merged into main, Git sees identical changes on both sides and usually applies them cleanly.
What does git revert -m 1 do? Jump to heading
The same parent logic in reverse: it creates a commit undoing the pull request’s changes relative to main. That is how a merged pull request is reverted, covered in undoing a bad merge on a shared branch.
Related Jump to heading
- Cherry-Pick & Backporting — the parent topic.
- Automating Backport Pull Requests with Labels — bots that pick merges this way.
- Backporting a Range of Commits in Order — the alternative to picking the merge.
- Reading History with --first-parent — finding the merge commits worth backporting.