Something that worked last release is broken now, and forty commits sit between then and now. Reading every diff is the slow way. git bisect does a binary search over the range, so six checks find the culprit instead of forty.
Mark the ends#
git bisect start
git bisect bad # the commit you are on is broken
git bisect good v1.4.0 # the last tag you know was fine
Git checks out a commit in the middle. Run whatever reproduces the bug — the feature test, the artisan command, the page that 500s — then tell Git the answer:
git bisect good # or: git bisect bad
Repeat. Each answer halves the range, and after a few rounds Git names the first bad commit with its full diff and message.
Let the test suite drive#
If a single command answers “is this commit broken”, hand the whole search to it:
git bisect run php artisan test --filter=CheckoutTest
Bisect now walks the range alone, marks each commit from the exit code, and stops on the first failure. This is the version worth using: it is fast, and it removes the temptation to eyeball a step and guess.
Two practical bumps#
- A commit that cannot run at all — a dependency changed and the test cannot boot — stalls the search.
git bisect skiptells Git to step over it, andgit bisect runcan skip automatically when the command exits with 125. - Vendored directories move between commits. Between steps you may need
composer install; insidebisect run, a tiny shell script that runs it before the test keeps every commit bootable.
When it names a commit#
git bisect reset
That returns you to where you started. The bisect log disappears, so note the hash first. The usual next step is not the fix — it is a failing regression test on current main that proves the bug, so the fix that follows can never quietly regress.
Bisect rewards the boring habits: small commits that each boot, and tags you can trust as “known good”. Those habits are cheap right up until the day they are the fastest way back to a working app.
Share this post
Keep reading
The Reflog: Undoing Almost Anything in Git
Reset too far, deleted the wrong branch, botched a rebase? The reflog remembers where HEAD has been, and that is usually enough.
What Not to Commit in a Laravel Project
vendor, .env, caches and build output: the short list of what stays out of the repo, and the .gitignore that keeps it out.
What to Do in a Detached HEAD
Detached HEAD sounds like an injury. It is just HEAD pointing at a commit instead of a branch - safe to look around, one command to keep any work.