Every developer has faced this nightmare scenario: you create a feature branch from develop
to fix a critical bug, but when it's time to deploy, you realize you only need the actual fix—not the dozens of experimental changes from develop
. This post shows you exactly how to surgically extract only the commits you need using Git cherry-pick, with real-world commands and troubleshooting tips.
Here's a scenario that happened to our team last week:
The Setup:
develop
branch had 15+ experimental features being testedfix-1
from develop
to address the bugfix-1
now contained:
develop
The Challenge: How to merge only the bug fix commits into master
without bringing along all the experimental work?
Let's examine what would happen with a standard merge:
# This would bring ALL changes from develop $ git checkout master $ git merge fix-1 # Result: 50+ commits including experimental features # Production would get unstable code
The issue is that fix-1
contains the entire history of develop
plus the fix commits. A direct merge would drag along everything, potentially breaking production.
Git cherry-pick allows you to apply specific commits from one branch to another, creating new commits with the same changes but different commit hashes.
First, let's find the specific commits we want to cherry-pick:
# Check the commit history on fix-1 $ git log fix-1 --oneline --graph * 8f3a2b1 (fix-1) Fix: Handle null values in user preferences * 9c4e7d2 Fix: Add validation for email format * a1b2c3d (develop) Experimental: New dashboard layout * d4e5f6g Experimental: Database schema changes * ... (many more develop commits)
In our case, we only want the last two commits: 8f3a2b1
and 9c4e7d2
.
# Switch to master branch $ git checkout master # Cherry-pick the fix commits in order $ git cherry-pick 9c4e7d2 $ git cherry-pick 8f3a2b1 # Output: # [master 7a8b9c2] Fix: Add validation for email format # Date: Wed Jul 23 14:30:00 2025 -0400 # 2 files changed, 45 insertions(+), 3 deletions(-) # [master 3d4e5f6] Fix: Handle null values in user preferences # Date: Wed Jul 23 14:35:00 2025 -0400 # 1 file changed, 12 insertions(+), 2 deletions(-)
# Check that only our fix commits are applied $ git log --oneline -5 3d4e5f6 Fix: Handle null values in user preferences 7a8b9c2 Fix: Add validation for email format abc1234 (origin/master, master) Previous stable commit # Verify no experimental changes came along $ git diff origin/master # Should only show changes related to the bug fix
For situations where you need to extract changes that span multiple commits or when cherry-picking would create conflicts, the diff/patch approach works well:
# Create a diff of only the files you changed $ git diff develop..fix-1 --name-only # This shows all files changed in fix-1 # Create a patch for specific files $ git diff develop..fix-1 -- src/utils/validation.js src/models/user.js > bug-fix.patch
# Switch to master and apply the patch $ git checkout master $ git apply bug-fix.patch # Or use git apply with --check first to test $ git apply --check bug-fix.patch # Dry run $ git apply bug-fix.patch # Actually apply
When you have many commits and want to start fresh:
# Create a new branch from master $ git checkout master $ git checkout -b hotfix-critical-bug
# Method 1: Copy specific files $ git checkout fix-1 -- src/utils/validation.js src/models/user.js # Method 2: Use git archive for complex directory structures $ git archive fix-1 src/utils/ | tar -x -C /tmp/changes/ $ cp -r /tmp/changes/src/utils/* src/utils/
$ git add . $ git commit -m "Hotfix: Critical email validation bug"
Let's walk through a complete example with actual commands and expected outputs:
# Initial state $ git branch -a develop fix-1 * master remotes/origin/HEAD -> origin/master remotes/origin/develop remotes/origin/master # Check what changed in fix-1 $ git log --oneline master..fix-1 a8f3c2d (fix-1) Fix: Prevent email injection attacks b4e7d9a Fix: Add rate limiting to login endpoint c2d8f1e Experimental: Machine learning user recommendations ... (15 more commits)
# 1. Switch to master $ git checkout master Switched to branch 'master' Your branch is up to date with 'origin/master'. # 2. Cherry-pick security fixes only $ git cherry-pick b4e7d9a [master 9f8e7d2] Fix: Add rate limiting to login endpoint Author: Sarah Chen <sarah@medianeth.agency> Date: Wed Jul 23 14:15:00 2025 -0400 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/middleware/rate-limit.js $ git cherry-pick a8f3c2d [master 7d3e9f1] Fix: Prevent email injection attacks Author: Sarah Chen <sarah@medianeth.agency> Date: Wed Jul 23 14:20:00 2025 -0400 2 files changed, 34 insertions(+), 8 deletions(-) # 3. Verify the result $ git log --oneline -3 7d3e9f1 Fix: Prevent email injection attacks 9f8e7d2 Fix: Add rate limiting to login endpoint abc1234 Previous stable commit # 4. Test the changes $ npm test ✓ Security tests pass ✓ Rate limiting works correctly ✓ No experimental features detected
# Error: cherry-pick resulted in conflicts $ git cherry-pick a8f3c2d error: could not apply a8f3c2d... Fix: Prevent email injection attacks hint: after resolving the conflicts, mark the corrected paths hint: with 'git add <paths>' or 'git rm <paths>' # Solution: Resolve conflicts and continue $ git status # See conflicted files $ # Edit files to resolve conflicts $ git add . # Mark conflicts as resolved $ git cherry-pick --continue
# If you cherry-picked the wrong commit $ git log --oneline -2 wrong-commit-hash Fix: Wrong change applied another-wrong-hash Fix: Another incorrect change # Undo the last cherry-pick $ git reset --hard HEAD~2 # Cherry-pick the correct ones $ git cherry-pick correct-commit-hash
# Error: changes depend on other commits $ git cherry-pick b4e7d9a error: could not apply b4e7d9a... Fix: Add rate limiting error: src/middleware/rate-limit.js: does not exist in index # Solution: Include dependent commits or create missing files $ git cherry-pick previous-commit-that-creates-file $ git cherry-pick b4e7d9a
# If commits have different authors and you need to preserve attribution $ git cherry-pick --allow-empty-message b4e7d9a $ git commit --amend --author="Sarah Chen <sarah@medianeth.agency>"
# Cherry-pick a range of commits $ git cherry-pick oldest-commit^..newest-commit # Cherry-pick specific commits $ git cherry-pick commit1 commit2 commit3 # Cherry-pick with custom message $ git cherry-pick -e commit-hash # Opens editor for commit message
# Interactive rebase to reorder/edit commits before cherry-picking $ git rebase -i develop fix-1 # Then cherry-pick from the cleaned branch
Before deploying your cherry-picked changes:
Verify Changes Only
$ git diff origin/master | grep -v "^\+\+\+" | grep "^\+" | wc -l # Should show reasonable number of changed lines
Run Complete Test Suite
$ npm test $ npm run test:integration $ npm run security-scan
Check for Configuration Changes
$ git diff origin/master --name-only | grep -E '\.(json|yml|yaml|env)$'
Review Database Migrations
$ git diff origin/master -- 'migrations/*'
git diff
before mergingThis post was written by Jomar, Senior Developer at Medianeth. We've helped dozens of development teams streamline their Git workflows and avoid production disasters. Want to work with us? Get in touch.
Ready to make your online presence shine? I'd love to chat about your project and how we can bring your ideas to life.
Free Consultation 💬