Finding the Commit That Broke It With git bisect

Written By  Crosscheck Team

Content Team

May 18, 2026 9 minutes

Finding the Commit That Broke It With git bisect

Finding the commit that broke it with git bisect

Export to CSV worked in the April release. Today it throws TypeError: Cannot read properties of undefined (reading 'rows') and produces an empty file. Between the two releases there are 312 commits and nine developers, and everyone is sure it was not them.

You could read 312 diffs. Or you could run about eight checks and get the exact commit. That is what git bisect does.

Short version

  • git bisect searches through commit history by halving the range each time.
  • 300 commits take about nine checks, not 300.
  • You need one commit where it works, one where it fails, and a reliable way to check.
  • git bisect run ./check.sh does the whole search on its own.
  • The result is a commit hash, an author, and a diff, which turns a vague bug into a specific one.

What bisect actually does

Imagine 300 commits in a line. The first works, the last fails. Somewhere in between one commit changed the behaviour.

Bisect jumps to the middle commit and asks you one question: does it work here? If yes, the break is in the second half. If no, it is in the first half. Either answer removes half the remaining commits.

This is a binary search, which means each step halves the search space. Ten commits take about 4 steps. 300 take about 9. 1,000 take about 10. The maths is generous, which is why bisect is worth learning once and using for years.

What you need before you start

A local copy of the repository. You do not need to be a developer, but you do need to run the app from source, or at least run a build command.

A known good commit. Usually a release tag such as v4.2.0, or a commit from before the bug appeared. If you only know a date, use git log --before="2026-04-01" -1 --format=%H to get a hash.

A known bad commit. Normally HEAD, which means the commit you have checked out right now.

A check you trust. This is the part that decides whether bisect helps or wastes your afternoon. Your check must give the same answer every time on the same commit. A flaky test or a "sometimes it looks wrong" judgement will send bisect down the wrong half and produce a confident, incorrect answer.

If the bug came in from a report, pin down the reproduction before touching git: the exact steps, the account, the environment, and the error text. Reports filed with a tool such as Crosscheck, which captures the console output, network requests, and environment details from the page, save you from guessing at that stage.

The manual walkthrough

Run these from the repository root.

  1. Start the session:
git bisect start
  1. Mark the current commit as broken:
git bisect bad
  1. Mark a commit where it worked:
git bisect good v4.2.0

Git now checks out a commit in the middle and tells you how much is left:

Bisecting: 155 revisions left to test after this (roughly 8 steps)
[9f3c1a7e2b4d8f6a0c5e1b3d7f9a2c4e6b8d0f2a] Add pagination to export service
  1. Build and test that commit. Whatever your app needs, for example:
npm ci && npm run build && npm run dev

Then perform the reproduction steps and decide.

  1. Tell git the answer:
git bisect good   # the bug is NOT here
git bisect bad    # the bug IS here
  1. Repeat steps 4 and 5. Git halves the range each time. After the last answer it prints the culprit:
9f3c1a7e2b4d8f6a0c5e1b3d7f9a2c4e6b8d0f2a is the first bad commit
Author: Priya Raman <[email protected]>
Date:   Tue Apr 22 11:04:18 2026 +0100

    Add pagination to export service
  1. End the session and go back to where you started:
git bisect reset

Always run step 7. Until you do, you are sitting on a detached commit from the middle of history, and any work you do there is easy to lose.

When a commit cannot be tested

Some commits will not build, or land in the middle of a migration. Do not guess.

git bisect skip

Git picks a nearby commit instead. If too many commits are unbuildable, bisect will report a range rather than a single commit, which is still far better than 312 candidates.

Two more commands worth knowing:

git bisect log     # everything you have marked so far
git bisect replay bisect.log   # rerun a saved session

Save the log with git bisect log > bisect.log before you stop for the day. If you make a wrong call, edit that file to remove the bad line and replay it, instead of starting over.

Automating the search

Manual bisect is fine for six steps of clicking. If your check can be scripted, hand the whole thing over and go do something else.

Create check.sh in the repository root:

#!/usr/bin/env bash
set -e

npm ci --silent || exit 125      # cannot build: skip this commit
npm run build --silent || exit 125

npx playwright test tests/export.spec.ts

Make it executable and run the automated search:

chmod +x check.sh
git bisect start
git bisect bad
git bisect good v4.2.0
git bisect run ./check.sh

Git now checks out commits, runs your script, and reads the exit code. The whole search finishes without you.

The exit codes are the contract:

Exit codeMeaning to bisect
0This commit is good
1 to 124This commit is bad
125Cannot test this commit, skip it
126 or 127Script not executable or not found
128 or aboveAbort the whole run

The 125 line is the one people miss. Without it, a commit that fails to build is recorded as "bad", and bisect confidently blames an innocent commit.

Two rules for a good bisect script. Keep it fast, because it runs about ten times. Make it check one specific thing, because a full suite will fail for unrelated reasons on old commits and poison the result.

What to do with the commit you found

The output is a hash, an author, a message, and a date. Turn it into an action.

git show 9f3c1a7        # the full diff
git log -1 9f3c1a7      # message and date only

Then read the diff with the bug in mind. In most cases you can point at the exact change in a few minutes, because the diff is usually small.

Add all of it to the ticket:

Regression introduced in 9f3c1a7 ("Add pagination to export service", Priya Raman, 22 Apr 2026), found by bisect between v4.2.0 and HEAD. The diff changes exportRows() to read result.data.rows, but the paginated response returns result.rows. Console shows TypeError: Cannot read properties of undefined (reading 'rows').

That version of the ticket goes straight to the right person with the answer attached. Compare it to "export is broken, worked last month".

Common mistakes

MistakeWhat happensFix
Testing with a flaky checkBisect blames the wrong commitRepeat the check twice on the first commit before starting
Forgetting to rebuild between stepsYou test old build outputPut the build inside your check script
Marking a build failure as badInnocent commit blamedUse git bisect skip, or exit 125 in a script
Skipping git bisect resetYou stay on a detached commitAlways reset when finished
Bisecting across a database migrationApp fails for unrelated reasonsNarrow the range to one side of the migration
Wrong direction of good and badBisect searches the wrong halfConfirm the bug at bad and its absence at good first

Frequently asked questions

Do I need to be a developer to use git bisect?

No. You need to run the app from the repository and answer one question per step. If someone gives you the build command, the rest is copying five commands.

What if the bug came from a dependency, not our code?

Bisect will point at the commit that changed the lock file, for example package-lock.json. That is still useful, since it names the dependency and the version jump to investigate.

How many commits is too many to bisect?

There is no practical limit. A thousand commits take roughly ten steps. The cost is the build and test time per step, not the number of commits.

Can I bisect without building the whole app?

Sometimes. If the bug is in a single file or a unit-testable function, run only that test in your script. A five-second check makes a ten-step bisect finish in under a minute.

What if bisect ends with a merge commit?

That means the break came in through a branch. Bisect inside that branch by using its first and last commits as the new good and bad points.

Related Articles

Contact us
to find out how this model can streamline your business!

Trusted by thousands ofengineering teams worldwide.

Add to Chrome
200+ reviews · 100k+ users
Crosscheck browser extension capture controls

Join the Crosscheck Community

Stay in the loop with Crosscheck's newest features and insights.