git cherry-pick & git bisect — Precision Surgery
Module 05 45 min
Section Objectives
- Use
git cherry-pickto apply specific commits to another branch - Use
git bisectto find the commit that introduced a bug - Know when to use these commands and their limitations
git cherry-pick — Apply Specific Commits
git cherry-pick takes a commit from one branch and applies it to another, creating a new commit with the same changes (different hash).
Basic Usage
# Cherry-pick a single commit
git cherry-pick abc1234
# Cherry-pick multiple commits
git cherry-pick abc1234 def5678 ghi9012
# Cherry-pick a range of commits (exclusive of first)
git cherry-pick abc1234..def5678
# Cherry-pick without creating a commit (stage only)
git cherry-pick --no-commit abc1234
# Cherry-pick and edit the commit message
git cherry-pick -e abc1234
Handle Cherry-pick Conflicts
# If a conflict occurs:
# 1. Fix the conflict
git status # Shows conflicted files
# edit the files...
git add fixed-file.py
# 2. Continue
git cherry-pick --continue
# OR: Abort
git cherry-pick --abort
# OR: Skip this commit
git cherry-pick --skip
When to Use Cherry-pick
| Scenario | Use Cherry-pick? | Alternative |
|---|---|---|
| Critical fix needed on release branch | ✅ Yes | - |
| Accidentally committed to wrong branch | ✅ Yes | - |
| Reuse a utility function from another branch | ✅ Yes | - |
| Integrate an entire feature | ❌ No | Merge or rebase |
| Regularly syncing branches | ❌ No | Merge or rebase |
Cherry-pick vs Merge
Cherry-pick creates a copy of the commit (new hash). If you later merge the original branch, Git will see these as duplicate changes. Use cherry-pick sparingly and for specific use cases.
Practical Examples
# Scenario 1: Critical fix needed on release branch
git switch release/v1.0
git cherry-pick hotfix-commit-hash
git push origin release/v1.0
# Scenario 2: Committed on wrong branch
git log --oneline # Find the commit hash you want to move
git switch correct-branch
git cherry-pick abc1234 # Apply to correct branch
git switch wrong-branch
git reset HEAD~1 # Remove from wrong branch
git bisect — Binary Search for Bugs
git bisect performs a binary search through your commit history to find the exact commit that introduced a bug.
Manual Bisect
# 1. Start bisect
git bisect start
# 2. Mark current commit as bad (bug present)
git bisect bad
# 3. Mark a known good commit (no bug)
git bisect good v1.0.0
# or by hash:
git bisect good abc1234
# Git will check out the middle commit
# Test your application...
# 4. Mark each tested commit
git bisect good # No bug on this commit
git bisect bad # Bug present on this commit
# 5. Git narrows down and shows the culprit:
# abc1234 is the first bad commit
# 6. Exit bisect
git bisect reset # Returns to original HEAD
Automated Bisect
If you have a test that can detect the bug, you can fully automate bisect.
Create a test script test_bug.sh:
#!/bin/bash
# Exits 0 (success) if no bug, 1 if bug present
python -c "
from app import get_statistics
from app import TASKS
TASKS.clear()
try:
stats = get_statistics()
if stats['total'] == 0:
exit(0)
else:
exit(1)
except ZeroDivisionError:
exit(1)
"
chmod +x test_bug.sh
# Let Git do the bisect automatically!
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
git bisect run ./test_bug.sh
# Git runs the script on each commit and finds the culprit automatically
# At the end:
git bisect reset
Bisect Log and Replay
# Save a bisect session for later
git bisect log > bisect-session.log
# Replay a saved session
git bisect replay bisect-session.log
git reflog — The Safety Net
git reflog records every HEAD movement in your repository — it's your ultimate safety net:
# View the reflog
git reflog
# Output:
# d5a1b4c HEAD@{0}: commit: feat: add statistics
# c2e8f3a HEAD@{1}: checkout: moving from feature to main
# b7c9d1e HEAD@{2}: reset: moving to HEAD~1
# a3f4b2c HEAD@{3}: commit: feat: add search
Recovering "Lost" Commits
# You accidentally ran git reset --hard and lost commits
git reset --hard HEAD~5 # Oops!
# Find the lost commits in reflog
git reflog
# HEAD@{2} was before the reset
# Recover
git reset --hard HEAD@{2}
# Or create a new branch from the lost state
git switch -c recovery-branch HEAD@{2}
Reflog stays local
The reflog is not pushed to GitHub. It's a local safety net only. Reflog entries are kept for 90 days by default.
Summary
| Command | Description |
|---|---|
git cherry-pick <hash> | Apply a specific commit |
git cherry-pick --continue | Continue after conflict |
git cherry-pick --abort | Cancel cherry-pick |
git bisect start | Start binary search |
git bisect good/bad | Mark commit as good/bad |
git bisect run <script> | Automatic bisect |
git bisect reset | End bisect session |
git reflog | View all HEAD movements |