← Back to blog2026-06-20

Why every AI coding agent needs its own git worktree

Point two agents at one checkout and they corrupt each other. Here's the fix — and how to make it automatic.

The problem

I build with AI coding agents — often four Claude Code or Codex sessions at once, each spawning sub-agents. For a while they all worked in one checkout. That is the trap.

A git working directory has one HEAD: one branch, one index, one set of files on disk. Run several agents in it and they aren't collaborating — they're overwriting the same physical files with no lock. Three real incidents:

  • One agent git switches mid-build. Another agent's cargo was compiling and read torn source — half the old branch, half the new — and failed with phantom errors pointing at code nobody had touched.
  • Two agents edit the same files. One was mid-rewrite; the other started on the same file; they nearly stashed each other's uncommitted work away.
  • A session lived in the shared main tree with uncommitted work. Others merged, so main advanced underfoot; git switch main was blocked because a branch can only be checked out in one place; and an un-mergeable .pbxproj blew up in conflict.

Different branches don't save you — the part people miss. A branch is only a label in shared history; a working directory can still check out just one branch at a time. So two agents sharing one directory must keep git switch-ing it back and forth, and that switch is the corrupting move. (Uncommitted work isn't on any branch at all — it's just files in the shared directory.) The fix has to be on disk: a second set of files.

That's a worktree: git worktree add checks a branch out into its own directory, with its own HEAD and index, over the one shared repo. Each agent gets private files; they physically can't collide. A branch in its own directory is a worktree — and that, not the branch, is what saves you.

✗ The trap one checkout, many agents agentagentagent one checkout (main) one HEAD · one set of files git switch mid-build feeds another agent's build torn source; uncommitted work gets clobbered. ✓ The fix one worktree per agent agentagentagent worktreeworktreeworktree one shared repo (.git) shared history · private files

Same agents, two layouts: one shared checkout (they collide) vs one worktree each (isolated).

How to set it up

You don't have to touch git — or a terminal at all. You describe the change in your editor or app — "add a raise slider; do it in its own worktree and merge back when it's green" — and the agent runs the git for you (you just approve the command). Both Claude Code and Codex are agents that execute shell, so creating the worktree is something they do, not you. And with the hooks below, you don't even have to say "in a worktree."

Claude Code — first-class support

Claude Code has built-in worktrees: ask for one (or, on the CLI, launch with the --worktree flag) and it runs git worktree add off your default branch and works there; merging back is still an instruction you give when it's green — the built-in never merges on its own. A sub-agent given isolation: worktree gets its own throwaway worktree, auto-removed if it ends with no changes. You stay in plain language the whole time.

Codex — same idea, mind the sandbox

Codex has no built-in worktree feature, but it's a shell-running agent too: ask it the same way and it runs git worktree add itself. The one real catch is the sandbox. Codex's workspace-write mode can write only inside its primary workspace — the directory you start it in (or pass with -C) — plus any extra writable roots. So if you launch Codex in the main repo, an out-of-tree worktree like ~/.codex/worktrees/<repo>/<name> (where this repo's tooling puts them) isn't writable by default. Three fixes: start Codex in the worktree (codex -C ~/.codex/worktrees/<repo>/<name>), so it becomes the workspace; or keep the main repo as the workspace and add the worktree with --add-dir <that dir>; or, for a layout you reuse, list the parent under [sandbox_workspace_write].writable_roots in ~/.codex/config.toml. A worktree placed under the primary workspace needs none of this.

The new problem: making every session and sub-agent follow the rule

So far a worktree gets made only when you — or the agent — remember to ask. Across dozens of sessions and sub-agents, "remember" fails. The fix is to make it structural with hooks, so a worktree is the default path rather than a thing to remember:

  • A SessionStart hook: a session that opens in the shared main tree gets a reminder to spin up a worktree before editing.
  • A PreToolUse hook: hard-blocks the edit tools (Edit/Write/MultiEdit) when the target is in the main tree — so an agent's edits to main are refused, pushing it onto a worktree. (Override: ALLOW_MAIN_EDIT=1.)
  • Commit/merge gates (a pre-commit hook plus a Bash PreToolUse hook) keep work off main except through the sanctioned merge.

The hooks gate the normal edit/commit/merge path; two wrapper scripts make the rest one step — one creates the worktree (linking .env, assigning deterministic alt ports clear of the main stack's :3001/:5173), one does the --no-ff merge and removes it, refusing if the tree is dirty.

How it works

There's one object database and one set of refs under the main repo's .git. Each linked worktree gets a small admin dir at .git/worktrees/<name>/ holding its own HEAD and index; its working directory holds a .git file (a one-line pointer back), not a directory.

StatePer-worktreeShared
HEAD, index, working files, in-progress merge/rebase
Objects, refs (branches/tags), config, hooks, stash

That split is the mechanism — and why a branch and a worktree aren't interchangeable: a worktree is a private slot on disk where one label becomes real files. Because each worktree owns its HEAD and files, two can sit on different branches at once; because they share objects and refs, a commit in one is instantly visible in the others. Two gotchas: the stash is repo-global (a stash pushed in one shows up in all), and the same branch can't be checked out twice — by default git refuses, and rejects any operation that would update a branch checked out elsewhere. Disk cost is the working set, not history: each worktree duplicates the checked-out files and build artifacts (target/, node_modules/), so N worktrees ≈ N× working set.

The honest limits

  • Worktrees don't prevent merge conflicts. Isolation isn't coordination: two worktrees editing the same lines still conflict at merge — the worktree just moves the collision from a silent, live corruption to an explicit merge you resolve on purpose. Keep branches small and short-lived, integrate often.
  • Disk and rebuild time. A cold cargo build / npm install per worktree is real gigabytes and minutes.
  • Local config is per-worktree. A fresh worktree has none of your gitignored setup; link it in. (The server binary reads env from the shell, not .env; cargo test needs DATABASE_URL, build/check don't.)
  • Cleanup discipline. Finish = merge, then git worktree remove — not rm -rf (which leaves a stale entry) — then git worktree prune.

Practical tips

  • Keep the main tree idle on a clean main as the merge target.
  • Give each worktree its own alt-port dev stack and its own node_modules — don't symlink to main (Vite's fs.allow 403s fonts served from outside the worktree).
  • Enforce with hooks, not willpower. The insight that shaped ours: a background sub-agent once merged to main against an explicit "do not merge," and there is no env var that tells a sub-agent from the main thread — so the gate must be default-deny, not a trust check.

Recap

  1. The problem: one checkout + many agents = corruption (torn builds, clobbered uncommitted work). Different branches don't fix it — a branch is just a name in shared history; the bug is on disk.
  2. The fix: a worktree = a branch checked out into its own directory (its own HEAD and files) over one shared repo — physical isolation, no collisions.
  3. How to use it: don't type git — describe the task and the agent makes its own worktree. The hook that matters isn't "protect main" (that's standard) — it's the one that makes a worktree the only workable path for every new session and sub-agent: nudged on entering the main tree, refused if it edits main — the session still creates the worktree, but can't skip it.
  4. The caveat: worktrees remove the working-tree race, not merge conflicts — you keep those small by keeping branches small and merging often.