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
The two parents of a merge commitMain had commits up to M2 when the pull request with commits P1 and P2 was merged, creating merge commit X. Parent one of X is M2 on main; parent two is P2 at the head of the pull request. The pull request's change is the diff from parent one to X.parent 1 = main, parent 2 = the pull requestmainM1M2Xpull requestM1P1P2parents of X^1 = M2^2 = P2diff X^1..X is what the merge added to main — exactly the pull request The two parents of a merge commitMain had commits up to M2 when the pull request with commits P1 and P2 was merged, creating merge commit X. Parent one of X is M2 on main; parent two is P2 at the head of the pull request. The pull request's change is the diff from parent one to X.parent 1 = main, parent 2 = the pull requestmainM1M2Xpull requestM1P1P2parents of X^1 = M2^2 = P2diff X^1..X is what the merge added to main — exactly the pull request

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
What -m 1 and -m 2 applyWith -m 1 the cherry-pick applies the difference between main before the merge and the merge commit, which is the pull request's change. With -m 2 it applies the difference between the pull request head and the merge commit, which is everything main gained in the meantime.-m 1 (mainline = main)-m 2 (mainline = PR)appliesthe pull requestmain's other changestypical sizesmalllarge, unrelateduse for backportsyesalmost neverwhen the merge was made on main, -m 1 is the answer What -m 1 and -m 2 applyWith -m 1 the cherry-pick applies the difference between main before the merge and the merge commit, which is the pull request's change. With -m 2 it applies the difference between the pull request head and the merge commit, which is everything main gained in the meantime.-m 1 (mainline = main)-m 2 (mainline = PR)appliesthe pull requestmain's other changestypical sizesmalllarge, unrelateduse for backportsyesalmost neverwhen the merge was made on main, -m 1 is the answer

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.

Merge commit or individual commits?If the whole pull request should go to the release branch, cherry-pick the merge with -m 1. If only some of its commits are fixes and the rest are features, pick those commits individually. If the pull request itself merged main in partway, picking individual commits avoids carrying that merge's changes.What should reach the release branch?the whole PRPick the merge-x -m 1only the fix commitsPick commitsin original orderPR merged main midwayPick commitsskip the inner mergegit log --oneline X^1..X^2 lists the pull request's own commits Merge commit or individual commits?If the whole pull request should go to the release branch, cherry-pick the merge with -m 1. If only some of its commits are fixes and the rest are features, pick those commits individually. If the pull request itself merged main in partway, picking individual commits avoids carrying that merge's changes.What should reach the release branch?the whole PRPick the merge-x -m 1only the fix commitsPick commitsin original orderPR merged main midwayPick commitsskip the inner mergegit log --oneline X^1..X^2 lists the pull request's own commits
# 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.