
Cherry-Pick: The Art of Commit Surgery
Master git cherry-pick, from basic usage `abc123` to serial application, complex conflict resolution, and automation for hotfixes and backports.
✨TL;DR / Executive Summary
Master git cherry-pick, from basic usage `abc123` to serial application, complex conflict resolution, and automation for hotfixes and backports.
💡 TL;DR (Too Long; Didn't Read)
Cherry-pick is Git's ability to apply specific commits from one branch to another, like surgical code transplantation. It goes far beyond basic usage
git cherry-pick abc123- it includes serial application, complex conflict resolution, reverse cherry-pick, and automation for multi-version releases. It's fundamental for production hotfixes, feature backports, and selective synchronization between development branches. When mastered, it becomes a precision tool for scenarios where full merge would be inadequate, but you need specific changes applied with complete control.
It's 3:47 AM. Your pager just went off with a critical alert: security vulnerability in production. The fix is already implemented and tested in the development branch, but production is running a version three releases back. Merging the entire branch would bring hundreds of untested commits to production.
You need surgery, not a complete organ transplant.
You need cherry-pick.
This is the story of Git's most underestimated tool - one that transforms developers into code surgeons, capable of extracting specific commits and transplanting them with millimetric precision to any branch that needs them.
The Problem Cherry-Pick Solves
Real Scenario: Production Emergency
Imagine this situation (based on a real payment system case):
# Current branch state
git log --oneline --graph --all
# * f1a2b3c (develop) Add new payment gateway
# * e4d5c6f Refactor authentication system
# * a7b8c9d Fix critical SQL injection vulnerability ← THIS IS THE ONE WE NEED
# * 1x2y3z4 Update dependencies
# * 5a6b7c8 (main, production) Last stable releaseThe Dilemma:
- Commit
a7b8c9dhas the critical security fix - But it's "buried" among other untested commits in production
- Full merge of
developwould introduce immense risks - Production needs the fix NOW
Traditional Solution (Inadequate):
# Option 1: Replicate changes manually
git checkout main
# Copy and paste code from commit
# Error-prone, loses history, not scalable
# Option 2: Full merge
git merge develop
# Brings ALL commits, tested or not
# Unacceptable risk for productionCherry-Pick Solution (Elegant):
# Precise surgery: extracts ONLY the necessary commit
git checkout main
git cherry-pick a7b8c9d
# Result: fix applied, history preserved, risk controlledWhy Cherry-Pick is Revolutionary
Cherry-pick implements a fundamental concept: selective application of changes. It's like having a tool that extracts specific DNA from one cell and transplants it to another, keeping all genetic information intact.
# Cherry-pick preserves:
# - Original author
# - Timestamp
# - Commit message
# - Exact changeset
# - Complete metadata
git cherry-pick a7b8c9d
# Author: John Doe <[email protected]>
# Date: Mon Oct 15 14:30:22 2024 -0300
# (cherry picked from commit a7b8c9d1234567890abcdef)Anatomy of a Cherry-Pick
The Algorithm Behind the Magic
Cherry-pick uses the same 3-way merge algorithm as Git, but differently:
# Cherry-pick of commit C applied on branch B
#
# BEFORE:
# A---B (current branch)
# \
# P---C (source branch)
#
# Git compares:
# - Parent of C (P)
# - Commit C
# - Current HEAD (B)
#
# AFTER:
# A---B---C' (C' = C applied over B)Internal process:
git cherry-pick c1234567
# 1. Git identifies source commit parent
git show --pretty=format:"%P" c1234567
# a9876543
# 2. Calculates diff between parent and commit
git diff a9876543..c1234567
# 3. Applies this diff to current HEAD
# 4. Creates new commit with same content but new parentCherry-Pick vs Other Operations
# MERGE: brings complete history
git merge feature-branch
# All commits from feature-branch come along
# REBASE: moves commits, rewriting history
git rebase feature-branch
# Changes parent commits, rewrites SHAs
# CHERRY-PICK: copies specific commit, preserves original
git cherry-pick abc123
# Original remains unchanged, new copy createdBasic Cherry-Pick: Beyond the Obvious
Single Commit - The Base
# Apply a specific commit
git cherry-pick a1b2c3d
# Visualize what will be applied beforehand
git show a1b2c3d --stat
# Shows files that will be modified
# Cherry-pick with custom commit message
git cherry-pick a1b2c3d --edit
# Opens editor to modify messageMultiple Commits - Real Power
# Range of commits (exclusive start)
git cherry-pick start-commit..end-commit
# Applies all between start and end (start not included)
# Inclusive range
git cherry-pick start-commit^..end-commit
# Includes start-commit too
# Specific list of commits
git cherry-pick commit1 commit2 commit3
# Applies the three commits in sequencePractical Example - Feature Backport:
# Feature implemented in develop with 4 commits
git log --oneline develop
# d4e5f6g Complete user authentication feature
# c3d4e5f Add password validation
# b2c3d4e Implement user registration
# a1b2c3d Add user model
# Complete backport to release branch
git checkout release/v2.1
git cherry-pick a1b2c3d^..d4e5f6g
# All 4 commits applied in order to releaseConflict Resolution: Complex Surgery
Cherry-Pick Conflicts Are Different
Conflicts in cherry-pick have unique characteristics because you're applying changes outside their original context:
git cherry-pick feature-commit
# Auto-merging src/payment.js
# CONFLICT (content): Merge conflict in src/payment.js
# error: could not apply a1b2c3d... Add new payment method
# Special state: CHERRY_PICK_HEAD
git status
# On branch main
# You are currently cherry-picking commit a1b2c3d.
# (fix conflicts and run "git cherry-pick --continue")
# (use "git cherry-pick --skip" to skip this patch)
# (use "git cherry-pick --abort" to cancel the cherry-pick operation)Resolution Strategies
1. Traditional Manual Resolution:
# Edit conflicting files
vim src/payment.js
# Resolve conflicts manually
git add src/payment.js
git cherry-pick --continue2. Automatic Strategies:
# Use specific strategy
git cherry-pick -X theirs a1b2c3d
# In case of conflict, prefer changes from commit being cherry-picked
git cherry-pick -X ours a1b2c3d
# In case of conflict, prefer current branch state3. Cherry-Pick with Merge Tool:
# Configure merge tool (example: vimdiff)
git config merge.tool vimdiff
git cherry-pick problematic-commit
# In case of conflict:
git mergetool
# Opens visual interface for resolutionSerial Conflicts - The Real Challenge
# Cherry-picking multiple commits with conflicts
git cherry-pick commit1 commit2 commit3
# If commit2 conflicts:
# 1. Resolve conflict in commit2
git add .
git cherry-pick --continue
# 2. Git automatically continues to commit3
# 3. If commit3 also conflicts, repeat processAutomation Script for Predictable Conflicts:
#!/bin/bash
# cherry-pick-with-auto-resolve.sh
commits=("$@")
for commit in "${commits[@]}"; do
echo "Cherry-picking $commit..."
if ! git cherry-pick "$commit"; then
echo "Conflict in $commit, attempting auto-resolve..."
# Custom strategy based on conflict patterns
if grep -q "package.json" .git/CHERRY_PICK_HEAD; then
# For package.json conflicts, prefer newer version
git checkout --theirs package.json
git add package.json
git cherry-pick --continue
else
echo "Manual resolution required for $commit"
exit 1
fi
fi
doneAdvanced Techniques: Mastery
Reverse Cherry-Pick - Undoing Applications
# Scenario: cherry-pick was applied incorrectly
git log --oneline
# f5e4d3c (HEAD) Fix authentication bug (cherry picked from a1b2c3d)
# c2b1a09 Previous commit
# Option 1: Revert the cherry-pick
git revert f5e4d3c
# Creates commit that undoes changes
# Option 2: Reverse cherry-pick (more elegant)
git cherry-pick -R a1b2c3d
# Applies the inverse of the original commitCherry-Pick with Modifications
# Apply commit but don't commit automatically
git cherry-pick --no-commit a1b2c3d
# Now you can:
# - Modify files
# - Add extra changes
# - Adjust commit message
# Finalize when ready
git commit -m "Cherry-picked a1b2c3d with modifications"Mainline Selection in Merge Commits
Merge commits have multiple parents - cherry-pick needs to know which to use:
# Merge commit has 2 parents
git show --pretty=format:"%P" merge-commit
# parent1-sha parent2-sha
# Specify which parent to use as base
git cherry-pick -m 1 merge-commit # Uses first parent
git cherry-pick -m 2 merge-commit # Uses second parentCherry-Pick with Merge Preservation
# To maintain merge structure in cherry-pick
git cherry-pick -m 1 --keep-redundant-commits merge-commit
# Useful when merge structure is important for historyProfessional Workflows with Cherry-Pick
1. Multi-Version Hotfix
Scenario: Company maintains 3 versions in production, critical bug discovered.
#!/bin/bash
# hotfix-multi-version.sh
BUG_FIX_COMMIT="a1b2c3d"
VERSIONS=("release/v1.0" "release/v2.0" "main")
echo "Applying hotfix $BUG_FIX_COMMIT to all versions..."
for version in "${VERSIONS[@]}"; do
echo "Processing $version..."
git checkout "$version"
git pull origin "$version"
if git cherry-pick "$BUG_FIX_COMMIT"; then
echo "✅ Successfully applied to $version"
git push origin "$version"
else
echo "❌ Conflicts in $version - manual resolution required"
# Notify team or open ticket
git cherry-pick --abort
fi
done2. Selective Feature Backport
# Scenario: Large feature in develop, but client needs only part
git log --oneline develop
# h7i8j9k Complete social login feature
# g6h7i8j Add Google OAuth
# f5g6h7i Add Facebook login
# e4f5g6h Add base OAuth framework
# d3e4f5g Previous work...
# Client wants only Google OAuth (without Facebook)
git checkout release/client-special
# Selective cherry-pick
git cherry-pick e4f5g6h # Base framework
git cherry-pick g6h7i8j # Google OAuth
# Intentionally skip f5g6h7i (Facebook)
# Result: client has Google OAuth without Facebook3. Release Preparation Workflow
# Scenario: Prepare release with specific features from develop
git checkout -b release/v2.3.0 main
# Features approved for release
APPROVED_FEATURES=(
"feature1-final-commit"
"feature2-final-commit"
"bugfix-critical-commit"
)
# Release preparation script
for feature in "${APPROVED_FEATURES[@]}"; do
echo "Including feature: $feature"
# Find all commits from feature
feature_commits=$(git log --reverse --pretty=format:"%H" $feature ^main)
# Cherry-pick entire feature
git cherry-pick $feature_commits
done
# Release branch ready with only approved features4. Dependency Update Selective Sync
# Scenario: Dependency update in develop, apply selectively
git log --oneline develop | grep "deps:"
# j9k0l1m deps: Update lodash to 4.17.21
# i8j9k0l deps: Update react to 18.2.0
# h7i8j9k deps: Update webpack to 5.75.0
# Apply only critical security updates
git checkout main
git cherry-pick j9k0l1m # lodash (security fix)
# Skip react and webpack (breaking changes)
# Main has security fix without breaking changesAdvanced Automation and Scripting
Smart Cherry-Pick Script
#!/bin/bash
# intelligent-cherry-pick.sh
function smart_cherry_pick() {
local commit=$1
local target_branch=$2
echo "Analyzing commit $commit for $target_branch..."
# Check if commit already applied
if git merge-base --is-ancestor "$commit" "$target_branch"; then
echo "⚠️ Commit already in $target_branch history"
return 1
fi
# Check if it's a merge commit
if [[ $(git cat-file -p "$commit" | grep -c "^parent ") -gt 1 ]]; then
echo "⚠️ Merge commit detected - manual review required"
return 1
fi
# Try cherry-pick
if git cherry-pick "$commit"; then
echo "✅ Successfully cherry-picked $commit"
return 0
else
echo "❌ Conflicts detected in $commit"
# Automatic conflict analysis
conflict_files=$(git diff --name-only --diff-filter=U)
echo "Conflicted files: $conflict_files"
# Automatic strategies based on file type
for file in $conflict_files; do
case "$file" in
*.json)
echo "JSON conflict - attempting merge..."
git checkout --theirs "$file"
git add "$file"
;;
*.md|*.txt)
echo "Documentation conflict - preferring theirs..."
git checkout --theirs "$file"
git add "$file"
;;
*)
echo "Code conflict in $file - manual resolution required"
return 1
;;
esac
done
# Try to continue after automatic resolution
if git cherry-pick --continue; then
echo "✅ Auto-resolved and applied $commit"
return 0
else
git cherry-pick --abort
echo "❌ Auto-resolution failed for $commit"
return 1
fi
fi
}
# Script usage
smart_cherry_pick "a1b2c3d" "$(git rev-parse --abbrev-ref HEAD)"Cherry-Pick with Automatic Validation
#!/bin/bash
# validated-cherry-pick.sh
function validated_cherry_pick() {
local commit=$1
echo "Pre-flight checks for $commit..."
# Backup current state
local current_commit=$(git rev-parse HEAD)
# Try cherry-pick in temporary branch
git checkout -b "temp-cherry-pick-$(date +%s)"
if git cherry-pick "$commit"; then
echo "✅ Cherry-pick successful, running validations..."
# Automatic validations
if command -v npm &> /dev/null && [[ -f "package.json" ]]; then
echo "Running npm tests..."
if npm test; then
echo "✅ Tests pass"
else
echo "❌ Tests fail - aborting cherry-pick"
git checkout -
git branch -D "temp-cherry-pick-*"
return 1
fi
fi
# Build validation
if [[ -f "Makefile" ]]; then
echo "Testing build..."
if make; then
echo "✅ Build successful"
else
echo "❌ Build fails - aborting cherry-pick"
git checkout -
git branch -D "temp-cherry-pick-*"
return 1
fi
fi
# Apply to original branch
git checkout -
git cherry-pick "$commit"
git branch -D "temp-cherry-pick-*"
echo "✅ Validated cherry-pick complete"
else
echo "❌ Cherry-pick failed"
git checkout -
git branch -D "temp-cherry-pick-*"
return 1
fi
}Troubleshooting: When Cherry-Pick Goes Wrong
Common Problems and Solutions
1. Commit Already Applied:
# Error: "The previous cherry-pick is now empty"
git cherry-pick a1b2c3d
# error: The previous cherry-pick is now empty, possibly due to conflict resolution.
# Diagnosis: commit already exists in current branch
git log --grep="a1b2c3d"
# Solution: skip if intentional
git cherry-pick --skip2. Unsatisfied Dependencies:
# Commit depends on other commits not present
git cherry-pick feature-final-commit
# Error: code doesn't compile, undefined references
# Solution: cherry-pick dependencies first
git log --reverse feature-branch ^current-branch
# Identifies missing commits, applies in order3. Recurring Conflicts:
# Same conflict appears in multiple cherry-picks
# Solution: rerere (reuse recorded resolution)
git config rerere.enabled true
# Git memorizes conflict resolutions
# Applies automatically in similar situationsRecovery from Problematic Cherry-Pick
# Corrupted state during cherry-pick
git status
# You are currently cherry-picking commit a1b2c3d.
# Recovery options:
# 1. Abort completely
git cherry-pick --abort # Returns to previous state
# 2. Skip current commit
git cherry-pick --skip # Skip to next in series
# 3. Manual reset if everything fails
git reset --hard HEAD~1 # Go back 1 commit (use with caution)Performance and Optimization
Cherry-Pick in Large Repositories
# For repositories with millions of commits
# Use performance strategies
# 1. Shallow clone for temporary operations
git clone --depth 50 large-repo temp-cherry-pick
cd temp-cherry-pick
# 2. Fetch only necessary commits
git fetch origin +commit-hash:refs/remotes/origin/temp-ref
# 3. Cherry-pick with optimized cache
git config core.preloadindex true
git config core.fscache trueOptimized Batch Cherry-Pick
#!/bin/bash
# batch-optimized-cherry-pick.sh
commits=("$@")
total=${#commits[@]}
echo "Batch cherry-picking $total commits..."
# Pre-load all objects
git cat-file --batch-check <<< "${commits[*]}" |
git cat-file --batch >/dev/null
# Apply with progress
for i in "${!commits[@]}"; do
commit=${commits[$i]}
progress=$(( (i + 1) * 100 / total ))
echo -ne "\r[$progress%] Applying $commit..."
if ! git cherry-pick "$commit" >/dev/null 2>&1; then
echo -e "\n❌ Failed at $commit"
exit 1
fi
done
echo -e "\n✅ Batch complete: $total commits applied"Advanced Use Cases
1. Security Patch Distribution
# Scenario: Vulnerability discovered, patch must go to all versions
SECURITY_PATCH="security-fix-sha"
AFFECTED_BRANCHES=(
"release/v1.0" # Legacy still in use
"release/v2.0" # Current stable
"release/v2.1" # Current beta
"main" # Development
)
function distribute_security_patch() {
local patch=$1
shift
local branches=("$@")
echo "🔒 Distributing security patch $patch..."
for branch in "${branches[@]}"; do
echo "Applying to $branch..."
git checkout "$branch"
git pull origin "$branch"
if git cherry-pick "$patch"; then
# Tag as security release
version=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
new_version="${version%.*}.$((${version##*.} + 1))-security"
git tag -a "$new_version" -m "Security release: $patch"
git push origin "$branch" --tags
echo "✅ $branch updated and tagged as $new_version"
else
echo "❌ Manual resolution required for $branch"
git cherry-pick --abort
fi
done
}
distribute_security_patch "$SECURITY_PATCH" "${AFFECTED_BRANCHES[@]}"2. A/B Test Feature Deployment
# Scenario: Feature for A/B test, apply selectively
FEATURE_COMMITS=(
"ab1234 - Add A/B test framework"
"cd5678 - Implement variant A"
"ef9012 - Implement variant B"
"gh3456 - Add metrics collection"
)
# Deploy A/B framework + variant A to production A
git checkout production-a
git cherry-pick ab1234 cd5678 gh3456
# Deploy A/B framework + variant B to production B
git checkout production-b
git cherry-pick ab1234 ef9012 gh3456
# Both environments have framework and metrics
# But different variants for comparison3. Client-Specific Customization
# Scenario: SaaS with customizations per client
CLIENT_CUSTOMIZATIONS=(
"client-a:feature1,feature3,bugfix2"
"client-b:feature2,feature3,enhancement1"
"client-c:feature1,feature2,bugfix1,bugfix2"
)
function deploy_client_version() {
local client=$1
local features=$2
echo "Building version for $client..."
# Client-specific branch
git checkout -b "release/$client-$(date +%Y%m%d)" main
# Apply specific features
IFS=',' read -ra FEATURES <<< "$features"
for feature in "${FEATURES[@]}"; do
echo "Applying $feature for $client..."
git cherry-pick "$feature"
done
# Build and deploy
echo "✅ Custom version for $client ready"
}
# Process each client
for config in "${CLIENT_CUSTOMIZATIONS[@]}"; do
client="${config%%:*}"
features="${config##*:}"
deploy_client_version "$client" "$features"
doneMonitoring and Auditing
Tracking Cherry-Picks
# Find all cherry-picks in repository
git log --grep="cherry picked from commit" --oneline
# Track origin of cherry-picked commit
git show commit-sha | grep "cherry picked from"
# See all cherry-picks from a specific commit
git log --all --grep="$(git rev-parse --short original-commit)"Audit Script
#!/bin/bash
# cherry-pick-audit.sh
function audit_cherry_picks() {
local branch=${1:-HEAD}
echo "Cherry-pick audit for branch: $branch"
echo "=================================="
# Find all cherry-picks
cherry_picks=$(git log "$branch" --grep="cherry picked from commit" --pretty=format:"%H")
if [[ -z "$cherry_picks" ]]; then
echo "No cherry-picks found."
return 0
fi
echo "Found $(echo "$cherry_picks" | wc -l) cherry-picked commits:"
echo
for commit in $cherry_picks; do
echo "Commit: $(git rev-parse --short $commit)"
echo "Message: $(git log -1 --pretty=format:'%s' $commit)"
# Extract original commit
original=$(git log -1 --pretty=format:'%b' $commit |
grep "cherry picked from commit" |
sed 's/.*cherry picked from commit \([a-f0-9]*\).*/\1/')
if [[ -n "$original" ]]; then
echo "Original: $original"
echo "Author: $(git log -1 --pretty=format:'%an <%ae>' $original)"
echo "Date: $(git log -1 --pretty=format:'%ad' $original)"
fi
echo "---"
done
}
audit_cherry_picks "$@"Conclusion: The Mastery of Precision
Cherry-pick is much more than a Git command - it's a philosophy of surgical precision applied to software development. When mastered, it transforms you from a developer who applies changes in bulk to a code surgeon capable of extracting and transplanting functionality with millimetric precision.
The Fundamental Lessons
1. Precision over Volume
- You don't always need a full merge
- Sometimes, specific changes are exactly what you need
- Cherry-pick offers granular control impossible with merge
2. Context Preservation
- Cherry-pick maintains original commit integrity
- Author, timestamp, and metadata are preserved
- Complete traceability between original and copy
3. Risk Management
- Selective application reduces risk surface
- Tests can focus only on applied changes
- Rollback is simple and granular
When to Use Cherry-Pick
✅ Use for:
- Critical production hotfixes
- Selective feature backports
- Synchronization between release branches
- Application of security patches
- Client-specific customizations
❌ Avoid for:
- Replacing normal merge workflows
- Applying changes that depend on lost context
- Situations where historical integrity is critical
- When there's a less disruptive alternative
The Power of Automation
The scripts and workflows shown in this article demonstrate that truly powerful cherry-pick comes from intelligent automation:
- Validation scripts prevent errors
- Batch operations scale to multiple commits
- Automated conflict resolution reduces manual intervention
- Audit trails maintain compliance and transparency
Continuous Evolution
Cherry-pick continues evolving with new Git features:
- Better handling of merge commits
- More sophisticated conflict resolution strategies
- Integration with CI/CD tools
- Support for GitOps workflows
True mastery isn't in using cherry-pick for everything, but in knowing exactly when it's the right tool for the specific problem.
Like all powerful tools, cherry-pick requires wisdom. Use it as a surgeon uses their scalpel - with precision, purpose, and deep understanding of the consequences of each cut.
"The best surgeon is not the one who operates the most, but the one who knows exactly when surgery is necessary."
In the Git world, cherry-pick is your surgical tool. Use it wisely.