Intermediate 3 min · March 30, 2026

Git Cherry-Pick — 5 Untraceable Backports

Security found 5 branches with same commit message but no source hash, making fix verification impossible.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • -x flag — appends source hash to commit message for traceability
  • --no-commit — stage changes without committing (review before committing)
  • A..B range syntax — cherry-pick a sequence of commits (exclusive of A)
  • -m flag — required for cherry-picking merge commits (specifies which parent)
Plain-English First

Cherry picking in Git means 'I want exactly that one commit from that other branch, applied here.' You don't take the whole branch — just the specific change you need. It's the scalpel to git merge's broad brush. Most useful for backporting a critical fix to a release branch without dragging along every other change made since the release.

Cherry pick applies a specific commit's changes onto your current branch. It solves the problem of needing one fix from another branch without merging everything else on that branch.

The primary use case is backporting: a security fix landed on main, and you need it on release/2.4, release/2.3, and release/2.2 — without pulling in every other change since those releases. Cherry pick with -x gives you the fix with full traceability to the source commit.

Common misconceptions: that cherry pick duplicates the commit (it creates a new commit with the same diff but a different hash), that it's always dangerous (it's the right tool for backporting), and that you can cherry-pick merge commits without special handling (you can't — you need the -m flag to specify which parent to diff against). The real danger is cherry-picking without -x, which destroys traceability across release branches.

Basic Cherry Pick Usage

Find the hash of the commit you want, check out the destination branch, run git cherry-pick <hash>. Git applies the diff from that specific commit onto your current branch as a new commit.

The -x flag appends (cherry picked from commit <hash>) to the commit message. This traceability is not optional for any team maintaining release branches — without it you lose the ability to reason about which fixes have been applied where.

io/thecodeforge/git/CherryPick.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# io.thecodeforge — Git Cherry Pick

# ─────────────────────────────────────────────────────────────
# BASIC CHERRY PICK WITH TRACEABILITY
# ─────────────────────────────────────────────────────────────

# Find the commit hash to cherry pick
git log --oneline feature/security-fix
# a3f9c2e Fix SQL injection in payment query
# 7b2d4f1 Add input validation

# Cherry pick to release branch with traceability flag
git checkout release/2.4
git cherry-pick -x a3f9c2e
# -x appends '(cherry picked from commit a3f9c2e)' to the message.
# This is your traceability link. Never skip it.

# ─────────────────────────────────────────────────────────────
# CHERRY PICK A RANGE OF COMMITS
# ─────────────────────────────────────────────────────────────

# Range: commits AFTER a3f9c2e up to and INCLUDING d4f8b3c
# Note: a3f9c2e itself is EXCLUDED (exclusive start)
git cherry-pick -x a3f9c2e..d4f8b3c

# To INCLUDE the starting commit, use the caret syntax:
git cherry-pick -x a3f9c2e^..d4f8b3c
# The ^ means 'parent of' — so a3f9c2e^..d4f8b3c includes a3f9c2e.

# ─────────────────────────────────────────────────────────────
# CHERRY PICK SPECIFIC NON-CONSECUTIVE COMMITS
# ─────────────────────────────────────────────────────────────

# List individual hashes — order matters
git cherry-pick -x a3f9c2e 7b2d4f1 9c3e8a2
# Applied in sequence: first a3f9c2e, then 7b2d4f1, then 9c3e8a2.
# If any commit conflicts, the sequence pauses for resolution.

# ─────────────────────────────────────────────────────────────
# CHERRY PICK WITHOUT COMMITTING (--no-commit)
# ─────────────────────────────────────────────────────────────

# Stage the changes without creating a commit
# Useful when you want to review or modify before committing
git cherry-pick --no-commit a3f9c2e
git status   # Review staged changes
git diff --cached  # See exactly what was staged

# Then commit with your own message (or amend to combine with other changes)
git commit -m "Backport: Fix SQL injection in payment query (from a3f9c2e)"

# ─────────────────────────────────────────────────────────────
# CHERRY PICK FROM ANOTHER BRANCH (latest commit)
# ─────────────────────────────────────────────────────────────

# Cherry pick the tip of another branch
git cherry-pick -x feature/security-fix
# Equivalent to: git cherry-pick -x $(git rev-parse feature/security-fix)

# ─────────────────────────────────────────────────────────────
# VERIFY: Confirm the cherry pick was applied correctly
# ─────────────────────────────────────────────────────────────

# Show the new commit on the destination branch
git log --oneline -1
# b5c9d3f (HEAD -> release/2.4) Fix SQL injection in payment query

# Verify the source reference is in the message
git log -1 --format='%B' | grep 'cherry picked from'
# (cherry picked from commit a3f9c2e)

# Diff the cherry-picked file against the source commit's version
git diff release/2.4:src/main/java/io/thecodeforge/payment/PaymentQueryBuilder.java \
       a3f9c2e:src/main/java/io/thecodeforge/payment/PaymentQueryBuilder.java
# Should show no differences (or only expected context differences).
Output
[release/2.4 b5c9d3f] Fix SQL injection in payment query
Date: Mon Mar 30 14:22:00 2026 +0530
1 file changed, 3 insertions(+), 1 deletion(-)
(cherry picked from commit a3f9c2e)
Cherry Pick Copies the Diff, Not the Commit
  • New commit with new hash — not a reference to the original
  • The diff is applied against the destination branch's current state
  • If the destination has diverged, the diff may conflict or produce different results
  • The -x flag creates the traceability link back to the source commit
Production Insight
The --no-commit flag is underused but critical for complex backports. When cherry-picking a fix that needs minor adjustments for an older release (different API version, different import paths), --no-commit lets you stage the cherry-pick, make the adjustments, and commit once. Without it, you would cherry-pick (creating a commit), then amend (creating a second commit with a new hash), which muddies the history. The --no-commit pattern produces one clean commit that is traceable to the source.
Key Takeaway
git cherry-pick -x <hash> applies a specific commit's diff to your current branch with source traceability. Always use -x. Use --no-commit when the cherry-pick needs adjustments before committing. Use A^..B range syntax to include the starting commit (without ^, the start is excluded).

Handling Cherry Pick Conflicts

If the cherry-picked commit touches code that has diverged significantly on the destination branch, you will get a merge conflict. Git stops mid-pick and leaves you in a CHERRY-PICKING state — visible in your shell prompt if you use a Git-aware prompt.

Resolve the conflict files, git add them, then git cherry-pick --continue. If you change your mind, git cherry-pick --abort restores the branch to its pre-pick state.

io/thecodeforge/git/CherryPickConflict.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# io.thecodeforge — Cherry Pick Conflict Resolution

# ─────────────────────────────────────────────────────────────
# CONFLICT DURING SINGLE CHERRY PICK
# ─────────────────────────────────────────────────────────────

git cherry-pick -x a3f9c2e
# CONFLICT (content): Merge conflict in
# src/main/java/io/thecodeforge/payment/PaymentQueryBuilder.java

# Check state
git status
# You are currently cherry-picking commit a3f9c2e.
# (fix conflicts and run "git cherry-pick --continue")
# (run "git cherry-pick --abort" to cancel)

# Edit conflicted file and resolve
# ... fix conflicts in PaymentQueryBuilder.java ...

# Stage and continue
git add src/main/java/io/thecodeforge/payment/PaymentQueryBuilder.java
git cherry-pick --continue
# Opens editor for commit message — keep or modify

# ─────────────────────────────────────────────────────────────
# ABORT: Return to pre-pick state
# ─────────────────────────────────────────────────────────────

git cherry-pick --abort
# Branch restored to state before the cherry-pick started.
# No changes. Clean slate.

# ─────────────────────────────────────────────────────────────
# SEQUENCE CHERRY PICK: Multiple commits, one may conflict
# ─────────────────────────────────────────────────────────────

git cherry-pick -x a3f9c2e 7b2d4f1 9c3e8a2
# a3f9c2e applied cleanly.
# 7b2d4f1 conflicts...

# Resolve, stage, continue — 9c3e8a2 will be applied next
git add <resolved-files>
git cherry-pick --continue
# 9c3e8a2 applied. All three commits now on the destination branch.

# ─────────────────────────────────────────────────────────────
# SKIP: Skip a commit in a sequence that's causing too much trouble
# ─────────────────────────────────────────────────────────────

git cherry-pick --skip
# Skips the current commit in the sequence.
# Remaining commits continue to be applied.
# Use when one commit in a sequence is too divergent to resolve.

# ─────────────────────────────────────────────────────────────
# POST-RESOLUTION VERIFICATION
# ─────────────────────────────────────────────────────────────

# After resolving, verify no code was silently dropped during conflict resolution
git diff HEAD~1 HEAD  # See what the cherry-pick actually committed

# Compare against the source commit's intent
git show a3f9c2e -- src/main/java/io/thecodeforge/payment/PaymentQueryBuilder.java
# Verify your resolution includes all the changes from the source commit.
Output
[release/2.4 c7d2e5f] Fix SQL injection in payment query
(cherry picked from commit a3f9c2e)
Conflict Resolution Can Silently Drop Code
  • Conflict markers show destination version vs source version — correct resolution may need both
  • Silent drops: resolving by picking one side may discard critical parts of the fix
  • Verification: diff your resolution against git show <source-commit> after resolving
  • Use --no-commit for complex backports — review staged changes before committing
Production Insight
Cherry-pick conflict resolution is where partial-backport bugs originate. The engineer resolves conflicts by picking the 'incoming' side, not realizing the destination branch has different surrounding code that requires the fix to be adapted, not just applied. The result: the cherry-pick commits cleanly, but the fix doesn't work on the destination branch because context-dependent code was dropped during resolution. The verification step — diffing the resolution against the source commit's intent — catches these bugs. Make it a mandatory step in your backporting runbook.
Key Takeaway
Cherry-pick conflicts resolve like merge conflicts: fix files, git add, git cherry-pick --continue. Use --abort to cancel. Use --skip to skip a problematic commit in a sequence. Always verify after resolution that no code was silently dropped — diff against the source commit's intent.

Cherry Picking Merge Commits: The -m Flag

A merge commit has two (or more) parents. When you cherry-pick a merge commit, Git doesn't know which parent to diff against — it needs the -m flag to specify the parent number.

-m 1 means diff against the first parent (usually the branch that received the merge — typically main). -m 2 means diff against the second parent (usually the branch that was merged in). In most cases, -m 1 is what you want — it applies all the changes that the merge introduced relative to main.

However, cherry-picking merge commits is almost always the wrong approach. The merge commit's diff includes every change from the merged branch, which may be much more than the specific fix you need. Instead, identify the individual commits inside the merge and cherry-pick those.

io/thecodeforge/git/CherryPickMerge.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# io.thecodeforge — Cherry Picking Merge Commits

# ─────────────────────────────────────────────────────────────
# WRONG: Cherry-picking a merge commit directly
# ─────────────────────────────────────────────────────────────

git cherry-pick -x abc1234
# error: abc1234 is a merge but no -m option was given
# Git doesn't know which parent to diff against.

# WRONG: Using -m 1 blindly
# This applies ALL changes from the merged branch — not just the fix you need.
git cherry-pick -x -m 1 abc1234
# This may work, but you're pulling in everything the merge introduced.
# If the merge included 15 commits, all 15 are now on your branch.

# ─────────────────────────────────────────────────────────────
# RIGHT: Cherry-pick the individual commits inside the merge
# ─────────────────────────────────────────────────────────────

# Step 1: Identify the merge commit
git log --oneline --merges -1
# abc1234 Merge branch 'feature/security-fix' into main

# Step 2: Find the individual commits that were merged
# List commits on the merged branch that aren't on main's first parent
git log --oneline abc1234 ^abc1234^1
# Output:
# a3f9c2e Fix SQL injection in payment query
# 7b2d4f1 Add input validation
# 9c3e8a2 Add unit tests for SQL injection prevention

# Step 3: Cherry-pick only the commits you need
git checkout release/2.4
git cherry-pick -x a3f9c2e
# Only the SQL injection fix — not the input validation or unit tests.

# ─────────────────────────────────────────────────────────────
# WHEN -m IS ACTUALLY NEEDED
# ─────────────────────────────────────────────────────────────

# Rare case: you need the entire merge's effect on a release branch.
# Example: a release branch needs a complete feature that was merged to main.
git cherry-pick -x -m 1 abc1234
# -m 1: diff against first parent (main before the merge)
# This applies all changes the merge introduced.
# Creates multiple commits — one for each file changed by the merge.

# -m 2: diff against second parent (the merged branch)
# Almost never what you want. This reverses the merge direction.
Output
# git log --oneline abc1234 ^abc1234^1
# a3f9c2e Fix SQL injection in payment query
# 7b2d4f1 Add input validation
# 9c3e8a2 Add unit tests for SQL injection prevention
# git cherry-pick -x a3f9c2e
# [release/2.4 b5c9d3f] Fix SQL injection in payment query
# (cherry picked from commit a3f9c2e)
Cherry-Picking Merge Commits Almost Always Means You Want the Individual Commits
  • Merge commit diff = all changes from the merged branch (often too much)
  • Individual commits = specific changes (usually what you need)
  • -m 1 = diff against first parent (main). -m 2 = diff against second parent (merged branch).
  • Use git log --oneline <merge> ^<merge>^1 to list commits inside a merge
Production Insight
The -m flag confusion is a common source of accidental large backports. Engineers see a merge commit that 'contains the fix they need' and cherry-pick it with -m 1, not realizing the merge included 12 other commits that are now also on the release branch. The result: the release branch gets a security fix plus only the ones you need.
Key Takeaway
Cherry-picking merge commits requires -m to specify which parent to diff against. But in 95% of cases, you want the individual commits inside the merge, not the merge commit itself. Use git log --oneline <merge> ^<merge>^1 to list the individual commits and cherry-pick those instead.

Backporting Strategy: Multi-Branch Patch Workflow

Backporting is the primary production use case for cherry-pick. A fix lands on main, and you need it on multiple release branches. The workflow must be systematic to avoid missing branches or introducing partial 12 unrelated feature changes, which may introduce new bugs. The discipline: always list the individual commits inside a merge before cherry-picking, and cherry-pick fixes.

The correct order: fix on main first, verify the fix works, then cherry-pick to release branches from oldest to newest. Cherry-picking from oldest to newest reduces conflict probability because each successive branch is closer to main's state.

Always use -x. After cherry-picking to each branch, run the relevant tests on that branch. A fix that works on main may not work on release/2.1 due to API differences.

io/thecodeforge/git/BackportWorkflow.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# io.thecodeforge — Backporting Workflow

# ─────────────────────────────────────────────────────────────
# SCENARIO: CVE-2026-1234 SQL injection fix needs to be
# backported to release/2.4, release/2.3, release/2.2
# ─────────────────────────────────────────────────────────────

# STEP 1: The fix is already on main — verify it
git log --oneline -1 main
# a3f9c2e Fix SQL injection in payment query

# STEP 2: Identify all release branches that need the fix
git branch -r | grep 'release/'
# origin/release/2.4
# origin/release/2.3
# origin/release/2.2
# origin/release/2.1  (EOL — skip)
# origin/release/2.0  (EOL — skip)

# STEP 3: Backport from oldest to newest (reduces conflict probability)

# Backport to release/2.2 (oldest supported)
git checkout release/2.2
git cherry-pick -x a3f9c2e
# If conflicts: resolve, verify, continue
# Run tests: ./gradlew test --tests '*PaymentQueryBuilder*'

# Backport to release/2.3
git checkout release/2.3
git cherry-pick -x a3f9c2e
# Run tests: ./gradlew test --tests '*PaymentQueryBuilder*'

# Backport to release/2.4 (newest — least likely to conflict)
git checkout release/2.4
git cherry-pick -x a3f9c2e
# Run tests: ./gradlew test --tests '*PaymentQueryBuilder*'

# STEP 4: Push all backported branches
git push origin release/2.2 release/2.3 release/2.4

# STEP 5: Verify traceability — all branches should reference the source
git log --all --grep='cherry picked from commit a3f9c2e' --oneline
# f1a2b3c (release/2.4) Fix SQL injection in payment query
# d4e5f6g (release/2.3) Fix SQL injection in payment query
# h7i8j9k (release/2.2) Fix SQL injection in payment query

# STEP 6: Tag the hotfix releases
git checkout release/2.2 && git tag -a v2.2.5 -m "Hotfix: CVE-2026-1234"
git checkout release/2.3 && git tag -a v2.3.3 -m "Hotfix: CVE-2026-1234"
git checkout release/2.4 && git tag -a v2.4.2 -m "Hotfix: CVE-2026-1234"
git push origin v2.2.5 v2.3.3 v2.4.2
Output
# git log --all --grep='cherry picked from commit a3f9c2e' --oneline
# f1a2b3c (release/2.4) Fix SQL injection in payment query
# d4e5f6g (release/2.3) Fix SQL injection in payment query
# h7i8j9k (release/2.2) Fix SQL injection in payment query
Backport Oldest to Newest
  • Oldest branch is most divergent from main — most likely to conflict
  • Each successive branch is closer to main — less likely to conflict
  • If you cherry-pick newest first, conflict resolutions may not transfer to older branches
  • Always test on each branch after cherry-picking — API differences may break the fix
Production Insight
The backport workflow must include per-branch testing. A fix that works on main may fail on release/2.2 because the API it depends on changed between releases. The PaymentQueryBuilder class may have a different method signature on release/2.2, or the SQL injection vulnerability may manifest differently due to different query construction patterns. Cherry-pick applies the diff blindly — only testing confirms the fix works on the destination branch. CI pipelines for release branches should trigger on cherry-pick pushes and run the relevant test suite.
Key Takeaway
Backport systematically: fix on main first, then cherry-pick to release branches from oldest to newest. Always use -x. Always test on each branch after cherry-picking — the fix may need adaptation for older API versions. Verify traceability with git log --all --grep='cherry picked from commit <hash>'.

The Duplicate Commit Problem: When Cherry Pick Meets Merge

Cherry-picked commits have different hashes than the originals. When two branches that both received the same cherry-pick are later merged, Git sees two different commits with the same content and includes both in the merge result. This creates duplicate entries in git log that confuse code review and git blame.

The problem is cosmetic — the code is correct, having the same change applied twice doesn't double the effect. But it clutters history and makes git log harder to read. More importantly, if the cherry-picked commits had different conflict resolutions on different branches, the merge may include both versions, creating a semantic conflict that Git can't detect.

Prevention: cherry-pick to leaf branches only. Don't cherry-pick to both a feature branch and its merge target. If you need a fix on multiple branches, cherry-pick to each leaf branch independently.

io/thecodeforge/git/CherryPickDuplicates.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# io.thecodeforge — Cherry Pick Duplicate Commits

# ─────────────────────────────────────────────────────────────
# SCENARIO: Duplicate commits after merge
# ─────────────────────────────────────────────────────────────

# You cherry-picked commit a3f9c2e to release/2.4
git checkout release/2.4
git cherry-pick -x a3f9c2e
# Creates commit b5c9d3f on release/2.4

# Later, release/2.4 is merged into release/2.3 (which also got the cherry-pick)
git checkout release/2.3
git cherry-pick -x a3f9c2e
# Creates commit e4f5g6h on release/2.3

# Now merge release/2.4 into release/2.3
git merge release/2.4
# Git sees b5c9d3f (from release/2.4) is NOT an ancestor of release/2.3
# because it has a different hash than e4f5g6h.
# Both commits appear in the merged history.

# Result: git log shows two commits with the same message and content
# but different hashes. This is the duplicate commit problem.

# ─────────────────────────────────────────────────────────────
# DETECTING DUPLICATES
# ─────────────────────────────────────────────────────────────

# Find all cherry-picked commits on all branches
git log --all --grep='cherry picked from commit' --oneline
# b5c9d3f (release/2.4) Fix SQL injection in payment query
# e4f5g6h (release/2.3) Fix SQL injection in payment query
# Both reference a3f9c2e — same source, different hashes.

# Find commits with identical diffs (content duplicates)
git log --all --oneline | while read hash msg; do
  git show --stat $hash | head -1
  git diff $hash~1 $hash --stat
done | sort | uniq -d
# This finds commits that changed the same files with the same stats.

# ─────────────────────────────────────────────────────────────
# PREVENTING DUPLICATES
# ─────────────────────────────────────────────────────────────

# Rule: Cherry-pick to leaf branches only.
# If release/2.3 will be merged into release/2.4,
# cherry-pick to release/2.3 only. Don't also cherry-pick to release/2.4.
# The merge will carry the fix forward.

# If you must cherry-pick to both independently (no merge relationship),
# the duplicates are acceptable — the branches are independent.
Output
# git log --all --grep='cherry picked from commit a3f9c2e' --oneline
# b5c9d3f (release/2.4) Fix SQL injection in payment query
# e4f5g6h (release/2.3) Fix SQL injection in payment query
Duplicate Commits Are Cosmetic, Not Functional
  • Same content, different hashes = duplicate commit in merged history
  • Cosmetic clutter: git log shows two entries for the same logical change
  • Real danger: different conflict resolutions on different branches may both survive the merge
  • Prevention: cherry-pick to leaf branches only. Let merges carry fixes forward.
Production Insight
The duplicate commit problem becomes severe in teams with many release branches. A security fix cherry-picked to 5 release branches creates 5 duplicate commits. When those branches are periodically merged (in GitFlow, release branches merge into each other), the duplicates multiply. git log becomes unreadable. git blame shows the cherry-pick hash instead of the original authorship. The solution: use git log --first-parent to see the integration timeline without cherry-pick noise, and enforce cherry-pick-to-leaf-only policies.
Key Takeaway
Cherry-picked commits have different hashes. When branches with the same cherry-pick are merged, duplicate commits appear in history. This is cosmetic but clutters git log and git blame. Prevention: cherry-pick to leaf branches only — let merges carry fixes forward. Use git log --first-parent to filter out cherry-pick noise.
● Production incidentPOST-MORTEMseverity: high

Security Fix Backported Without -x: Three Months of Untraceable Patches

Symptom
A security audit required verifying that CVE-2026-1234 (SQL injection in PaymentQueryBuilder) was patched on all supported release branches. The team checked git log on each branch and found commits with the message 'Fix SQL injection in payment query' but no source hash reference. They could not determine if the commits were original fixes or cherry-picks, and whether all five branches had the same fix.
Assumption
The engineer who backported the fix assumed the commit message was sufficient for traceability. They did not know about the -x flag or considered it optional. The team assumed they could always determine patch status by reading commit messages.
Root cause
1. The fix was committed to main as a3f9c2e 'Fix SQL injection in payment query'. 2. The engineer cherry-picked a3f9c2e to release/2.4, release/2.3, release/2.2, release/2.1, and release/2.0 — all without -x. 3. Each cherry-pick created a new commit with the same message but a different hash, and no reference to the source commit. 4. Three months later, a second vulnerability was found in the same PaymentQueryBuilder class. 5. The security audit required verifying that the original fix was present on all five branches. 6. Without (cherry picked from commit a3f9c2e each branch's PaymentQueryBuilder.java against main to confirm the fix was present. 7. Two branches had the fix. One had a partial fix (conflict resolution during cherry-pick dropped a null check). Two had the fix but with different implementations (the engineer had to resolve conflicts and made different choices on different branches).
Fix
1. Retroactively verified fix presence by diffing each branch's PaymentQueryBuilder.java against the known-good version on main. 2. Found the partial fix on release/2.1: the conflict resolution during cherry-pick had dropped a null check. Applied the missing null check as a new commit. 3. Going forward: added a pre-push hook that checks for cherry-picked commits without -x and warns the developer. 4. Updated the team's backporting runbook to require -x on all cherry-picks. 5. Added a CI check that scans commit messages on release branches) in the messages, the team had to manually diff for (cherry picked from commit and flags any backport-like commits without the annotation.
Key lesson
  • Always use -x on cherry-picks. It's not optional for teams maintaining release branches. The traceability it provides is essential for security audits and release management.
  • Conflict resolution during cherry-pick can silently drop code. After resolving conflicts, always diff the result against the source commit to verify no code was lost.
  • Pre-push hooks and CI checks can enforce -x usage. Don't rely on documentation — automate the enforcement.
  • When maintaining multiple release branches, cherry-pick traceability is a security requirement, not a convenience feature.
Production debug guideSystematic recovery paths for cherry-pick failures and traceability problems.5 entries
Symptom · 01
Cherry pick produces unexpected conflicts on a file you didn't expect
Fix
1. The destination branch has diverged from the source in ways that affect the cherry-picked commit's context. 2. Run git diff <parent-of-source-commit> <source-commit> to see exactly what the source commit changed. 3. Compare that diff against the destination branch's version of the same files. 4. If the destination branch already has different code in the same area, the conflict is expected — resolve manually. 5. After resolving, diff your resolution against the source commit's intent to verify no code was silently dropped.
Symptom · 02
Cherry-picked code doesn't work on the destination branch even though there were no conflicts
Fix
1. The cherry-pick applied cleanly but the surrounding code on the destination branch is different. 2. The fix may depend on functions, imports, or data structures that exist on the source branch but not the destination. 3. Run git show <source-commit> to see the full context of the original fix. 4. Check if the cherry-picked code references anything not present on the destination branch. 5. If dependencies are missing, cherry-pick those dependency commits first, or manually add the missing code.
Symptom · 03
Duplicate commits appear after merging branches that both received the same cherry-pick
Fix
1. You cherry-picked commit X to branch A, then merged branch A into branch B which also had commit X cherry-picked. 2. Git sees the cherry-picked commits as different objects (different hashes) and includes both in the merge. 3. To detect duplicates: git log --oneline --grep='cherry picked from' to find all cherry-picked commits. 4. To prevent: avoid cherry-picking to both a branch and its merge target. Cherry-pick to the leaf branch only. 5. If already merged: the duplicate commits are cosmetic — they don't cause code issues but clutter git log.
Symptom · 04
'is a merge but no -m option was given' error when cherry-picking a merge commit
Fix
1. The target commit is a merge commit. Git needs to know which parent to diff against. 2. Option A: Use -m 1 to diff against the first parent (usually main). git cherry-pick -x -m 1 <merge-hash>. 3. Option B (usually better): Identify the individual commits inside the merge and cherry-pick those instead. 4. Run git log --oneline <merge-hash> ^<parent-2-hash> to see the commits that were merged. 5. Cherry-pick the individual commits: git cherry-pick -x <commit1> <commit2> <commit3>.
Symptom · 05
Cannot determine which release branches received a specific fix
Fix
1. If -x was used: git log --all --grep='cherry picked from commit <source-hash>' finds all cherry-picks of conflicts, git add, git that commit. 2. If -x was NOT used: search by commit message content. git log --all --grep='Fix SQL injection' --oneline. 3. For definitive verification: diff the relevant file on each branch against the known-good version. 4. Going forward: enforce -x via pre-push hooks and CI checks. 5. Create a release branch audit script that lists all cherry-picked commits and their source hashes.
★ Git Cherry Pick Triage Cheat SheetFast recovery for cherry-pick failures and traceability issues.
Cherry pick conflict — code diverged on destination branch
Immediate action
Resolve conflicts or abort if too complex.
Commands
git status (see which files have conflicts)
git cherry-pick --abort (escape to pre-pick state)
Fix now
If resolving: git add <resolved-files> && git cherry-pick --continue. Verify with diff against source commit intent.
'is a merge but no -m option was given'+
Immediate action
Cherry-pick individual commits instead of the merge commit.
Commands
git log --oneline <merge-hash> ^<merge-hash>^1 (list commits inside the merge)
git cherry-pick -x <commit1> <commit2> <commit3> (cherry-pick individual commits)
Fix now
If you must cherry-pick the merge: git cherry-pick -x -m 1 <merge-hash>
Cherry-picked code compiles but doesn't work — missing dependencies+
Immediate action
Check if the fix depends on code not present on the destination branch.
Commands
git show <source-commit> (see full context of original fix)
grep -r '<missing-symbol>' src/ (search for missing dependency on destination)
Fix now
Cherry-pick the dependency commits first, or manually add the missing code.
Cannot find which branches got a specific fix — no -x traceability+
Immediate action
Search by message content and verify by diffing.
Commands
git log --all --grep='Fix SQL injection' --oneline
git diff <branch>:<file> main:<file> (compare against known-good version)
Fix now
Enforce -x going forward with pre-push hook: reject cherry-picks without '(cherry picked from commit' in message.
Duplicate commits after merging branches that both got the same cherry-pick+
Immediate action
Detect duplicates — they are cosmetic, not functional.
Commands
git log --oneline --grep='cherry picked from' (find all cherry-picks)
git log --oneline --all --grep='cherry picked from commit <source-hash>' (find duplicates of specific commit)
Fix now
Prevent: cherry-pick to leaf branches only, not to both a branch and its merge target.
Cherry Pick vs Merge vs Rebase
Aspectgit cherry-pickgit mergegit rebase
What it transfersSpecific commits (by hash)All commits from source branchAll commits replayed on new base
Use caseBackporting fixes to release branchesIntegrating feature branches into mainCleaning up local branch before PR
History shapeNew commit on destination (different hash)Merge commit with two parentsLinear history (rewritten commits)
Traceability-x flag records source hashMerge commit references both branchesCommits are rewritten — original hashes lost
Safe on shared branchesYes — creates new commit, no rewriteYes — does not rewrite historyNo — rewrites history
Conflict frequencyLow (single commit's diff)Medium (all diverged changes)High (once per commit during replay)
Duplicate commit riskHigh if cherry-picked to multiple branchesLow — merge commits are uniqueN/A — rebase rewrites, doesn't copy
Best forSelective backportingTeam integrationLocal branch cleanup

Key takeaways

1
git cherry-pick -x <hash> applies a specific commit to your current branch and records the source hash in the message
always use -x for traceability.
2
Cherry-pick conflicts resolve the same way as merge conflicts
fix the files, git add, then git cherry-pick --continue.
3
Never cherry-pick merge commits without understanding -m
cherry-pick the individual commits inside the merge instead.
4
Cherry pick is the right tool for backporting fixes to release branches. It's the wrong tool for keeping long-lived branches in sync
use merge or rebase for that.
5
Backport from oldest to newest release branch to reduce conflict probability.
6
Always test on each destination branch after cherry-picking
the fix may need adaptation for older API versions.
7
Cherry-pick to leaf branches only to prevent duplicate commits in merged history.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What does git cherry-pick do?
02
What is the -x flag in git cherry-pick?
03
How do I cherry-pick a range of commits?
04
What happens if I cherry-pick a commit that's already been applied to the destination branch?
05
How do I cherry-pick a merge commit?
🔥

That's Git. Mark it forged?

3 min read · try the examples if you haven't

Previous
Git Stash: Save and Restore Work in Progress
16 / 19 · Git
Next
Git Reset: Hard, Soft and Mixed Explained