git bisect --term-old/--skip: Custom Bisect in CI
git bisect skip excludes untestable commits from the search, and --term-old/--term-new rename good/bad when "good" is the wrong word (e.g. performance).
A full automated bisect in CI needs three things: a way to mark untestable commits (skip), optionally custom terms when good/bad do not fit, and a test script for git bisect run. This page covers the parts beyond the basic good/bad flow.
What it does
git bisect performs a binary search between a known-bad and known-good commit. skip removes a commit that cannot be tested (broken build, missing dep) so git picks a nearby one. git bisect start --term-old=fast --term-new=slow lets you label commits with words that match the change you are hunting.
Common usage
git bisect start
git bisect bad HEAD
git bisect good v1.4.0
# can't build this one
git bisect skip
# fully automated: exit 0 = good, 1-127 (except 125) = bad, 125 = skip
git bisect run ./run-test.sh
git bisect resetOptions
| Command / flag | What it does |
|---|---|
| bisect start | Begin a bisect session |
| bisect good / bad <rev> | Mark a known-good or known-bad commit |
| bisect skip [<rev>...] | Exclude commits that cannot be tested |
| bisect run <cmd> | Automate: cmd exit 125 means skip, 0 good, else bad |
| --term-old / --term-new | Rename good/bad to custom terms |
| bisect reset | End the session and return to the original HEAD |
In CI
A shallow clone has too few commits to bisect; run git fetch --unshallow first or clone with full history (fetch-depth: 0). In git bisect run, reserve exit code 125 for "skip" so a flaky build step does not get misread as a real failure. Always finish with git bisect reset so the next job starts on a clean HEAD.
Common errors in CI
"You need to start by "git bisect start"" means a prior run left no session, or it was reset. "git bisect run failed: exit code 125 ... too many" means every candidate is being skipped, often because the test script cannot build the older commits. "fatal: Bad rev input" means the good ref is not present locally (shallow clone).
Using this in CI
CI checkouts are shallow and detached by default, which changes the answer this command gives you. Commands that read history, branch names, or tags need the checkout configured for it.
- uses: actions/checkout@v4
with:
fetch-depth: 0 # history, tags, and git describe all need this
- run: |
git rev-parse --is-shallow-repository # expect false
git rev-parse --abbrev-ref HEAD # prints HEAD when detached