DEV Community

Cover image for Let Claude Code Run git bisect in Its Own Worktree While You Keep Coding
Gabriel Anhaia
Gabriel Anhaia

Posted on AI-assisted

Let Claude Code Run git bisect in Its Own Worktree While You Keep Coding


Your CLI's format 1234.5 used to print 1,234.50. On main it prints 1234.5. The last release tag where it worked is 200 commits back, and you are halfway through a feature with a dirty working tree.

The classic answer is git bisect, and the classic cost is your checkout. Bisect checks out commit after commit in the folder you run it in. So you stash, bisect, rebuild, unstash, and lose the afternoon's train of thought.

You don't have to pay that. Give bisect its own git worktree, hand the boring part to Claude Code, and keep typing in your own folder. Every command below is plain git and runs on any repo. The last section shows the same job started from Hermes IDE, which I build.

Why bisect can live in a second folder

A worktree is an extra working folder attached to the same repository, with its own checked-out commit. The part that makes this safe is in the git-worktree manual: refs are shared between worktrees in general, "There are exceptions, however: refs inside refs/bisect, refs/worktree and refs/rewritten are not shared." HEAD is per-worktree too.

So a bisect session in a linked worktree has its own good and bad marks and its own detached HEAD. Your main checkout keeps its branch, its uncommitted changes and its build output. Both folders still share one object store, so nothing is cloned twice.

Bisect is a binary search. Each test halves the range, so 200 commits take about 8 tests (2^8 is 256), plus one more for each commit that can't be tested. Count your range first, using your own last good tag:

git rev-list --count v1.8.0..HEAD
Enter fullscreen mode Exit fullscreen mode

The repro script: 0, 1 and 125

git bisect run hands the whole search to a script. The git-bisect manual sets the contract: exit 0 if the commit is good, and "a code between 1 and 127 (inclusive), except 125" if it is bad. Exit 125 means the commit "cannot be tested" and gets skipped. "Any other exit code will abort the bisect process."

Two traps hide in that contract. Exit codes 126 and 127 (found but not executable, command not found) count as bad, so a typo in your script's path marks every commit bad. And a script whose last command was killed by a signal exits above 128, which aborts the run. The script below only ever exits 0, 1 or 125:

#!/bin/sh
# ~/bisect/repro.sh
# 0 = good, 1 = bad, 125 = this commit can't be tested
npm ci --silent >/dev/null 2>&1 || exit 125
npm run build --silent >/dev/null 2>&1 || exit 125
[ -f dist/cli.js ] || exit 125

out=$(node dist/cli.js format 1234.5 2>/dev/null)
if [ "$out" = "1,234.50" ]; then
  exit 0
fi
echo "got: '$out'" >&2
exit 1
Enter fullscreen mode Exit fullscreen mode

A commit that doesn't install or build exits 125 and is skipped, not blamed. Swap the three setup lines for your stack (make, cargo build, go build ./...) and the comparison for whatever shows your bug.

The script lives at ~/bisect/, outside the repository, on purpose. The manual says it is "safer" to keep bisect scripts outside the repo "to prevent interactions between the bisect, make and test processes and the scripts." A script inside the tree can vanish or change as bisect checks out old commits.

Before you trust it, run it at both ends. You'll do that in the next section, inside the bisect worktree, never in your own checkout.

Run the bisect in a throwaway worktree

Say your checkout is ~/code/app. From there, add a detached worktree next to it. -d (or --detach) creates it with a detached HEAD and no new branch:

git worktree add -d ../app-bisect main
cd ../app-bisect
Enter fullscreen mode Exit fullscreen mode

Now the sanity runs. If the good end doesn't exit 0, the bisect result means nothing:

git switch --detach v1.8.0
sh ~/bisect/repro.sh; echo "exit: $?"   # want 0
git switch --detach main
sh ~/bisect/repro.sh; echo "exit: $?"   # want 1
Enter fullscreen mode Exit fullscreen mode

Then start the search. The first commit is bad, the second is good, and -- ends the revisions:

git bisect start main v1.8.0 --
git bisect run sh ~/bisect/repro.sh
Enter fullscreen mode Exit fullscreen mode

When no revisions are left, the manual says git will "print out a description of the first bad commit," and refs/bisect/bad is left pointing at it, so git show refs/bisect/bad gives you the diff. If commits right next to the culprit were skipped, git can't name a single one. The manual is direct about it: "Git will be unable to tell exactly which of those commits was the first bad one."

A long row of commit beads with brackets closing in from both ends on one burnt-orange bead
Each test halves the range. About 8 tests cover 200 commits.

If your history has merged branches full of commits that never built, add --first-parent to git bisect start. Bisect then follows only the first parent of each merge, so the merge commit itself is blamed and the branch's broken commits are never tested. The manual describes this as "useful in avoiding false positives when a merged branch contained broken or non-buildable commits, but the merge itself was OK."

Your main checkout hasn't moved through any of this. Run git status there and you'll see the feature you were working on, untouched.

Hand the loop to Claude Code

Everything above is mechanical: write a script, check both ends, run, read the result. That is a good fit for a coding agent, as long as it works in its own worktree and you approve the commands. Start Claude Code in ../app-bisect and give it a task like this:

Find the commit that broke `node dist/cli.js format 1234.5`.
Expected output: 1,234.50. Known good: v1.8.0. Known bad: main.

1. Write ~/bisect/repro.sh, outside the repo. Exit 0 if good,
   1 if bad, 125 if the commit doesn't install or build.
   Never exit with any other code.
2. Run it at v1.8.0 and at main. Show me both exit codes.
   Stop if they aren't 0 and 1.
3. git bisect start main v1.8.0 --, then
   git bisect run sh ~/bisect/repro.sh
4. Save git bisect log to ~/bisect/bisect.log and show it.
   Explain the first bad commit's diff. Then git bisect reset.
Don't commit, and don't edit any tracked file.
Enter fullscreen mode Exit fullscreen mode

In Claude Code's Manual mode, Bash commands ask for approval except a built-in set of read-only ones, and file writes ask too. You'll approve the script, the sanity runs, git bisect start and git bisect run. The whole search is that one git bisect run call, so there is no prompt per commit.

You could pre-approve it with an allow rule such as Bash(git bisect *) in .claude/settings.local.json. Think before you do: git bisect run executes whatever command follows it, so that rule approves any script behind a git bisect run. For a one-off hunt, approving the few prompts by hand is cheap.

A long search won't block the session either. Claude Code's Bash tool defaults to a two-minute timeout, and when a foreground command hits its timeout, Claude Code "moves it to the background instead of stopping it," per the tools reference. In a local terminal session, background commands have no time limit, so an npm ci per commit is fine.

Start it from Hermes with ⌘N

I build Hermes IDE, so weigh this section with that in mind. It is free, source-available (BSL 1.1), runs on macOS, Windows and Linux, and isn't affiliated with Anthropic or any agent vendor. The current release is 2.3.3, from October 13, 2026.

Hermes does the worktree step for you. Press ⌘N (Ctrl+Shift+N on Windows and Linux), pick Claude Code, paste the task above and press Enter. The README describes what happens: the task starts "on its own hermes/<name> branch in its own worktree, with the task as the agent's first prompt." Claude Code runs in a real terminal with its own interface and your own sign-in. When bisect detaches HEAD in that worktree, the branch it left is the task branch.

Two separate workbenches on one shared tree trunk: a robot arm sorting commit beads on one, a person typing at a laptop on the other
The agent bisects in its own worktree. Your feature stays in yours.

While it runs, you go back to your feature. When Claude stops to ask you to approve git bisect run, its session shows "needs approval", and ⌘I (Ctrl+Shift+I) jumps to the agent that has waited longest so you can answer in Claude's own terminal; Hermes never answers a permission prompt for you. On macOS and Linux, agents keep working while Hermes quits, updates or crashes, so a long bisect survives an app update.

When the culprit is found, the same session can write the fix and a regression test on its hermes/ branch. ⌘G opens the Review Desk, where you read the changes by turn or by file before you land them.

Read the log, then leave nothing behind

If you ran the bisect by hand, keep the record before you reset (the agent task above already saves it). git bisect log prints every good, bad and skip mark the session made. Save it, and the manual's replay command can rebuild the same state later, for example after you remove a wrongly marked entry from the file:

git bisect log > ~/bisect/bisect.log
git bisect reset
# later, if you need the session back:
# git bisect replay ~/bisect/bisect.log
Enter fullscreen mode Exit fullscreen mode

git bisect reset clears the bisect state and, per the manual, returns the tree "to the commit that was checked out before git bisect start." In the plain-git setup, remove the worktree from your main checkout and delete the script. Keep bisect.log as long as you might want to replay it:

cd ~/code/app
git worktree remove ../app-bisect
rm ~/bisect/repro.sh
git worktree list
Enter fullscreen mode Exit fullscreen mode

git worktree remove refuses a worktree with modified or untracked files. If it refuses, run git status in that folder to see why before you reach for --force. Afterwards git worktree list no longer shows app-bisect. With plain git, there's no stash to pop and no branch to delete.


If this was useful

A bisect run is a small, clean agent job: a strict contract (0, 1, 125), one long command, and a result you can check with git show. Download Hermes IDE to start it with ⌘N, and if it saves you an afternoon, star it on GitHub. Every step Claude took here was a tool call you approved: write a file, run a command, read the output, decide what comes next. If you want to build that loop yourself, AI That Acts walks through tool calling and functions in TypeScript until you have a first working agent.

AI That Acts — AI in TypeScript

It is book 3 of AI in TypeScript, five books from your first LLM call to agents in production: the whole series.

Top comments (0)