AI coding

Using Git Worktrees to Isolate Agent Changes

Antonio Evans9 min read

A top-down view of a filesystem tree drawn as a gentle graph.
Key points

Key points

  1. 1A git worktree gives each agent its own checked-out directory while sharing one object database, so parallel runs cannot overwrite each other's files.
  2. 2Agents left in a shared checkout collide over the same utility files, and those unrequested edits are exactly the ones that overlap.
  3. 3Naming each worktree after the task or agent and putting them in a shared parent directory makes review, cleanup and rollback predictable.
  4. 4A diff budget and a pre-edit scope check close the gap that isolation alone cannot: a clean diff that still touches the wrong files.

Why one checkout per agent stops working

A single working directory is fine for one person with one task. It stops being fine the moment two agents edit at once. Git can check out a given branch in only one working tree at a time, and a shared checkout means both agents write into the same set of files, the same index and the same uncommitted state. Worktrees exist precisely because that constraint is real: each has its own working directory while sharing the repository's history (Nx on worktrees for agents).

The collision is predictable once you look at what agents actually change. The task itself is rarely the conflict. It's the drive-by cleanup that overlaps. One practitioner who runs parallel implementation agents describes it precisely: two agents never fight over the task they were assigned, they fight over the shared utility file both decided to tidy up (the diff budget write-up).

There is a second, quieter cost: review. A stored git diff is the natural inspection surface for agent work, and a small, file-scoped change is quick to verify. When an agent rewrites half a function to fix a one-line bug, the diff becomes enormous even though the output is functionally correct. That is the over-editing problem, and it is invisible to test suites because the tests still pass (the minimal-editing analysis). Isolation does not fix over-editing by itself, but it makes each agent's output separable, which is what lets you review and compare diffs at all.

What a worktree actually is

A worktree is an extra working directory attached to the same repository. The official documentation describes it as a way to have more than one working tree attached to a single repository, each checked out to a different branch, all sharing the same object store and refs (git-worktree docs). Nothing is duplicated: commits made in any worktree are immediately visible to the others, and you merge between them exactly as you would merge branches (Nx).

That sharing is the point and the trap. Sharing the object database keeps disk use low and keeps history consistent. But two worktrees cannot check out the same branch at once. If your main checkout has feature/x checked out, git worktree add ../wt-x feature/x fails. Each agent needs its own branch, not just its own directory.

A minimal layout for three parallel agents looks like this:

parent-dir/
├─ repo/                 <<< main checkout, stays on your working branch
├─ wt-agent-1/           <<< worktree, branch feature/agent-1
├─ wt-agent-2/           <<< worktree, branch feature/agent-2
└─ wt-agent-3/           <<< worktree, branch feature/agent-3

Everything lives under one parent directory so a single ls .. shows the whole fleet, and cleanup is one command per directory.

The mechanics: fetch, add, name

Start from a known state. Before adding anything, fetch and confirm the main checkout is clean, because a worktree branched from a stale or dirty base inherits the confusion. The Espressif agent-coding workshop is blunt about this: check git status and git diff, understand or commit existing changes first, and never let a new task get mixed with unfinished work (the ESP-IDF workflow guide).

cd repo
git fetch origin
git status            # must be clean enough to reason about
git worktree list     # see what already exists

Then create one worktree per agent, branching from the freshly fetched base:

git worktree add ../wt-agent-1 -b feature/agent-1 origin/main
git worktree add ../wt-agent-2 -b feature/agent-2 origin/main
git worktree add ../wt-agent-3 -b feature/agent-3 origin/main

Three details matter here. First, -b creates the new branch in the same step; if the branch already exists locally, drop -b and pass the existing name. Second, basing on origin/main after a fetch means you do not need your local main to be up to date first. Third, --no-track keeps the new branch from tracking origin/main, which is what you want when each agent branch will get its own upstream on first push (socketbox worktree setup notes).

If you are branching from an existing remote branch rather than main, the same command shape works; the older Stack Overflow question on this is still the clearest reference for the error messages you will hit (Stack Overflow).

Naming that survives the review

Name the branch after the task, and the directory after the agent or the task ID. The Espressif guide uses short prefixes (feat/, fix/, refactor/, step/) that make the intent readable in git log and in a PR list (ESP-IDF workflow). The wt/ CLI follows the same idea with standardized folder names of {reponame}-{branch-name} (Nx). Pick one convention and write it down, because the whole review loop depends on mapping a directory back to a task in one glance.

ConventionWorktree directoryBranchUse when
By task ID../wt-0412fix/0412-paginationTracker tickets, many agents
By agent../wt-agent-afeature/agent-aA/B experiments on one task
By author+task../alice-wt-0412fix/0412-paginationShared machines, human + agent
Repo-prefixed../repo-feature-xfeature/xMany repositories on one machine

Where the worktree lives and how agents behave in it

Put worktrees as siblings of the repository, not nested inside it. A nested worktree under repo/ gets picked up by file watchers, glob searches and language servers, and the agent may start editing files in a directory it did not intend to touch. Keeping them as siblings means each agent's editor, build cache and dependencies are genuinely independent, which also removes the need to reinstall packages when switching branches (Nx).

Give each agent an explicit working directory when you start it, and say so in the prompt. Something like "you are working in ../wt-agent-1, branch feature/agent-1, base origin/main" is enough. An agent that does not know its root will happily cd into the main checkout and edit there, and the isolation silently disappears.

For teams that want a cleaner root, the bare-clone pattern keeps a hidden .bare directory holding all history, with every branch as its own sibling folder:

mkdir project && cd project
git clone --bare [email protected]:org/repo.git .bare
echo "gitdir: ./.bare" > .git
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
git fetch origin
git worktree add main

That fetch refspec is the step people miss. A bare clone only fetches the default branch by default, so adding a worktree for an existing remote branch silently creates an empty branch from main instead (Git worktree like a boss). The alternative, git init --bare plus a remote, writes the correct refspec automatically and leaves no local branch until you create a worktree for it (socketbox).

Because every worktree writes to the same repository, a machine can accumulate many short-lived branches as agents come and go. The same scripting that creates them should also prune them, or the branch list becomes noise. Tooling that hooks into the Git workflow to record what each agent session did, such as the Entire CLI, is useful here, but it complements the worktree rather than replacing it.

Reviewing and merging across worktrees

Because all worktrees share history, review is ordinary Git. Inside any worktree you can diff against the base, see the branch log, and compare two agent branches against each other:

cd ../wt-agent-1
git diff origin/main...HEAD        # full change for this agent
git diff --stat origin/main...HEAD # size at a glance

cd ../repo
git log --oneline main..feature/agent-1
git log --oneline main..feature/agent-2
git diff feature/agent-1 feature/agent-2   # compare two agents directly

To merge the winner, check out the base branch in the main checkout and merge the branch, exactly as the Nx walkthrough describes, then remove the losing worktrees:

cd repo
git switch main
git merge --no-ff feature/agent-1
git worktree remove ../wt-agent-1
git worktree remove ../wt-agent-2
git worktree remove ../wt-agent-3
git branch -d feature/agent-2 feature/agent-3
git worktree prune

Two cautions. git worktree remove refuses to delete a worktree with uncommitted changes or untracked files; that refusal is a feature, and you should investigate before forcing it. And stale administrative entries can linger after a directory is deleted manually, which is what git worktree prune cleans up. Keeping the list tidy is a documented best practice worth automating with aliases such as git worktrees and git prunetrees (ChristopherA's worktree notes).

Isolation is not scope control

A clean worktree guarantees that agent A cannot see agent B's uncommitted files. It guarantees nothing about what agent A does inside its own directory. An agent told to fix an off-by-one can still rename variables, convert a callback to async, and reformat the file, all within its private worktree. The diff is isolated but still bloated.

The decision rule that works in practice is a two-layer fence. Layer one is the worktree, which handles concurrent writers. Layer two is a per-task diff budget: allowed paths, a maximum file count and a maximum changed-line count, declared when the task is created. A pre-edit hook blocks writes outside the allowed paths and feeds the reason back to the model, and a post-run check enforces the size budget. Instructions describe intent; a mechanism enforces it (the diff budget write-up).

A ready-made plugin exists for that second layer if you do not want to write hooks, though the underlying idea is the same and worth understanding before installing it (scope-guard).

LayerControlsToolingFailure it catches
Worktree + branchConcurrent writers, shared indexgit worktree add, git worktree listTwo agents editing one file, lost uncommitted work
Path scopeWhich files an edit may touchPre-edit hook, allowed-path globsEdits to unrelated modules and config
Size budgetHow large the change may getPost-run diff checkDrive-by refactors, reformatting, added helpers
ReviewWhether the change is rightgit diff, git log, PRCorrectness, design, hidden scope

The budget numbers are a judgment call, not a science. The published example uses tight limits for bug fixes and wide ones for planned refactors, with refactors tracked as their own task type rather than smuggled into a fix (diff budget write-up). For a typical bug fix, a cap of a handful of files and under a hundred changed lines keeps review to about a minute.

It also helps to tell the agent, in its instructions, what surgical means in concrete terms: touch only what you must, match existing style, do not refactor things that are not broken, and mention unrelated dead code instead of deleting it. The widely shared Karpathy Guidelines skill states the test plainly: every changed line should trace directly to the user's request (skill source). A skill like that loads only when a request matches it, so it costs nothing on unrelated turns (TypingMind listing).

A worked example, with numbers

Suppose you want a fix to a pagination helper, and you are running three agents to compare approaches. The sequence, with realistic bookkeeping:

  1. Fetch the base and confirm the main checkout is clean. Record the base commit SHA so every review diff uses the same anchor.
  2. Create three worktrees, each on its own branch from origin/main: wt-0412-a, wt-0412-b, wt-0412-c.
  3. Give each agent the same task, its working directory and a budget of 4 files and 80 changed lines.
  4. Let all three run in parallel. Each worktree carries its own dependencies, build cache and test runner, so nothing contends for a lock.
  5. Review with git diff --stat origin/main...HEAD in each directory. If one agent's diff touches twelve files, the budget check has failed and you review that reasoning first, not the code.
  6. Merge the branch whose fix is smallest and correct, then remove the other worktrees and their branches.

The payoff is measurable in review attention, not in model quality. A five-line fix takes a minute to verify; a twelve-file diff where the real fix is buried in the middle takes real attention, and attention is the resource you cannot generate (diff budget write-up).

What this changes for a team

Three operational shifts follow from moving to worktrees for agents.

First, the base branch becomes a shared contract. Every agent branches from a fetched origin/main, so a merge of one agent's work does not silently invalidate another's assumptions until the next fetch. Rebase or re-fetch before merging.

Second, rollback gets cheap. If an agent's fix is wrong, you revert the fix without dragging an unrelated refactor with it, because the refactor was never in that worktree in the first place (diff budget write-up).

Third, the boundary between temporary working state and durable history needs a policy. Agents make lots of changes while investigating, and only the final state usually matters for review; if every probe becomes a permanent commit, the branch history becomes hard to read. The Espressif guide recommends committing after each coherent, verified state instead of waiting until the end of a large task, because small commits make the agent's work easier to review, easier to compare between steps, and easier to recover from (ESP-IDF workflow).

Finally, the quality of the work still starts upstream of the worktree. Teams that split product design from technical design and write a spec the agent and the human both read report fewer surprises during implementation, because verification moves earlier and code review becomes easier rather than larger (Boundary and HumanLayer discussion). A worktree gives each agent a clean room; a good spec gives it something worth building in that room.

When you are running several agents across more than one model, wiring each worktree's agent to a single endpoint keeps the plumbing simple: Quanta Inference serves text, image, video, audio and embedding models through one OpenAI-compatible key, billed prepaid in US dollars, so an experiment can swap models per worktree without changing the harness.

FAQ

FAQ

Can two worktrees check out the same branch?

No. Git allows each branch to be checked out in only one working tree at a time, so a worktree added for an already-checked-out branch fails. Give each agent its own branch with -b, or check out an existing branch that no other working tree holds (git-worktree docs).

Does a worktree duplicate the repository and waste disk space?

It duplicates only the working files, not the history. All worktrees share one object database, so commits, refs and objects are stored once, which is why it is cheaper than cloning the repository repeatedly (git-worktree docs).

How do I add a worktree for a branch that exists only on the remote?

Fetch first, then run git worktree add ../dir branch-name so Git finds the remote-tracking branch and creates a local tracking branch, or base a new branch on it with git worktree add ../dir -b new-branch origin/branch-name (Stack Overflow). In a bare-clone setup, set the fetch refspec first or the branch will appear empty (Git worktree like a boss).

Why does `git worktree remove` refuse to delete a directory?

It refuses when the worktree has uncommitted changes or untracked files, as a safety check against losing work. Inspect the diff, commit or discard deliberately, then remove; use git worktree prune for stale entries whose directories are already gone (ChristopherA's notes).

Does a worktree stop an agent from editing unrelated files?

No. Isolation confines an agent to its own directory and branch, but within that directory it can still over-edit. Add a per-task path scope and a diff size budget, enforced by a pre-edit hook and a post-run check, if you need a guarantee rather than an instruction (diff budget write-up).

Sources

Sources

  1. How Git Worktrees Changed My AI Agent Workflow | Nx Blog
  2. How I Stopped My AI Coding Agent's Scope Creep With a Diff Budget
  3. Coding Models Are Doing Too Much - wh
  4. Git - git-worktree Documentation
  5. AI agent coding for ESP-IDF workshop - Optional: Git workflow for agent ...
  6. Set up a local repository in which every branch is checked ...
  7. Git add a worktree from existing remote branch
  8. Git worktree like a boss
  9. GitHub - entireio/cli: Entire CLI hooks into your Git workflow to capture AI ...
  10. Git Worktree Best Practices and Tools
  11. GitHub - atoolz/scope-guard: Stop AI scope creep. A Claude Code plugin ...
  12. skills - karpathy-guidelines - GitHub
  13. Karpathy Guidelines by multica-ai, AI Skill for Any LLM | TypingMind
  14. Why Your AI Coding Agent Keeps Writing Bad Code (It's Not the Prompt)
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles