Bisect and Cherry-Pick
Git Bisect: Finding Regressions with Binary Search
When a test that used to pass is now failing, git bisect performs a binary search through commit history to find exactly which commit introduced the regression. Instead of manually checking dozens of commits, bisect finds the answer in log2(n) steps. For 1000 commits, that is about 10 steps.
How Bisect Works
You tell Git one "good" commit (where the test passed) and one "bad" commit (where the test fails). Git checks out the midpoint and asks you to evaluate it. Based on your answer, it narrows the range by half and repeats.
Commit history: A B C D E F G H I J K L M N O P
good? bad
Step 1: Git checks out H (midpoint)
→ You test: PASS → good
Range narrows to: I J K L M N O P
Step 2: Git checks out L (midpoint of remaining)
→ You test: FAIL → bad
Range narrows to: I J K L
Step 3: Git checks out J (midpoint)
→ You test: PASS → good
Range narrows to: K L
Step 4: Git checks out K
→ You test: FAIL → bad
Result: K is the first bad commit
Four steps to find the problem among 16 commits.
Manual Bisect
# Start the bisect session
git bisect start
# Mark the current commit as bad (the test is failing here)
git bisect bad
# Mark a known good commit (a tag, SHA, or branch where the test passed)
git bisect good v2.3.0
# Git checks out a middle commit. Run your test:
npm run test:checkout
# If the test passes:
git bisect good
# If the test fails:
git bisect bad
# Git narrows the range and checks out another commit.
# Repeat until Git identifies the first bad commit.
# Git will output something like:
# abc123f is the first bad commit
# Author: Dev McDevface
# Date: Mon Jan 15 14:32:00 2024
# Subject: refactor coupon validation logic
# When done, reset to your original branch:
git bisect reset
Automated Bisect
The real power of bisect is automation. Give Git a command that exits with 0 (good) or non-zero (bad), and it runs the entire bisect automatically.
# Fully automated bisect
git bisect start HEAD v2.3.0
git bisect run npm run test:checkout
# Or with a specific test file
git bisect start HEAD v2.3.0
git bisect run npx playwright test tests/checkout.spec.ts
# Using a custom script for more complex checks
git bisect start HEAD v2.3.0
git bisect run ./scripts/bisect-test.sh
A practical bisect script that handles setup:
#!/bin/bash
# scripts/bisect-test.sh
# Install dependencies (they may differ between commits)
npm ci --silent 2>/dev/null
# Run the specific test
npx playwright test tests/checkout.spec.ts --reporter=list 2>/dev/null
# Exit code 0 = good, non-zero = bad
# Exit code 125 = skip (cannot test this commit, e.g., build fails)
Exit code 125 tells bisect to skip this commit (the commit cannot be tested, perhaps because it does not compile). Bisect continues with the next candidate.
When to Use Bisect
| Scenario | Why Bisect Helps |
|---|---|
| A test that passed last week now fails | Find the exact commit that broke it |
| Performance regression (page load 3x slower) | Find when the slowdown was introduced |
| Visual regression (layout shifted) | Find which commit changed the CSS |
| Flaky test appeared | Find when the flakiness was introduced |
| Feature that "used to work" is broken | Prove which commit introduced the bug |
Bisect is invaluable for QA because it gives you a specific commit, author, and description to reference in your bug report. "This regression was introduced in commit abc123f by @developer on Jan 15 when they refactored coupon validation" is much more actionable than "checkout is broken."
Cherry-Pick: Targeted Commit Application
Cherry-pick applies a specific commit from one branch to another without merging the entire branch. It creates a new commit with the same changes but a different hash.
# A bug fix was committed to develop, but you need it on the release branch now
git checkout release/2.4
git cherry-pick abc123f
# Cherry-pick multiple commits
git cherry-pick abc123f def456a ghi789b
# Cherry-pick a range of commits
git cherry-pick abc123f..ghi789b
# If there is a conflict, resolve it and continue
git add .
git cherry-pick --continue
# Or abort the cherry-pick
git cherry-pick --abort
Common QA Scenarios for Cherry-Pick
Test Fix Needed on Multiple Branches
You fixed a flaky test on develop, but the same test is flaky on the release/2.4 branch.
# Find the commit hash of your fix on develop
git log --oneline develop -- tests/payment.spec.ts
# Output: abc123f fix(payment): stabilize test by awaiting API response
# Apply it to the release branch
git checkout release/2.4
git cherry-pick abc123f
Hotfix Verification
A hotfix was committed to main for a production issue. You need to verify it on a test branch without merging all of main.
git checkout test/regression-suite
git cherry-pick <hotfix-commit-hash>
# Run regression tests including the fix
npm run test:regression
Pulling Test Infrastructure Into a Feature Branch
The test infrastructure was updated on develop (e.g., new page object helpers), but your feature branch was created before that update.
git checkout feature/my-tests
git cherry-pick <infrastructure-commit-hash>
# Now you have the new helpers without merging everything else from develop
Cherry-Pick Best Practices
- Cherry-pick creates a new commit: The original and cherry-picked commits have different hashes. Git does not know they are related.
- Conflicts are common: If the cherry-picked commit depends on prior changes that do not exist on the target branch, you will get conflicts.
- Prefer merge or rebase when possible: Cherry-pick is for targeted, exceptional cases. If you find yourself cherry-picking frequently, your branching strategy may need adjustment.
- Document cherry-picks: Add a note in the commit message: "Cherry-picked from abc123f on develop."
# Cherry-pick with a note
git cherry-pick abc123f --edit
# In the editor, add: "(cherry-picked from abc123f on develop)"
Combining Bisect and Cherry-Pick
A powerful QA workflow:
- Bisect to find the commit that introduced a regression
- Review the commit to understand the root cause
- Work with the developer to create a fix
- Cherry-pick the fix to the release branch for immediate deployment
- Merge the fix to develop for long-term integration
# Step 1: Find the regression
git bisect start HEAD v2.3.0
git bisect run npx playwright test tests/checkout.spec.ts
# Result: commit abc123f introduced the regression
# Step 2: Review
git show abc123f
# Step 3: Developer creates fix on develop (commit def456a)
# Step 4: Cherry-pick fix to release branch
git checkout release/2.4
git cherry-pick def456a
# Step 5: Verify the fix
npx playwright test tests/checkout.spec.ts
Hands-On Exercise
- Create a repository with 20+ commits. Intentionally break a test in commit 12.
- Use
git bisect(manual mode) to find the breaking commit. - Repeat with automated bisect:
git bisect run <test-command> - Practice cherry-picking a single commit from one branch to another.
- Intentionally create a cherry-pick conflict, resolve it, and complete the cherry-pick.
- Write a bisect script that handles dependency installation and returns exit code 125 for un-buildable commits.