Great work!

XP to next level

BugEater
EN

A Full Manual Bisect Session

Learning Objectives

By the end of this lesson you will be able to:

  • Run a bisect from start to finish and read every message it prints
  • Mark commits good and bad and know what each does to the range
  • Interpret the final "is the first bad commit" output
  • End a session cleanly with git bisect reset

Starting

git bisect start
git bisect bad                 # HEAD is broken
git bisect good v4.1           # this tag was fine
Bisecting: 189 revisions left to test after this (roughly 8 steps)
[4c1f8ab2e94b7d0c1f5a2836b4c9d0e1f2a3b4c5d] refactor: extract discount calculation

Two things just happened. Git worked out the range — 380 commits, so 189 on each side of the midpoint — and it checked out the midpoint commit. Your working directory is now that commit, in detached HEAD.

Git also tells you the budget: roughly 8 steps. You can plan your afternoon.

The Loop

Build. Run your reproduction. Answer.

npm ci && npm run build         # whatever your project needs
# run the reproduction steps

Bug present:

git bisect bad

Bug absent:

git bisect good

Either way, Git narrows and checks out the next commit:

Bisecting: 94 revisions left to test after this (roughly 7 steps)
[9d3e072a1b8c4f5e6d7a8b9c0d1e2f3a4b5c6d7e] feat: add discount audit trail

Repeat. Eight or nine iterations, and each one is exactly this: build, test, one word.

Do not switch branches during a session. Git is driving your working directory now, and moving it yourself confuses the search.

The Result

4c1f8ab2e94b7d0c1f5a2836b4c9d0e1f2a3b4c5d is the first bad commit
commit 4c1f8ab2e94b7d0c1f5a2836b4c9d0e1f2a3b4c5d
Author: Maria Kovalenko <maria@example.com>
Date:   Tue Mar 12 14:22:07 2026 +0200

    refactor: extract discount calculation into a helper

 src/payments/discount.ts | 24 ++++++++++++++----------
 src/payments/refund.ts   |  8 ++++----
 2 files changed, 18 insertions(+), 14 deletions(-)

Read "first bad commit" precisely. It means: this commit is bad, and its parent is good. That is the exact boundary where the behaviour changed, which is what you asked for.

Everything you need for the report is in that block — commit, author, date, message, and the files touched.

Ending the Session

git bisect reset
Previous HEAD position was 4c1f8ab refactor: extract discount calculation
Switched to branch 'main'

Do not skip this. Bisect leaves you in detached HEAD on a historical commit. Without the reset, your next commit goes nowhere attached to a branch, and your working copy looks mysteriously like last March.

reset also clears the bisect state, so the next session starts clean.

Reviewing What Happened

Before you reset, or afterwards from the log:

git bisect log
git bisect start
# bad: [7b2e551...] fix: correct totals rounding
git bisect bad 7b2e551...
# good: [e2b90d1...] release: v4.1
git bisect good e2b90d1...
# good: [4c1f8ab...] refactor: extract discount calculation
git bisect good 4c1f8ab...

This is your audit trail. Two uses for it.

Reviewing your own answers. If the result is surprising, read back through and check whether one of your verdicts was wrong.

Replaying. Save the log and someone else can reproduce your search exactly:

git bisect log > bisect-PAY-4417.txt
git bisect reset
# later, or on a colleague's machine:
git bisect replay bisect-PAY-4417.txt

Attach that file to the ticket. It makes your result verifiable rather than merely asserted.

When You Answer Wrong

If you realise mid-session that a verdict was mistaken, do not start over:

git bisect log > /tmp/bisect.txt
# edit the file, remove or fix the wrong line
git bisect reset
git bisect replay /tmp/bisect.txt

The replay redoes the whole search with the corrected answers, skipping every step you already got right.

Pro Tip: Keep a note of the commit ID and your verdict at each step, in a scratch file. git bisect log gives you the same information, but writing it down yourself makes you slow down enough to be sure of each answer — and being sure is the entire reliability of the method.

Key Takeaways

  • git bisect start, then git bisect bad and git bisect good <ref> to set the range
  • Git checks out the midpoint each time and tells you how many steps remain
  • The loop is: build, test, good or bad — nothing else
  • Do not switch branches while a bisect is running
  • "First bad commit" means this one is bad and its parent is good
  • git bisect reset at the end, always — it returns you to your branch
  • git bisect log and git bisect replay make a session auditable and repeatable

Quiz

What happens immediately after git bisect good v4.1 completes the range?

"4c1f8ab is the first bad commit" tells you what, exactly?

Why must you run git bisect reset at the end?

Halfway through, you realise you marked a commit good when it was actually broken. What is the efficient fix?