Blog · Guides · 2026-08-03

How to Find the Commit That Introduced a Bug (git bisect)

Klavity
TL;DRgit bisect binary-searches your commit history to find the exact commit that introduced a bug. Mark one known-good and one known-bad commit, and Git checks out the midpoint for you to test; each answer halves the range, so 1,000 commits take about 10 tests. Automate it with `git bisect run <script>` to find the culprit hands-free.

git bisect finds the exact commit that introduced a bug by binary-searching your history. You mark one commit where the bug is absent (good) and one where it's present (bad), and Git repeatedly checks out the midpoint for you to test. Each verdict halves the range, so a 1,000-commit span narrows to the culprit in about 10 tests instead of reading every diff. When the search ends, Git prints the first commit where the bug appears — the change to inspect.

When should you use git bisect?

Reach for bisect when a bug used to be absent and now isn't, and you can reliably tell “buggy” from “fine” at any commit. That covers most regressions: a feature that broke between releases, a test that started failing, a performance cliff, or output that changed. It is far faster than skimming diffs because it doesn't care how many commits are in range — the work grows with the logarithm of the range, not its length.

Bisect is a poor fit when you can't consistently reproduce the bug (a flaky failure will send the search down the wrong branch), when every commit in range is broken for unrelated reasons, or when the bug was never introduced by a commit at all — a data or environment change, for example. In those cases, fix reproducibility first, then bisect.

How to run git bisect step by step

The manual loop is three commands to start and one verdict per step:

  1. Start the session. Run git bisect start. Git now expects you to bracket the range.
  2. Mark the current (broken) commit bad. Run git bisect bad with no argument to mark HEAD, or git bisect bad <rev> for a specific commit or tag.
  3. Mark a known-good commit. Run git bisect good <rev> — often the last release tag where the bug was absent, e.g. git bisect good v2.3.0. Git checks out the midpoint automatically.
  4. Test and report. Reproduce your check at the checked-out commit, then run git bisect good or git bisect bad. Git jumps to the next midpoint and tells you how many steps remain.
  5. Repeat until it names the culprit. When one commit is left, Git prints “<sha> is the first bad commit” with the full message and diff.
  6. End the session. Run git bisect reset to return to the branch you started on.

Keep your verdicts honest. One wrong “good” or “bad” poisons the whole search — Git will confidently point at the wrong commit — so if you're unsure, don't guess.

How do I skip a commit that won't build?

Sometimes the commit Git lands on can't be tested: it won't compile, the feature doesn't exist there yet, or a dependency is mid-migration. Marking it good or bad would be a lie. Instead run git bisect skip, and Git picks a nearby untested commit. You can skip a whole range with a revision expression, for example git bisect skip v2.1..v2.1.3.

If skipped commits leave a gap Git can't fully resolve, it won't guess — it reports the smallest set of commits that could each be the first bad one, and you inspect those by hand. That honesty is the point: bisect narrows the search even when part of your history is untestable.

Automate the search with git bisect run

The real speed-up is handing the testing to a script. git bisect run <command> executes your command at every step and reads its exit code:

  • 0 — the commit is good.
  • 1–124 or 126–127 — the commit is bad.
  • 125skip this commit (can't be tested).
  • 128 and above — abort the bisect (treat as a hard error).

So the whole run becomes: git bisect start; git bisect bad; git bisect good v2.3.0; git bisect run npm test -- path/to/failing.test.ts. Git walks the history unattended and stops on the first bad commit. Two tips make this reliable:

  1. Write the tightest repro you can. A single failing test or a one-line script that exits non-zero on the bug is faster and less flaky than the full suite. Have it exit 125 when the build fails so those commits are skipped, not blamed.
  2. Make it deterministic. A flaky repro turns bisect into a coin flip. Pin seeds, mock the network, and remove timing races before you start — the same discipline that keeps a normal test suite trustworthy.

What to do after git bisect finds the commit

Finding the commit is the start of root-cause work, not the end. Read the diff to understand why it broke the behavior rather than reverting on reflex — the offending line may be correct and merely have exposed a latent bug elsewhere. For a structured way to go from “this commit” to “this cause,” see our root cause analysis guide.

Then close the loop so the same regression can't return: convert your bisect repro into a test that fails before the fix and passes after, following how to turn a bug into a regression test. If UI selectors keep drifting and breaking those tests, self-healing end-to-end tests can repair the brittle parts so your regression coverage survives refactors — and future bisect runs stay honest because the test itself stays green until real behavior changes.

Key takeaways

  • Start with `git bisect start`, then mark a bad and a good commit to open the search range
  • Return a decisive verdict at each step — good, bad, or skip — and never guess
  • Automate the whole search with `git bisect run <script>` using exit code 125 to skip
  • Convert the culprit commit into a regression test so the bug can't silently return

FAQ

How many tests does git bisect need?

About log2(N). Because each test halves the remaining range, a span of 1,000 commits resolves in roughly 10 steps and 10,000 commits in about 14. That is why bisect beats reading diffs one by one.

What does 'git bisect skip' do?

It tells Git the current commit can't be tested — it won't build, or the feature didn't exist yet — so Git picks a nearby commit instead of counting it as good or bad. If skips leave a range Git can't fully resolve, it reports the smallest set of commits that could be the first bad one.

Can git bisect run without me testing each commit by hand?

Yes. `git bisect run <cmd>` runs your command at each step and reads its exit code: 0 means good, 1–124 (except 125) means bad, and 125 means skip. Point it at a test, a build, or a one-line repro script and it finds the first bad commit unattended.

How do I turn the bug git bisect found into a permanent test?

Write the repro from your bisect run as a failing test, confirm it fails before the fix and passes after, and add it to your suite so the regression can't return. See our guide on turning a bug into a regression test.

Catch bugs the moment a human sees them

Klavity: right-click bug reports, AI personas that review your product, and self-healing tests.

Get started free