WebTech1223411 logo WebTech1223411Web tech, read closely
Dev Tools

How Git Bisect Finds the Commit That Broke Your Build

A bug appears, and you know it worked last week. Somewhere in the hundreds of commits since, one change broke it.

A timeline of commits with a binary search narrowing toward a single broken one

A bug appears, and you know it worked last week. Somewhere in the hundreds of commits since, one change broke it. Reading every diff by hand is slow and demoralising, and staring at the log rarely helps because the guilty change often looks innocent. Git has a tool built for this exact moment. The bisect command runs a binary search across your history, asking you a yes or no question at each step, and lands on the single commit that introduced the problem in a handful of checks rather than a long afternoon of reading.

Why a binary search beats reading the log

If you have five hundred commits between a known good state and the current broken one, checking them one by one is five hundred potential tests in the worst case. A binary search halves the search space at every step, so those five hundred commits collapse to about nine checks. That is the whole promise of bisect. Instead of asking which change looks suspicious, a question that leans on intuition and often misleads, it asks a mechanical question at the midpoint, learns which half the bug lives in, and repeats until only one commit remains.

Marking a good commit and a bad commit

You start by telling git the boundaries. Run the start command, mark the current state as bad because the bug is present, then check out a commit you remember working and mark it good. Git now knows the bug entered somewhere in that window. It immediately checks out a commit near the middle and hands control back to you. The only requirement is honesty about those two endpoints. If the good commit was never actually good, the search points at the wrong place, so pick a boundary you can verify rather than one you merely hope was fine.

Testing each midpoint and reporting the result

At every midpoint git drops you onto a commit, and your job is to decide whether the bug is present. Build the project, run the failing scenario, or trigger whatever reproduces the problem, then tell git the verdict with a good or bad mark. Each answer removes half the remaining commits from consideration. The discipline that matters here is testing the same thing the same way every time. A flaky manual check that passes once and fails once will send the search down the wrong branch and waste the speed the tool just gave you.

Letting a script answer for you

The real power arrives when you hand bisect a test script instead of answering by hand. The run command takes any command and uses its exit code as the verdict, zero for good and a non-zero value for bad. Point it at a unit test, a build command, or a short script that reproduces the bug and exits with the right code, and git walks the entire search on its own. You come back to a named commit. This turns a fifteen minute interactive session into something you can start and ignore, which matters most on the long histories where bisect earns its keep.

Skipping commits that cannot be tested

Not every commit in a range is testable. Some will not build for unrelated reasons, some sit mid-refactor, and some predate the feature entirely. For these you use skip rather than good or bad. Git sets them aside and chooses a nearby commit instead, keeping the search honest without forcing a false verdict. If a whole cluster of commits refuses to build, that itself is a useful signal, and you can narrow the range manually before restarting. Skipping is also how you avoid poisoning an automated run with commits whose exit code would mean nothing.

Reading the result and cleaning up

When the search finishes, git prints the first bad commit with its hash, author and message. That is your answer: the change that introduced the bug, isolated from everything around it. Read that diff with full attention now, because it is the only one that matters. When you are done, run the reset command to leave bisect mode and return to the branch you started on. Forgetting that step leaves you on a detached commit, which confuses later work, so make the reset a reflex at the end of every bisect session.

Fitting bisect into everyday debugging

Bisect rewards a history of small, self-contained commits, because the smaller the guilty commit, the smaller the diff you end up reading. It pairs naturally with a reliable reproduction, so the effort you spend writing a short failing test up front pays back the moment you hand that test to the run command. Treat it as a first resort for any regression where you can name a commit that once worked. The habit of reaching for it early, rather than after an hour of manual log reading, is what quietly saves the most time across a project's life.

Frequently asked questions

Does git bisect change my code or history? No. It checks out existing commits so you can test them, and it never rewrites history. Running the reset command at the end returns you to the branch and state you began with.

What if the bug cannot be reproduced automatically? You can still bisect by answering good or bad by hand at each step. Automation only replaces that manual verdict with a script's exit code, so a careful manual check works just as well, if more slowly.

Can bisect find a performance regression, not just a crash? Yes, as long as your test script can decide good from bad. Write a check that measures the metric and exits non-zero past a threshold, and the search treats a slowdown exactly like any other failure.

A search worth reaching for first

Git bisect turns the vague dread of a regression into a short, structured hunt with a definite end. You name a good commit and a bad commit, answer a few questions or hand over a script, and the tool does the arithmetic that your intuition would only fumble. The payoff grows with the size of your history, which is the opposite of how manual debugging scales. Keep commits small, keep a reproduction handy, and let the binary search do the narrowing while you save your attention for the one diff that actually explains the bug.

KO
Kofi Oosterhuis

Kofi spent years keeping APIs and game servers alive through traffic spikes and the occasional bad deploy. He covers back-end design, scaling and networking, with a preference for boring systems that do not page anyone at night.

More posts by Kofi

More in Dev Tools

01doorganico 084goad 3hot8 4465456 after5internprogram ahandfulofblessings ahmadpc ahomcybersecurity allodialresources annodex antiquesandperiodcollectibles aotc asianworldkashmir beedlectrics bloggertemplatefree bluefeatheragency bodyweightsolution campaign08blog carfixadvisor catstreetcollective cbstvsports cnfanspurchasing committeeof500yearsofdignityandresistance covertopsdjs cs-kaiyuangame cuestacomunicaciontotal cxoutlet dancebetgame deskgearhub destinyvillastay detectmagazine di33 diamondsteelbuildings donmo dyanadropship emarsysfinance emimattl escapechan eskitarih evautoplay everythingweddings-orlando farmzone feedviewerapp foxstudiosscratch gamevui123 gleasongrade greetingsday hacklabasu haskell iw-kaiyun jagoanproperti jilidskripsi joltbusinessfinance juegodepokergratis juegospucca kaeru-festival latestmehndidesigns lestanzediottaviano litemagazines localcommunityhero lustrsazan mariashebets menswheels newwebstechs1 nikkit ootheday oxigenvpn plurfitness prefetial probudgetcarpetcleaning psychicmrsmartin pyaten qvantor rejoignezlogis rexzbot riselayouts ruchin rwxqsgd5 sakaltime shibiaowang shop-bot skrtips strcoding tetapatrium topselller uneplanteartificielle vipertechcr wo-kygaming yamaha-mekar yeknava yoybuysheets zenunwa yamaha-mekar jilidskripsi ankertoto ankertoto ankertoto ankertoto