🔧 The Problem
If you're running more than one AI coding agent at a time — Claude Code in one terminal, Aider in another, maybe a Cursor background agent chewing on a refactor — you've probably hit the same wall: they all want to work on the same repo, but they can't share a working directory without stepping on each other.
The usual workarounds are all bad:
-
git stashjuggling — you stash, switch branches, let the agent work, unstash, repeat. Fine for one agent. A nightmare for three running concurrently, because stash is a single shared stack and agents don't know how to negotiate over it. -
Cloning the repo N times — works, but now you've got N full copies of
.git, N sets of dependencies to install, and N places for config drift to sneak in. On a large monorepo this is also just slow. - One giant branch with agents committing to subdirectories — merge conflicts waiting to happen, and agents lose the ability to see a clean diff of just their own work.
What you actually want is N independent working directories, backed by one .git, so history, remotes, and object storage stay unified while the checked-out files stay isolated. That's exactly what git worktree gives you, and it's been sitting in Git core since version 2.5 (2015), mostly ignored until "run four agents at once" became a normal Tuesday.
🌳 Worktrees in Practice
The core workflow is boring in the best way:
bash
from your main checkout
git worktree add ../myapp-agent-a feature/agent-a
git worktree add ../myapp-agent-b feature/agent-b
git worktree add ../myapp-agent-c fix/flaky-test
see what's active
git worktree list
/home/dev/myapp abcd123 [main]
/home/dev/myapp-agent-a ef01234 [feature/agent-a]
/home/dev/myapp-agent-b 5678aaa [feature/agent-b]
/home/dev/myapp-agent-c 9911bbb [fix/flaky-test]
Each directory is a real, complete checkout — you can cd into it, run tests, open it in an editor, point an agent at it — and none of them affect each other's index or working tree. Behind the scenes they all share .git/objects, so you're not duplicating blobs, and any commit made in one worktree is immediately visible to git log in the others (once you fetch/checkout).
Cleaning up is just as direct:
bash
git worktree remove ../myapp-agent-c
or, if the agent left the directory dirty and you don't care:
git worktree remove --force ../myapp-agent-c
For an agent-driven workflow, I wrap this in a small script so I'm not hand-typing branch names every time I spin one up:
bash
!/usr/bin/env bash
spawn-agent.sh
set -euo pipefail
task="$1"
branch="agent/${task}"
worktree_path="../$(basename "$(pwd)")-${task}"
git worktree add -b "$branch" "$worktree_path" main
cd "$worktree_path"
per-worktree setup so agents don't fight over node_modules etc.
cp ../.env.example .env
npm install --prefer-offline
echo "Worktree ready at $worktree_path on branch $branch"
Now "give the agent a sandbox" is a single command, and tearing it down after review/merge is another single command. No stash stack, no second clone, no confusion about which branch is checked out where.
⚠️ The Gotchas Nobody Mentions
Worktrees are not a free lunch, and the failure modes are exactly the kind of thing that eats an afternoon if you don't know to look for them.
Shared package manager caches can lie to you. node_modules, .venv, and build caches are not shared between worktrees by default — each one needs its own install. If your agents are installing dependencies in parallel across worktrees pointed at the same global npm/pip cache, you can get lock contention or, worse, a half-written cache entry that silently corrupts a build in a different worktree. Pin a per-worktree cache directory if you're running installs concurrently:
bash
npm install --cache "$(pwd)/.npm-cache"
IDE indexing goes haywire. VS Code, JetBrains IDEs, and language servers built with a single-repo assumption will happily index every worktree directory you open as if it's an unrelated project — which is technically correct but means you're running 4x the TypeScript server memory, 4x the file watchers, and sometimes 4x the "go to definition" confusion if symlinks or path aliases assume a fixed repo root. If you're not actively reading code in a worktree, don't leave it open in the IDE — close the window when the agent is just running headless.
Branches can't be checked out twice. This one bites people immediately: Git will refuse to let two worktrees point at the same branch.
fatal: 'feature/agent-a' is already checked out at '/home/dev/myapp-agent-a'
This is a feature, not a bug — it's the mechanism that prevents two agents from independently committing to the same branch and creating divergent history in two places at once. But it does mean your orchestration script needs a real branch-per-agent naming scheme, not "reuse main for everything."
Detached HEAD surprises. If an agent (or you) checks out a commit instead of a branch, you get a detached HEAD in that worktree — harmless, but if the agent then commits and you forget to create a branch before removing the worktree, git worktree remove will happily let you lose those commits to garbage collection. Always git branch tmp-recovery before tearing down a detached-HEAD worktree you're unsure about.
Submodules and .git hooks need extra care. Hooks live in the shared .git directory by default (or .git/worktrees/<name> for some internals), so a hook that assumes $(pwd) is the repo root can misbehave across worktrees. If you use submodules, git worktree add doesn't initialize them for you — add --recurse-submodules or run git submodule update --init explicitly per worktree.
🚀 Putting It Together
The pattern that's worked well for me running 3-4 agents concurrently:
- One "orchestrator" checkout (your normal working directory) that never runs an agent directly — it's just for review and merging.
- One worktree per active task, named after the task, not the agent.
- A teardown step that force-removes the worktree and deletes the branch once merged, so
git worktree listdoesn't slowly fill up with zombie sandboxes.
bash
git worktree remove --force ../myapp-agent-a
git branch -d agent/task-a # or -D if the agent's commits got squashed on merge
It's not a glamorous feature — worktrees have existed for a decade specifically for things like hotfix-while-mid-feature workflows — but it maps almost perfectly onto "isolated sandbox per autonomous process" once you swap the human for an agent. The shared object store keeps disk usage sane, and the isolated working trees keep agents from corrupting each other's in-progress edits.
How are you isolating your parallel agents right now — worktrees, containers, or something else entirely? And if you've hit a worktree gotcha that isn't on this list, I'd genuinely like to hear about it in the comments.
Top comments (0)