Find the change that introduced a bug

tovio bisect: a binary search over history, manual or automated.

Written By Dustin

Last updated About 3 hours ago

tovio bisect start --bad main --good v1.0.0
tovio bisect good
tovio bisect bad
tovio bisect skip
tovio bisect status
tovio bisect reset

A stateful binary search between a known-good and a known-bad point. Each probe is materialized into your working copy so you can test it; good, bad and skip take the current probe by default, or an explicit revision. A candidate that is unbuildable because it carries conflicts is skipped automatically.

Automate it

tovio bisect start --bad main --good v1.0.0 --run cargo test
tovio bisect run cargo test

The test command follows git's exit convention: 0 means good, 125 means skip, anything else means bad. An exit code of 128 or above (or a signal) aborts the run with TVO-OP-027 and leaves the session intact so you can continue by hand.

Narrow it with the symbol graph

tovio bisect start --bad main --good v1.0.0 --symbol parse_header
tovio bisect start --bad main --good v1.0.0 --symbol parse_header --only

--symbol annotates each probe with whether it touched that symbol — advisory, and free. --only turns the annotation into a filter: only commits that changed the symbol are tested. That is much faster, but it answers a narrower question — "which change to this symbol broke it" — and cannot catch a regression introduced anywhere else. A bare name resolves through the symbol graph, so foo matches foo(), the type foo, or Type#foo().

Driving the checkout yourself

tovio bisect start --bad main --good v1.0.0 --no-checkout

Leaves the working copy alone and just tells you which commit to test — useful when you are testing against a build server rather than your desk.

It is local and it advances nothing

A bisect session is never synced and moves no lane. Ending it is tovio bisect reset (not undo), which restores the working copy to the start point, or to an explicit revision you name.

What can go wrong

  • TVO-OP-023 — a good commit is not an ancestor of bad, so the range is undefined. Pick a good point that is actually behind the bad one.
  • TVO-OP-024 — no bisect is running. Start one first.
  • TVO-OP-025 — both bounds must be set before a candidate can be chosen.
  • TVO-OP-026 — every remaining candidate was skipped or is unbuildable, so the first bad change cannot be decided. Un-skip one you can test.