Say the build broke sometime after the last release tag, and a long stretch of commits sits in between. Reading every diff is out. git bisect handles it with a binary search over commit history, looking for the first commit that introduced the bug. You give it one commit known to be good and one known to be bad, it checks out the midpoint, and the range gets cut in half after every verdict.
The search follows commit ancestry, not dates. It only really pays off once the range is too big to review diff by diff.
Linux kernel developer Ingo Molnar reported using it roughly once a day on average, and more often during merge windows (Git bisect paper, Linux-Kongress 2009).
What Is git bisect?

Git needs two commits from you before it can do anything. One is bad, usually the current HEAD where the bug shows up, and the other is good, like a release tag from before the bug. From there it narrows the range between them until a single commit is left.
Every step needs a check, run by hand or by script, and the end result is the first bad commit, with refs/bisect/bad pointing at it.
Because every change lives in the project’s version control history, bisect can test any past state of the code without extra tooling.
Git cannot know how your application should behave. Every verdict comes from you or from the script you hand it.
The Git manual says the command can find the commit that changed any property of a project, such as the commit that fixed a bug or sped up a benchmark (Git documentation, 2026). That is why old and new can replace good and bad.
The result is a commit, not a line. It shows where the change landed, and reading the diff still takes a person.
A Sumo Logic tester used it on 15 commits that landed between a morning and an afternoon test run, and found the one that broke the suite (Sumo Logic, 2025).
How Does git bisect Find the Bad Commit?

The number of tests grows with log2 of the range size, not with the range itself, because Git halves the commit range after every verdict. At each step it checks out the commit in the middle and waits for you to mark it.
Mark the middle commit bad and the first bad commit sits at or before it, so Git drops everything after it. Mark it good and the culprit has to come later, so Git drops everything up to and including it.
The loop ends when no untested revisions remain. Git then prints the full commit hash of the first bad commit, plus the files it touched.
Early manuals (Git 1.5.3) state that bisect uses git rev-list with its –bisect option to pick each midpoint.
The documentation and a couple of walkthroughs give concrete step counts.
- 675 revisions left, roughly 10 steps, then 337 left, roughly 9 steps (Git documentation, 2.56.0 manual, 2026)
- About 12 commits between v1.0 and the bad commit ended after 3 verdicts (Pro Git, 2nd edition)
- 12 commits took 4 test runs in a disposable repository on Git 2.43.0 (DEV Community, 2026)
Calculated from the halving rule, 1,000 commits need at most 10 tests and 1,000,000 need at most 20 (2^10 is 1,024 and 2^20 is 1,048,576).
Why a branchy history changes the count
Git picks the midpoint by the number of candidate commits, not by date, so a commit from a side branch can come up next.
It also explains why the output says roughly. The step count is an estimate from the size of the candidate set, and the two 12-commit examples above differ by one because the number of verdicts depends on where the culprit sits in the range.
How to Run a Manual git bisect Session

Only the testing step in a manual session needs your judgment. The rest is typing.
- Run
git bisect start. - Run
git bisect badto mark the current commit as bad, or pass a revision. - Run
git bisect goodwith a known good revision, such as a tag. - Build and test the commit Git checked out, then answer with
git bisect goodorgit bisect bad. - Repeat step 4 until Git prints the first bad commit.
- Run
git bisect resetto restore your original HEAD.
The shortcut form sets both endpoints in one line. git bisect start HEAD v1.0 puts the bad commit first and the good commit second (Pro Git, 2nd edition).
You can also shrink the search before it begins. Add a pathspec, as in git bisect start -- arch/i386 include/asm-i386, to test only commits that touch those paths, or list several known good commits after the bad one to cut the search space further.
While the session runs, Git checks out candidate commits by hash, which leaves you in a detached HEAD state. Pro Git warns that skipping the final reset leaves the repository in an odd state.
Reset has a few destinations depending on what you pass it.
git bisect resetgoes back to the commit checked out before the sessiongit bisect reset bisect/badlands on the first bad commitgit bisect reset HEADstays on the current bisection commit
Uncommitted edits can block a checkout that would overwrite them, so commit or stash your changes before starting.
A separate working tree made with git worktree add keeps the session away from unfinished work in the main checkout. I’d do that every time if the main checkout is messy.
How to Automate It with git bisect run

git bisect run takes a command, runs it on every checked-out commit, and reads the exit code as the verdict: good, bad, skip or abort.
| Exit code | What Git does | Typical cause |
|---|---|---|
| 0 | Marks the commit good (old) | Test passes |
| 1 to 127, except 125 | Marks the commit bad (new) | Test fails |
| 125 | Skips the commit | Build fails, commit cannot be tested |
| 128 to 255 | Aborts the whole bisect | Script killed by a signal, or a deliberate exit 255 |
Git documentation (2.56.0 manual, 2026) explains that 125 is the highest sensible value for skip, because POSIX shells use 126 and 127 for a command that is not executable or not found.
There’s a catch. Both 126 and 127 count as bad, so a mistyped script path exits 127, Git marks every commit bad, and the search ends on a meaningless result.
A C program that calls exit(-1) leaves 255 in the shell, which aborts the run.
For broken builds, put make || exit 125 at the top of the script. If you don’t want a separate script file at all, git bisect run sh -c "make || exit 125; ~/check_test_case.sh" does the same job in one line.
A full run looks like this: git bisect start HEAD HEAD~10 --, then git bisect run ~/test.sh, then git bisect reset. The current manual also lists –reset-when-found for git bisect start and git bisect run, which restores the original HEAD or leaves the first bad commit checked out.
Keep the script outside the repository. The manual calls that safer, and a DEV Community walkthrough (2026) adds the reason: older commits do not contain test files that arrived later, so the script fails there for the wrong reason.
Write the check as a small regression test that fails only on the bug you are chasing. Any other failure belongs on exit 125.
Temporary patches work too. The manual’s hot-fix example merges a fix branch, builds, runs the test, then undoes the tweak with git reset –hard and exits with the test’s status, or with 125 when the merge or build fails.
What to Do With Commits You Cannot Test
Run git bisect skip and Git checks out a different commit nearby. You pay in precision, because a culprit that sits next to skipped commits cannot be named exactly.
Skip takes a few forms. Bare git bisect skip hits the current commit, git bisect skip v2.5..v2.6 covers every commit after v2.5 up to and including v2.6, and git bisect skip v2.5 v2.5..v2.6 is the same range with v2.5 itself added.
The manual warns that skipping a commit adjacent to the culprit leaves Git unable to say which of the two came first (Git documentation, 2.56.0 manual, 2026). When only skipped commits remain, Git prints the list of possible first bad commits and stops.
You can choose the replacement yourself instead. Open git bisect visualize, move to a commit near the suggestion, test it, and mark it in the usual way.
Commits near an untestable commit tend to be untestable as well, and the best midpoints cluster together.
git bisect skip shipped in Git 1.5.4 (February 1, 2008). Until Git 1.6.4 (July 29, 2009) it offered the second-best midpoint after a skip, and Ingo Molnar and H. Peter Anvin complained that the best midpoints often sat inside a block of untestable commits (git-bisect-lk2009 paper).
Today Git picks a pseudo-random commit biased away from the good and bad endpoints. That keeps the search from walking one broken stretch a commit at a time.
When the broken stretch is one breaking commit plus the commit that repairs it, the paper’s workaround is a side branch where the repair is squashed into the breakage with an interactive rebase. You then bisect the side branch.
Custom Terms, Logs and Replay
Terms are fixed per session. Pick good and bad, old and new, or a custom pair when you start, and don’t mix them afterward.
git bisect start --term-old broken --term-new fixedlets you mark commits withgit bisect brokenandgit bisect fixed- For plain old and new, run
git bisect startwith no commits, thengit bisect old REVandgit bisect new REV git bisect termsprints the active pair, and –term-old or –term-new prints just one of them
A custom name cannot reuse a subcommand name such as reset or start (Git documentation, 2.56.0 manual, 2026). The manual’s own example for performance work is –term-old fast –term-new slow.
A wrong verdict sends every later step into the wrong half. In the paper’s example range of 10,928 revisions (roughly 14 steps), the first verdict alone decides which half the other 13 steps search (git-bisect-lk2009 paper).
The repair is a log edit. Run git bisect log, save the output to a file, delete the bad lines, run git bisect reset, then git bisect replay with that file.
| Command | Effect | Use it to |
|---|---|---|
| git bisect log | Prints the verdicts so far | Save or share a session |
| git bisect replay FILE | Reapplies a saved log | Fix a wrong verdict after a reset |
| git bisect visualize | Shows the remaining suspects | Pick a better commit to test |
| git bisect next | Computes the next commit to test | Resume after checking out something else |
visualize opens gitk when Git detects a graphical environment through DISPLAY, SESSIONNAME, MSYSTEM or SECURITYSESSIONID, and falls back to git log otherwise. Options such as –stat pass through.
–no-checkout skips the working tree and moves a reference called BISECT\_HEAD to each candidate instead. It is assumed in bare repositories and cannot be combined with –reset-when-found.
The manual’s example for it locates a good region of the object graph in a damaged repository, testing BISECT\_HEAD without touching any files.
When git bisect Fails or Does Not Fit

An unreliable check, a non-linear history, or project state that does not roll back with the checkout will all give you a wrong or vague answer. Most of these fail silently, because the search still converges on a commit.
Flaky and non-deterministic bugs
One false verdict sends the rest of the search into the wrong half. Git does not repeat tests by itself, and the 2.56.0 manual lists no option for it.
- Rerunning inside the script helps a little, but retrying is a stopgap, not a fix, because the real repair is a deterministic test
- When reruns disagree, exit 125, which costs precision whenever the culprit sits beside skipped commits
- Ingo Molnar told the paper’s author that a failed bisection signals a non-deterministic bug, one that depends on timing or kernel image layout
BBChop, linked from the same paper, applies Bayesian search to intermittent bugs. It assumes a version never fails when it is good, so it handles false negatives only.
Google’s syzkaller project keeps an umbrella issue for poor bisection results (syzkaller issue 1051). It lists searches that ended on a random commit after a broken boot hid the original crash behind another bug, searches thrown off by unrelated leaks and other bugs, and searches that failed because the crash reproduced too rarely.
Merge-heavy history
Merges change what counts as a first bad commit, so Git offers a flag and checks merge bases.
- –first-parent follows only the first parent at each merge, so the merge itself gets reported and the merged branch’s commits are ignored
- That removes false positives when a merged branch held broken or non-buildable commits but the merge was fine
- The answer then names the merge, so a second bisect inside the merged branch finds the commit within it
- A good commit does not have to be an ancestor of the bad one, and Git tests the merge bases first
git bisect start dev main finds a regression on dev that main does not have. If a merge base turns out bad, the run stops, because the bug was fixed between that merge base and the good commits (git-bisect-lk2009 paper).
A merge can also introduce a regression with no conflict, when one branch changes what a function means while the other adds calls to it. The paper recommends git rebase to linearize history, either to avoid such merges or to bisect a linear copy.
Submodules and external state
git bisect moves only the superproject, so submodules stay where they were unless the script moves them.
Espressif’s ESP-IDF bisect guide runs git submodule sync –recursive and git submodule update –init –recursive after each checkout. It also builds in a separate project folder, so bisect’s checkouts do not disturb the build directory.
Submodules are not checked out by default on clone or pull, and submodule.recurse makes checkout recurse into them (gitsubmodules manual, 2026).
Put the update commands in the run script and exit 125 when the update fails.
Bugs that appear, vanish and return
Git assumes a single first bad commit and treats every commit after it as bad (git-bisect-lk2009 paper). A bug that was fixed and later reintroduced breaks that assumption, so the result depends on where the midpoints land.
Bisect holds up when the check gives the same verdict on every run and the good commit is truly good. Commits in between also need to build, or the script has to exit 125 for the ones that don’t.
It falls apart with intermittent bugs, and with merges that brought in broken side branches when –first-parent is not used. Submodules or build output carrying state from another commit do it too. So does a bug that came, went and came back.
git bisect, git blame or git log -S: Which One to Use?
Pick the tool by what you already know. A known line points to git blame, a known string points to git log -S, and a symptom with no known location points to git bisect.
| Tool | Question it answers | What you must know |
|---|---|---|
| git bisect | Which commit changed the behavior? | A good commit, a bad commit, a check |
| git blame | Which commit last touched this line? | The file and line range |
| git log -S | Which commits added or removed this string? | The string itself |
The Git project’s own bisect paper calls its Makefile example a toy, because git blame or git log -S finds that commit more directly.
git blame annotates each line with the revision that last modified it, and -L limits the output to a range. Pro Git demonstrates it on the Linux kernel Makefile, lines 69 to 82.
It says nothing about lines that were deleted or replaced (Git documentation, 2.56.0 manual). A formatting-only commit also steals the blame, so –ignore-rev and –ignore-revs-file skip such commits.
The pickaxe, git log with -S and a string, searches the diffs for the commits that added or removed that string. That covers deleted code, which blame cannot see.
Bisect costs more setup, namely a good commit and a repeatable check. Ingo Molnar puts a roughly 15-step kernel bisection at 20 to 30 minutes in the best automated case, and rarely over an hour with manual help (git-bisect-lk2009 paper).
Reach for it last when you know the line or the string, and first when all you have is the symptom.
git bisect FAQ
Does git bisect depend on the programming language or the Git host?
No. It works on the commit graph and reads only the verdict of your check, so a Rust, JavaScript or Python project behaves the same.
GitHub, GitLab or a private server makes no difference, because the search runs locally.
Can git bisect run inside a CI pipeline?
Yes. Any CI job that can run a shell command can call git bisect run, provided the checkout holds the full history.
GitHub’s actions/checkout fetches a single commit by default, so set fetch-depth: 0 first (GitHub, 2026).
The Git bisect paper (2009) also describes nightly failures that launch a bisect and email the first bad commit’s author.
Is there a Mercurial equivalent of git bisect?
Yes. hg bisect marks changesets good, bad or skip, and its –command option runs a check automatically (Mercurial documentation, 2026).
One exit code differs. Mercurial aborts on 127 (command not found), while Git counts 127 as bad.
Who created git bisect?
Linus Torvalds, according to the Git project’s bisect paper (2009).
He wrote the original search algorithm, and Junio Hamano, Git’s maintainer at the time of writing, later refined it.
Can QA testers or end users run git bisect?
Yes, if they have the source or every build. The Git bisect paper says QA staff and even end users can bisect, and that they work in the environment where the bug occurs.
That setup often makes the regression easier to reproduce than it would be on a developer machine.
Confirming the First Bad Commit Before You Fix It
What git bisect prints is a suspect, not a verdict. The commit is the first one where your check failed, and nothing more.
Rerun the check on that commit and on its parent, then read the diff for the change that matters. After that you can revert the commit, or fix forward and commit a regression test with the fix.
That order guards against a flaky check naming an innocent commit, since reverting the wrong one hides the bug instead of removing it.
git revert undoes the commit without rewriting history. The trade-off is that it also removes whatever the commit delivered, so fix forward when the change is wanted.
The advice changes only if Git adds built-in repeat testing for unreliable checks. The 2.56.0 manual (September 28, 2026) lists no such option.
- Notepad++ Android Options: Apps That Come Close - October 21, 2026
- Data Management Services: A Practical Guide for Growing Companies - October 21, 2026
- MongoDB Cheat Sheet - October 20, 2026




