Back to all articles
Cherry-Pick: The Art of Commit Surgery

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.

Human-architected research synthesized with the assistance of AI personas.
17 min read

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):

bash
# 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 release

The Dilemma:

  • Commit a7b8c9d has the critical security fix
  • But it's "buried" among other untested commits in production
  • Full merge of develop would introduce immense risks
  • Production needs the fix NOW

Traditional Solution (Inadequate):

bash
# 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 production

Cherry-Pick Solution (Elegant):

bash
# Precise surgery: extracts ONLY the necessary commit git checkout main git cherry-pick a7b8c9d # Result: fix applied, history preserved, risk controlled

Why 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.

bash
# 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:

bash
# 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:

bash
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 parent

Cherry-Pick vs Other Operations

bash
# 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 created

Basic Cherry-Pick: Beyond the Obvious

Single Commit - The Base

bash
# 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 message

Multiple Commits - Real Power

bash
# 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 sequence

Practical Example - Feature Backport:

bash
# 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 release

Conflict Resolution: Complex Surgery

Cherry-Pick Conflicts Are Different

Conflicts in cherry-pick have unique characteristics because you're applying changes outside their original context:

bash
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:

bash
# Edit conflicting files vim src/payment.js # Resolve conflicts manually git add src/payment.js git cherry-pick --continue

2. Automatic Strategies:

bash
# 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 state

3. Cherry-Pick with Merge Tool:

bash
# 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 resolution

Serial Conflicts - The Real Challenge

bash
# 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 process

Automation Script for Predictable Conflicts:

bash
#!/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 done

Advanced Techniques: Mastery

Reverse Cherry-Pick - Undoing Applications

bash
# 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 commit

Cherry-Pick with Modifications

bash
# 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:

bash
# 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 parent

Cherry-Pick with Merge Preservation

bash
# To maintain merge structure in cherry-pick git cherry-pick -m 1 --keep-redundant-commits merge-commit # Useful when merge structure is important for history

Professional Workflows with Cherry-Pick

1. Multi-Version Hotfix

Scenario: Company maintains 3 versions in production, critical bug discovered.

bash
#!/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 done

2. Selective Feature Backport

bash
# 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 Facebook

3. Release Preparation Workflow

bash
# 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 features

4. Dependency Update Selective Sync

bash
# 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 changes

Advanced Automation and Scripting

Smart Cherry-Pick Script

bash
#!/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

bash
#!/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:

bash
# 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 --skip

2. Unsatisfied Dependencies:

bash
# 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 order

3. Recurring Conflicts:

bash
# 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 situations

Recovery from Problematic Cherry-Pick

bash
# 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

bash
# 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 true

Optimized Batch Cherry-Pick

bash
#!/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

bash
# 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

bash
# 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 comparison

3. Client-Specific Customization

bash
# 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" done

Monitoring and Auditing

Tracking Cherry-Picks

bash
# 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

bash
#!/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.


Receive new articles

Subscribe to receive notifications about new articles directly to your email

We won't send spam. You can unsubscribe at any time.