Git bisect found your bug in 3 commits, not 300
A regression shipped somewhere in the last 300 commits. Nobody knows which one. The old way to find it is checking out commits one by one until the bug disappears. This git bisect tutorial covers a fa
A regression shipped somewhere in the last 300 commits. Nobody knows which one. The old way to find it is checking out commits one by one until the bug disappears. This git bisect tutorial covers a faster way: binary search through your history instead of a slow crawl.
Git bisect is a built-in command that turns bug hunting into a search problem. Instead of testing commits in order, it jumps to the midpoint of a range and asks you a simple question: good or bad? A few answers later, it hands you the exact commit that broke things.
If you have never used it, the payoff is bigger than it sounds. A 300-commit range that would take hours to check manually can usually be solved in eight or nine tests. That is the difference between linear search and binary search, and it is worth understanding before you touch the command.
Why Binary Search Beats Manual Debugging
Checking commits one at a time is linear search. If the bug was introduced 300 commits ago and you check them in order, you might need to test all 300 before finding it. On average, you would test half that many, but worst case is still painful.
Binary search cuts the range in half every time. Instead of scanning forward, you jump to the middle, test it, then jump to the middle of whichever half still contains the bug. According to Stack Overflow, this is exactly the algorithm git bisect uses instead of a straightforward linear scan.
The math works in your favor fast. Doubling the number of commits only adds one more test, because each test eliminates half the remaining range. That is why 300 commits and 600 commits take almost the same number of steps to search.
Rough test counts by range size:| Commit range | Manual worst case | Bisect tests needed |
|---|---|---|
| 10 commits | up to 10 | ~4 |
| 100 commits | up to 100 | ~7 |
| 300 commits | up to 300 | ~9 |
| 1,000 commits | up to 1,000 | ~10 |
This shows how test counts grow slowly with bisect even as the commit range grows fast.
From 300 Commits to 3 Tests: Real-World Bisect Scenarios
Picture a team that shipped a memory leak sometime in the last sprint. The last known good release was tagged two weeks ago, and 300 commits have landed since. Nobody remembers which pull request introduced the leak.
Checking out all 300 commits, rebuilding, and running a memory profiler on each one is not realistic. Nobody has that kind of time before a release deadline. This is exactly the scenario git bisect was built for.
With bisect, the engineer marks the tag as good and the current HEAD as bad. Git checks out the midpoint commit, roughly 150 commits in. The engineer runs the app, watches memory usage, and marks it good or bad.
Each answer halves the remaining range. After about eight or nine rounds of testing, git lands on a single commit, the exact one that introduced the leak. What looked like a needle in a haystack becomes a short, structured conversation between the engineer and git.
The same pattern applies to flaky test failures, broken builds, performance regressions, and UI bugs that appeared "sometime last month." Any bug that can be reproduced with a pass or fail check is a candidate for bisect.
Setting Up Your First Bisect
Before running anything, make sure your working directory is clean. Bisect checks out different commits repeatedly, and uncommitted changes will get in the way or cause conflicts.
You need two reference points: a commit you know is bad (the bug exists there) and a commit you know is good (the bug does not exist there). According to Git's official documentation, this pair of endpoints is the starting requirement for any bisect session.
Start the session with:
git bisect startThen mark your bad commit, usually the current HEAD:
git bisect badNow mark a known good commit, such as a tagged release or a commit from before the bug appeared:
git bisect good v1.4.0Git responds by checking out a commit roughly halfway between the two points. It also tells you how many steps remain, on average, until it isolates the culprit.
The Bisect Loop Explained: Mark, Test, Repeat
Once the session starts, you enter a loop. Git checks out a commit, you test it, and you report back with one of two commands.
If the bug is present at the current commit:
git bisect badIf the bug is absent:
git bisect goodAccording to a DEV Community writeup on the process, each answer narrows the search range and git automatically checks out the next midpoint. You repeat this until git reports something like "commit abc1234 is the first bad commit."
When you are done, always run:
git bisect resetThis returns you to the branch and commit you were on before starting, according to DEV Community. Skipping this step leaves your repository in a detached HEAD state, which confuses people later.
Automating Bisect with a Test Script
Manually testing every midpoint works, but it is not the fastest path if your bug can be checked with a script. Git bisect supports full automation through git bisect run.
The idea is simple. You write a script that exits with status 0 if the commit is good, and a non-zero status if it is bad. Git handles the rest, checking out each midpoint and running your script automatically.
git bisect start
git bisect bad HEAD
git bisect good v1.4.0
git bisect run ./test-for-bug.shA test script might look like this:
#!/bin/bash
# test-for-bug.sh
npm install --silent
npm test --silent -- --grep "checkout flow"If the test suite passes, the script exits 0 and git marks that commit good. If it fails, the exit code is non-zero and git marks it bad. This is git bisect automated debugging in its simplest form: no manual marking, no waiting around.
This approach shines with CI pipelines. You can wire a bisect run into a pipeline job that triggers whenever a regression is detected, letting the machine do the binary search in git bisect run script fashion while you work on something else.
One caution: your script needs to build and test the code, not just check out the commit. If your build system is slow, automated bisect will still take real wall-clock time, even though it needs far fewer iterations than a manual crawl.
When Bisect Fails: Handling Edge Cases
Bisect is not immune to human error. If you mark a commit incorrectly, the search range gets corrupted and git may point you at the wrong commit or get confused entirely. If this happens, the safest move is to reset and start over with more careful testing.
Merge commits are a common source of confusion. A merge can introduce a bug that did not exist on either parent branch alone, which sometimes throws off the simple good and bad model. In these cases, it can help to bisect within a single branch's linear history before checking the merge itself.
Non-deterministic bugs, like flaky race conditions, also cause trouble. If a commit sometimes passes and sometimes fails, your good and bad markings become unreliable, and bisect can point at the wrong spot. Running the same test several times before marking a commit reduces this risk.
If you need to bail out partway through, you can skip a commit that cannot be tested for some reason:
git bisect skipThis tells git to try a nearby commit instead of forcing a good or bad answer on a commit that will not build or cannot be evaluated fairly.
Bisect vs Other Git Debugging Tools
Bisect is not the only way to hunt down a bug in git history, and it is not always the right first choice. Sometimes git log with filters or git blame will get you there faster.
| Tool | Best for |
|---|---|
| git bisect | Unknown range, need to find bug git history introduction point |
| git blame | Known file and line, want to see last change |
| git log -S | Searching for when a specific string appeared or disappeared |
| git log --grep | Finding commits by message content |
This shows which tool fits which kind of investigation, rather than ranking one above the others.
If you already know which file and line caused the problem, git blame answers "who touched this line last" in a single command, no binary search needed. If you are hunting for when a particular function name or string entered the codebase, git log -S is a quicker match.
Bisect earns its place when you have a reproducible symptom but no idea where in history it started. That is a different problem than "who wrote this line," and it needs a different tool.
Advanced Bisect Patterns
Bisect is not limited to a single branch. You can mark commits on different branches or tags as your good and bad boundaries, letting git search across merged history as long as there is a valid ancestry path between them.
For repositories with heavy rebasing, be aware that bisect relies on commit ancestry. If history has been rewritten, old commit hashes referenced in notes or tickets may no longer exist, so double check your good and bad references point to commits that are still reachable.
You can also narrow bisect to a specific path if the bug is isolated to part of the codebase:
git bisect start -- src/payment/This limits the search to commits that touched files under that path, which speeds things up further in large monorepos where most commits are unrelated to the area you are debugging.
Key Takeaways
Git bisect turns a slow, linear commit-by-commit search into a fast binary search that usually finishes in single digits, not hundreds of tests. The setup is simple: pick a good commit, pick a bad commit, and answer git's questions honestly as it narrows the range.
- Always start with a clean working directory before running
git bisect start - Use
git bisect runwith a test script whenever the bug is scriptable, for hands-free automation - Watch out for flaky tests and merge commits, which can throw off good and bad markings
- Run
git bisect resetwhen finished to return to your original branch - Reach for
git blameorgit log -Sinstead when you already know roughly where the bug lives
FAQ
Q: How much faster is git bisect compared to manually checking commits?A: It depends on the range, but the gap grows fast. A 300-commit range that could take dozens of manual checks in the worst case usually resolves in around nine bisect tests, because each test halves the remaining range instead of just eliminating one commit.
Q: Can git bisect be automated with test scripts?A: Yes. Using git bisect run with a script that exits 0 for good and non-zero for bad lets git test every midpoint on its own, without you manually checking out and testing each commit.
A: The search range becomes unreliable and git may point to the wrong commit at the end. If you suspect a mistake, it is usually faster to run git bisect reset and start the session over with more careful testing.
A: Yes. You can set your good and bad markers on any commits, tags, or branches, as long as there is a valid history connecting them for git to search through.
Sources
Researched from the following. Figures and claims were current when this piece was written and may have moved since.
- Stack Overflowstackoverflow.com
- Git Official Documentationgit-scm.com
- DEV Communitydev.to