Hunting Bugs with git bisect
Reviewed & published by Brayan K
"It worked last week, it's broken now, and there are 300 commits in between." By the end of this lesson you'll use git bisect to binary-search your history and pinpoint the exact commit that introduced a bug — often in under ten checks, and fully automated.
Part of the free Git course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
What You'll Learn
- Start a session with git bisect start and bound it with good/bad
- Understand the binary search that makes bisect O(log n) fast
- Test each midpoint and answer git bisect good / bad
- Automate the whole hunt with git bisect run <script>
- Use exit codes correctly: 0 = good, non-zero = bad, 125 = skip
- Always finish with git bisect reset to restore your branch
1️⃣ Starting a Bisect
Tell Git two things: a bad commit where the bug exists, and a good commit where it didn't. Git then checks out a commit halfway between them and asks you to test it. Each answer halves the range, so even hundreds of commits resolve in about log₂(n) steps.
# A bug appeared "somewhere in the last few hundred commits". Checking them
# one by one would be brutal. git bisect does a BINARY SEARCH instead:
# halve the range each step until one commit is left.
# 1) Start a bisect session
git bisect start
# 2) Mark the current (broken) commit as bad
git bisect bad
# 3) Mark a commit you know was working as good (a hash, tag, or HEAD~50)
git bisect good v1.0
# Git now checks out a commit halfway between good and bad and asks you
# to test it. You answer "git bisect good" or "git bisect bad" each time.2️⃣ Answering good / bad
At each stop, build and test the checked-out commit, then report the result. Git keeps narrowing until one commit remains and announces it as the first bad commit. Whatever happens, finish with git bisect reset to return to your original branch.
# At each step Git stops on a commit. Build/run your test, then tell Git
# what you found. It keeps halving the range automatically.
git bisect bad # this commit still has the bug
# ...Git jumps to a new midpoint...
git bisect good # this one works fine
# ...repeat until one commit is left...
# When the search narrows to a single culprit, Git announces it:
# <hash> is the first bad commit
# ALWAYS clean up afterwards — this returns you to your original branch:
git bisect reset3️⃣ Automating with bisect run
The real power is git bisect run. Give it a command whose exit code decides the verdict — 0 for good, non-zero for bad — and Git tests every midpoint itself, hands-free. Your project's test suite is the perfect script. The special code 125 tells bisect to skip an untestable commit.
# The real magic: automate the whole thing. Give bisect a script (or one
# command) that returns exit 0 when the commit is GOOD and non-zero when BAD.
# bisect runs it at every step and finds the culprit with zero typing.
git bisect start HEAD v1.0 # bad=HEAD, good=v1.0 in one line
git bisect run npm test # exit 0 = good, non-zero = bad
# Use your project's test command — anything with a meaningful exit code:
# git bisect run ./run-tests.sh
# git bisect run sh -c '! grep -q BUG app.txt'
#
# Special exit code 125 tells bisect "skip — this commit can't be tested".Your turn. Drive a full automated bisect from start to reset. Fill in the three blanks.
# 🎯 YOUR TURN — replace each ___ then run it in a repo with a known bug.
# Goal: let bisect find the first broken commit for you.
# 1) Begin the session
git bisect ___ # 👉 the subcommand that opens a bisect
# 2) Mark the current commit as broken
git bisect bad
# 3) Mark an older, working commit as good
git bisect good HEAD~10
# 4) Automate the search with your test command
git bisect ___ npm test # 👉 the subcommand that runs a test each step
# 5) Clean up and return to your branch
git bisect ___ # 👉 the subcommand that ends the session
# ✅ Expected: Git prints "<hash> is the first bad commit".Common Errors (and the fix)
- Stuck on a detached HEAD after bisecting: you forgot to finish. Run git bisect reset to return to your original branch.
- bisect run always reports the same commit: your script's exit code is backwards. Confirm it exits 0 when the bug is absent and non-zero when present.
- "You need to start by 'git bisect start'": you marked good/bad before starting. Run git bisect start first.
- A midpoint commit won't compile: don't mark it bad — that pollutes the result. Use git bisect skip (or exit 125 in a run script).
- Good and bad swapped: if you mark the old commit bad and the new one good, bisect searches the wrong direction. The newer/broken commit is bad.
Pro Tips
- 💡 One-line start: git bisect start <bad> <good> sets both bounds at once.
- 💡 Make a tiny repro script: a focused test runs far faster than the whole suite at each step.
- 💡 See the trail: git bisect log records your answers; git bisect replay reruns them.
- 💡 Visualize progress: git bisect visualize shows the remaining suspect commits.
📋 Quick Reference
| Command | Purpose |
|---|---|
| git bisect start | Begin a bisect session |
| git bisect bad / good | Mark the current commit broken / working |
| git bisect run cmd | Automate using a script's exit code |
| git bisect skip | Skip an untestable commit |
| git bisect reset | End the session, restore your branch |
| git bisect log / replay | Record / re-run your answers |
Mini-Challenge: Catch the Regression
No commands given this time — just the plan. Run a fully automated bisect and name the commit that introduced a bug.
# 🎯 MINI-CHALLENGE: catch the regression automatically.
# No commands given — just the plan. Run each step in a real repo.
#
# 1. Identify the broken state (HEAD) and a commit you know worked
# 2. Start a session: git bisect start HEAD <good-commit>
# 3. Write a one-line test that exits 0 when good, non-zero when bad
# 4. Let "git bisect run <your-test>" drive the search to the end
# 5. Read off the first bad commit, then run "git bisect reset"
#
# ✅ Expected: bisect names the exact first bad commit, and reset returns
# you to your original branch with no bisect state left over.
# your commands here🎉 Lesson Complete!
- ✅ git bisect binary-searches history to find the first bad commit
- ✅ Bound the search with one good and one bad commit
- ✅ Answer good/bad at each midpoint, or automate with bisect run
- ✅ Exit codes: 0 = good, non-zero = bad, 125 = skip
- ✅ Always end with git bisect reset
Practice quiz
What problem does git bisect solve?
- Merging branches
- Recovering deleted files
- Finding the exact commit that introduced a bug via binary search
- Renaming branches
Answer: Finding the exact commit that introduced a bug via binary search. git bisect binary-searches your history to pinpoint the first commit where a bug appeared.
Which command starts a bisect session?
- git bisect start
- git bisect run
- git bisect begin
- git bisect init
Answer: git bisect start. git bisect start opens a session; you then mark a good and a bad commit.
After 'git bisect start', what two markers do you give Git?
- A branch and a tag
- Two file paths
- An author and a date
- One known-good commit and one known-bad commit
Answer: One known-good commit and one known-bad commit. You mark a bad commit (bug present) and a good commit (bug absent) to bound the search.
Roughly how many tests does bisect need across 1,000 commits?
- About 1,000
- About 10
- About 500
- Exactly 2
Answer: About 10. Binary search needs about log2(N) steps — roughly 10 tests for 1,000 commits.
What does 'git bisect run <script>' do?
- Automates bisect by using the script's exit code at each step
- Runs the bug
- Runs all commits at once
- Reverts the bad commit
Answer: Automates bisect by using the script's exit code at each step. git bisect run executes your script at each step: exit 0 means good, non-zero means bad.
In a bisect run script, what exit code marks a commit as BAD?
- 0
- 125
- Any non-zero code (except 125)
- -1 only
Answer: Any non-zero code (except 125). Exit 0 = good, non-zero = bad. Exit 125 specially means 'skip / cannot test this commit'.
What does exit code 125 mean to git bisect run?
- Bad commit
- Skip this commit — it can't be tested
- Good commit
- Abort the bisect
Answer: Skip this commit — it can't be tested. 125 tells bisect the commit is untestable, so it skips it like git bisect skip.
What MUST you run when the bisect is finished?
- git bisect end
- git bisect stop
- git checkout main
- git bisect reset
Answer: git bisect reset. git bisect reset returns you to your original branch and ends the session cleanly.
If a commit can't be built or tested, which command skips it?
- git bisect ignore
- git bisect skip
- git bisect pass
- git bisect next
Answer: git bisect skip. git bisect skip moves past an untestable commit so bisect keeps narrowing the range.
What does bisect print when it finishes?
- 'all commits are good'
- 'merge complete'
- '<hash> is the first bad commit'
- nothing
Answer: '<hash> is the first bad commit'. Bisect reports the first bad commit, with its diff, so you know exactly what introduced the bug.
Continue this course
- Previous: Finding Who Changed What with git blame
- Next: Git Configuration & Aliases — Set up your identity, editor, and time-saving aliases with git config
- Quick reference: Git cheat sheet
Frequently asked questions
How does git bisect work under the hood?
It does a binary search of your commit history. You give it one commit where the bug is present (bad) and one where it isn't (good). Git checks out the commit halfway between them and asks you to test it; your answer halves the remaining range each time. That's why it only needs about log2(N) tests — roughly 10 for a thousand commits.
What's the difference between marking good/bad manually and using bisect run?
Manual bisect checks out each midpoint and waits for you to type git bisect good or git bisect bad after testing. git bisect run automates that: you hand it a script or command whose exit code decides good (0) or bad (non-zero), and bisect drives the whole search to the answer without further input.
What do the exit codes mean in a bisect run script?
Exit 0 marks the commit good, any non-zero code (1–124, 126, 127) marks it bad, and the special code 125 means 'this commit can't be tested — skip it', the same as git bisect skip. Make sure your test script returns these correctly, or bisect will draw the wrong conclusion.
Do I have to run git bisect reset at the end?
Yes. During a bisect Git leaves you on a detached HEAD at whatever commit it last tested. git bisect reset returns you to the branch you started on and clears the bisect state. Forgetting it leaves you stranded on a random old commit, which is confusing.
What if a commit in the middle won't build or run?
Use git bisect skip (or exit 125 in a run script). Git will pick a nearby commit to test instead and keep narrowing. If several commits in a row are untestable, the result may be reported as a range, but bisect still gets you very close to the culprit.