
September 11, 2026
11 min read
By Kokil Thapa | Last reviewed: September 2026
A regression lands in production and nobody can name the commit. You need Git Bisect: Find the Bad Commit Fast instead of reading fifty diffs by hand. Git bisect walks your history with a binary search, halving the suspect range on every step until one commit remains. On a Git recovery workflow or a long-lived Laravel branch, that turns days of guesswork into minutes. This guide covers manual bisect, automated git bisect run, and the traps I see on real client projects.
git bisect start, mark a known-good commit with good and a broken one with bad, then repeat git bisect good or bad at each checkout until Git names the first bad commit. Use git bisect run to automate tests.What is git bisect and when should you use it?
Git bisect is a built-in binary search over your commit graph. You give Git two anchors: one commit where the bug does not exist, and one where it does. Git checks out the midpoint, you classify it, and the range shrinks by roughly half each round.
For a history of 1,000 commits, a linear search might need hundreds of checks. Bisect typically needs about ten. That difference matters when a payment callback broke after a Friday deploy, or when a Laravel Livewire booking flow regressed and the team merged daily for two weeks.
Use bisect when you can reproduce the failure reliably and you know a good baseline. Skip it when the bug is flaky, environment-specific, or caused by uncommitted local changes. Fix your working tree first, then bisect.
Bisect works on merge commits too. Git can skip untestable merge commits when you pass --no-merges, which keeps CI runs predictable on busy main branches.
When bisect beats blame and log scanning
- Cross-cutting regressions: A CSS change broke checkout three modules away.
- Long gaps: QA reported the bug weeks after the offending merge.
- Binary outcomes: Tests pass or fail, page loads or 500s, API returns 200 or 422.
- Release archaeology: Pair bisect with conventional commits to read the final SHA in context.
How do you start a git bisect session to find a bad commit?
Start on a clean working tree. Stash or commit local edits first. A dirty tree can poison every midpoint checkout.
- Identify a bad commit — usually
HEADon the broken branch. - Identify a good commit — the last known release tag or CI-green SHA.
- Run
git bisect start, then mark both ends. - At each checkout, reproduce the bug and run
git bisect goodorgit bisect bad. - When Git stops, read the reported first bad commit and inspect its diff.
# Example: checkout broken branch, ensure clean tree
git switch main
git pull
git status # must be clean
# Start bisect: bad is HEAD, good is v2.4.0 tag
git bisect start
git bisect bad HEAD
git bisect good v2.4.0
# Git checks out a midpoint — run your repro steps
php artisan test --filter=PaymentCallbackTest
# Classify result
git bisect bad # if test fails
# OR
git bisect good # if test passes
# Repeat until Git prints something like:
# abc1234 is the first bad commit On production Laravel apps I maintain, I often anchor good to the last Deployer release tag. That tag maps cleanly to a known deploy on sister sites sharing the same Linux deployment pipeline.
Pick boundaries that actually test the bug
Your good commit must predate the regression. Your bad commit must show it. If both are too recent, bisect still works but wastes steps inside a tiny range. If good is too old, you walk more history than necessary.
Tags beat vague dates. A tag like release-2026-08-15 beats guessing from a calendar. For hotfix branches, bisect between the last green main SHA and the hotfix tip.
The official reference is the git-bisect documentation. Keep it open the first time you run a live incident bisect.
Can git bisect run tests automatically instead of manual checks?
Yes. git bisect run executes a shell command at each midpoint. Exit code 0 means good. Exit codes 1 through 125 mean bad. Exit codes 125 through 127 skip the commit when tests cannot run — for example missing migration files on very old SHAs.
# Automated bisect with PHPUnit
git bisect start HEAD v2.4.0
git bisect run php artisan test --filter=PaymentCallbackTest
# Custom script returning proper exit codes
git bisect run ./scripts/check-regression.sh A minimal check script might curl a local endpoint and grep for an error string:
#!/usr/bin/env bash
set -euo pipefail
php artisan migrate --force --no-interaction > /dev/null
php artisan serve --port=8099 &
PID=$!
sleep 2
if curl -sf http://127.0.0.1:8099/health | grep -q '"ok":true'; then
kill "$PID"
exit 0 # good
fi
kill "$PID"
exit 1 # bad Automated bisect pairs well with fast Composer installs in CI. Run bisect locally with cached vendor trees, or wire the same test command your pipeline already trusts.
Manual vs automated bisect
| Approach | Best for | Exit criteria | Risk |
|---|---|---|---|
Manual good/bad | UI bugs, visual regressions, intermittent repro | Your judgment after manual testing | Human error mislabels a step |
git bisect run | Unit tests, API contracts, CLI scripts | Shell exit code 0 vs 1–125 | Old commits lack deps or migrations |
| CI bisect job | Large teams, shared repro environments | Same test command as main pipeline | Runner cost on wide ranges |
| Bisect + skip | Merge-heavy history | git bisect skip for untestable SHAs | Skips can widen search slightly |
For flaky tests, fix or quarantine the flake before bisect. A test that fails 30% of the time will lie to the binary search and send you down the wrong half of history.
The Pro Git book chapter on debugging with Git walks through run with compile checks — the same pattern applies to PHP test suites.
What are common git bisect mistakes that waste your time?
Most failed bisect sessions come from setup problems, not from Git itself. These show up often when bisecting Laravel apps after a cluster of merges.
Dirty working tree or generated files
Bisect checkouts can overwrite tracked files mid-search. Commit or stash first. Add build artefacts to .gitignore so they do not block checkout. On one production deployment I traced a false bad mark to an uncommitted .env tweak that changed database credentials at each midpoint.
Dependencies that drift across old commits
Checking out a six-month-old SHA with today's vendor/ folder produces nonsense failures. For PHP projects, run composer install after each checkout when lock files changed often. Cache ~/.composer to keep that tolerable. Node frontends need the same discipline if lockfiles moved during the range.
Database and migration mismatch
A commit may expect columns your local DB lacks. Refresh test schema at each step, or point bisect at a disposable Docker database you rebuild per checkout. I prefer a dedicated test database name like bisect_tmp over touching dev data.
Mislabeled good or bad
One wrong label corrupts the search. When unsure, run git bisect log and verify marks. Use git bisect replay to resume a saved session instead of guessing where you stopped.
Pair bisect with Git hooks and secrets scanning in CI so future regressions surface before they reach the good baseline you depend on.
How do you finish or abort a git bisect search?
When Git reports the first bad commit, inspect it before you reset:
git show abc1234
git bisect log > /tmp/bisect-session.log
git bisect reset git bisect reset returns you to the branch you started from. Without it you stay on a detached HEAD — a common post-incident footgun on shared staging servers.
To abort mid-flight:
git bisect reset
# or explicitly
git bisect abort # alias in newer Git versions Save logs when the regression spans teams. Paste the log into your ticket so the next developer can git bisect replay without repeating manual steps. That habit mirrors how I document deploy rollbacks on legal-tech portals where audit trails matter.
After you find the bad commit
- Read the diff — confirm causation, not just correlation.
- Write a regression test if one did not exist.
- Cherry-pick a fix or revert the SHA on main.
- Post-mortem: why did CI miss it? See AI code review in CI for extra signal.
- Update runbooks on support and maintenance so the next on-call engineer knows the bisect command set.
For merge-heavy workflows, read resolving merge conflicts and branching strategy docs before you widen the bisect range across dozens of parallel feature branches.
When the bug lives in server config rather than app code, bisect the dotfiles repo separately. Application bisect and infrastructure bisect should not share one muddled range.
Developers debugging parser edge cases sometimes validate repro strings in a regex tester before encoding them into a bisect script guard clause. Small tooling habits reduce false bad marks.
If bisect points to a commit that only changed minified assets, rebuild front-end artefacts for that SHA before you label it. Committed Vite 8.x bundles on a Laravel 13.x app must match the PHP code at that point in history.
For deeper post-bisect analysis, AI-assisted debugging can summarize the offending diff once you hold the SHA. Bisect finds the needle; review tools help you explain it to stakeholders.
Supply-chain conscious teams cross-check the bad commit author and signature with signed commits policy before reverting on main.
Key Takeaways
- Mark a known-good and known-bad commit, then let git bisect binary-search the range — about ten steps for a thousand commits.
- Keep a clean working tree and reinstall dependencies when lock files changed inside the range.
- Prefer
git bisect runwith a deterministic test that exits 0 for good and 1 for bad. - Always run
git bisect resetwhen finished so you are not left on a detached HEAD. - Save
git bisect logoutput to tickets so teammates can replay the session. - Add a regression test after you find the bad commit so bisect stays a one-time rescue tool.
People Also Ask
How many steps does git bisect need?
Git bisect needs at most log₂(n) steps for n commits in a linear range. Roughly ten classifications isolate one commit among a thousand. Merge-heavy histories may need a few extra skip calls when midpoints are untestable merge commits.
Can you bisect across multiple branches?
Bisect walks reachable commits between your good and bad anchors on the current graph. If the fix lives only on a feature branch, fetch and mark SHAs that exist locally. You cannot bisect commits Git never fetched from origin.
Does git bisect work with shallow clones?
Shallow clones may lack midpoints inside the range. Run git fetch --unshallow or deepen with git fetch --depth=N until both anchors and expected midpoints exist. CI shallow clones cause more bisect pain than full local mirrors.
What exit codes does git bisect run expect?
Exit 0 marks good. Exit codes 1 through 125 mark bad. Codes 125 through 127 skip the commit. Anything outside that convention produces unreliable results — wrap your test command in a script that normalizes codes.
Ship faster when regressions hit
Git Bisect: Find the Bad Commit Fast is the difference between guessing and knowing. Binary search turns a vague “something broke last sprint” into one SHA you can revert, test, and explain. On long-running PHP and Laravel systems — the kind I have maintained since 2010 — that speed protects revenue and client trust. If your team wants bisect wired into CI, deploy runbooks, or post-incident hardening, see the testing and optimization service or contact us for a practical review. More Git workflows live on the blog and the home page.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

