What you need to know
Coding agents got good enough in 2025–26 that the bottleneck moved: the limiting factor is no longer how fast one agent works, but how many an engineer can supervise at once. The naive way to run two agents — two terminal tabs in the same checkout — fails immediately, because both agents edit the same files, fight over the same git index and trip over each other's half-finished changes. The fix operates at the filesystem level, and Git has shipped it natively since version 2.5 in July 2015: git worktree.
A worktree is an additional working directory attached to the same repository. Each worktree has its own checked-out files, its own HEAD and its own staging area, while sharing one object database, one set of branches and one config with the main checkout. Creating one takes seconds and almost no disk. Put one agent in each worktree, each on its own branch, and the agents cannot touch each other's work — no stashing, no collisions, no corrupted index. As of August 2026, several agent vendors recommend this one-agent-per-worktree pattern in their own documentation, and teams commonly run three or four trees per developer.
Two scoping notes before the commands. First, this guide is about filesystem-level isolation: separate directories, separate branches, one human merging the results. If what you want is one agent session delegating to subagents inside a single context, that is a different technique with different trade-offs — covered in multi-agent orchestration with Claude Code subagents. Second, everything here is tool-agnostic: the layout works identically whether the process in each tree is Claude Code, Cursor's CLI, Copilot CLI or anything else that edits files, because the isolation lives in git, not in the agent.
Prerequisites
You need surprisingly little:
- Git 2.5 or later. Any machine imaged in the last decade qualifies; check with
git --version. Worktrees have been stable for the whole of that period — the GitHub blog announcement dates to July 2015. - A repository with a test suite the agents can run. Parallel agents multiply output; without an automated pass/fail signal in each tree, they multiply unverified output.
- Disk headroom for dependencies, not history. The git side of a worktree is nearly free because objects are shared. What costs disk is the per-tree
node_modules, virtualenv or build cache — budget roughly one dependency install per tree (a typical TypeScript service runs 400–600 MB each). - Task splits that do not overlap. The whole approach depends on agents working in different corners of the codebase. The section on when not to parallelise covers the failure cases.
- A conventions file the agents will inherit. Your
AGENTS.mdorCLAUDE.mdis versioned, so every worktree gets a copy at checkout — one more reason to write it properly (the patterns that actually steer agents).
Worktree fundamentals: one repository, many working trees
The core commands, verified against the official git-worktree documentation. Create a worktree as a sibling of the main checkout, on a fresh branch:
# from inside ~/code/acme-api (the main checkout)
git worktree add ../acme-api-payments -b agent/payments
# equivalent long form: new branch agent/payments from HEAD,
# checked out at ../acme-api-payments
If you omit -b, git creates a branch named after the last path component (here acme-api-payments) — convenient for throwaway experiments, but for agent work an explicit agent/ prefix keeps branches greppable and lets CI treat them differently. To base the tree on something other than HEAD, name it: git worktree add ../acme-api-hotfix -b hotfix/rate-limit origin/main.
See what exists, and clean up:
git worktree list
# /Users/rishi/code/acme-api 83f2e1a [main]
# /Users/rishi/code/acme-api-payments 9c4d7b2 [agent/payments]
git worktree remove ../acme-api-payments # refuses if the tree is dirty
git worktree remove -f ../acme-api-payments # removes even with local changes
git worktree prune # forget trees whose directories were deleted manually
Three behaviours matter for agent work. First, git worktree remove refuses to delete a tree containing modifications or untracked files unless you force it — a safety net when an agent has uncommitted work you have not reviewed. Second, prune does the opposite job people expect: it never deletes directories, it only removes the stale metadata left behind when a directory was deleted by hand (or by a cleanup script gone wrong). Third, branches outlive their worktrees — removing a tree does not delete its branch, which is exactly what you want between "agent finished" and "PR merged".
How does this compare with the other ways to isolate agents on a filesystem?
| Approach | Isolation | Disk cost | Setup time | Where it fits |
|---|---|---|---|---|
| Worktrees | Files, branch, index — full; history and refs shared | Checkout + dependencies only | Seconds | The default for parallel agents on one machine |
| Separate clones | Everything, including objects and config | Full history per clone | Minutes on large repos | Different remotes or credentials per agent; otherwise pure overhead — branches need pushing to be seen across clones |
| Branches in one tree | None while working — one directory, one index | Zero extra | Zero | One agent at a time only; two agents here is the failure mode this article exists to prevent |
| Containers / microVMs | Files plus process, network and credential isolation | Image + volume per agent | Minutes, plus image maintenance | Untrusted or autonomous agents that run arbitrary commands; complements worktrees rather than replacing them (sandboxing guide) |
The honest summary: worktrees isolate files, not processes. An agent in a worktree can still read your environment and open network connections. For supervised sessions on your own machine that is normally acceptable; for autonomous agents, put the worktree inside a sandbox and get both.
Setting up an agent-per-worktree layout
A layout convention worth adopting: keep worktrees as siblings of the main checkout, named <repo>-<task>, and give every tree its own copy of the environment. Sibling directories (rather than nesting trees inside the repo) keep file watchers, editors and the agents themselves from indexing each other's trees.
~/code/
acme-api/ # main — yours; never give this tree to an agent
acme-api-payments/ # agent 1 · branch agent/payments
acme-api-tests/ # agent 2 · branch agent/test-backfill
acme-api-docs/ # agent 3 · branch agent/openapi-docs
Two things do not come along automatically when a worktree is created, because git only checks out tracked files:
- Dependencies.
node_modules,.venvand friends are gitignored and directory-local. Install fresh in each tree (npm ci,uv sync,bundle install). Do not symlink a sharednode_modulesacross trees: native binaries carry absolute paths, and the first time two agents install different versions of anything, both trees break at once. - Environment files.
.envis gitignored, so a new tree starts without one. Copy it from the main tree — then edit the values that collide. Ports are the big one: two dev servers on 4000, or two test suites binding the same Postgres port, produce exactly the confusing half-failures agents are worst at diagnosing. A fixed offset per tree (main on 4000, trees on 4010/4020/4030) ends the problem permanently. Databases deserve the same treatment: either a schema per tree or a database name derived from the branch.
Automate the whole thing with a small script, because you will do it several times a day:
#!/usr/bin/env bash
# new-agent-tree.sh <task> <port-offset> — e.g. ./new-agent-tree.sh payments 10
set -euo pipefail
TASK="$1"
OFFSET="${2:-10}"
ROOT="$(git rev-parse --show-toplevel)"
NAME="$(basename "$ROOT")"
TREE="$ROOT/../$NAME-$TASK"
git -C "$ROOT" worktree add "$TREE" -b "agent/$TASK"
# per-tree environment: copy .env, bump the port
BASE=4000
if [ -f "$ROOT/.env" ]; then
cp "$ROOT/.env" "$TREE/.env"
BASE="$(grep -E '^PORT=' "$TREE/.env" | cut -d= -f2)"
BASE="${BASE:-4000}"
sed -i.bak "s/^PORT=.*/PORT=$((BASE + OFFSET))/" "$TREE/.env" && rm "$TREE/.env.bak"
fi
# per-tree dependencies
( cd "$TREE" && npm ci )
echo "ready: $TREE branch agent/$TASK port $((BASE + OFFSET))"
Then start one agent per tree, each in its own terminal, each with a brief that names its lane:
cd ../acme-api-payments && claude # or: cursor-agent, copilot, aider …
cd ../acme-api-tests && claude
cd ../acme-api-docs && claude
Put the lane boundaries in the agent's first prompt, not just in your head: "You are working only on the payments provider integration under src/payments/. Do not modify migrations, package.json or shared types — if you believe you need to, stop and say so." Agents respect explicit fences far more reliably than implied ones, and the stop-and-say-so clause converts a future merge conflict into a one-line question.
Orchestrating agents and surviving the review bottleneck
Here is the uncomfortable arithmetic of parallel agents: generation scales linearly with worktrees, review does not scale at all. You are the merge queue. Every branch an agent finishes must be read, tested and integrated by the same single human, and reviewing agent output is slower per line than reviewing your own. Teams that ignore this end up with six impressive branches, zero merged, and a rebase war. Three disciplines keep the queue moving.
Small branches, merged continuously. Brief each agent towards the smallest shippable slice — hours of work, not days. A 300-line branch merges the same afternoon; a 3,000-line branch waits, rots and conflicts. Merge finished work immediately rather than batching "integration day" at the end of the week: every merge shrinks the surface the remaining branches can conflict with.
Frequent rebases in every tree. After each merge to main, bring the surviving branches forward so conflicts surface while they are one commit old:
# inside each still-active worktree, after something merges
git fetch origin
git rebase origin/main # resolve now, while the conflict is small
npm test # re-verify on the new base before the agent continues
Because all worktrees share one object database, the fetch is instant from every tree — the objects are already local once any tree has them. Have the agent itself run the rebase and re-run the tests; fixing its own conflicts on a fresh base is usually within a 2026 agent's competence — verify the result before continuing, and it keeps you in the reviewer seat rather than the mechanic seat.
An explicit review order. Review the riskiest branch first while your attention is best, and sequence merges so cheap ones land early: docs and tests first (near-zero conflict surface), isolated features next, anything touching shared files last. Cap work-in-progress the way you would for a human team — if three branches are waiting on review, do not brief a fourth agent; drain the queue. If your pipeline includes AI reviewers, tune them before multiplying their input volume by four (AI code review gates that cut noise), and if you are rolling this out beyond yourself, the organisational half of the problem — policy, permissions, metrics — is a separate playbook: rolling out coding agents across a team.
Cleanup deserves automation too, or dead trees accumulate until the day git worktree list scrolls. This reaper removes any worktree whose branch has fully merged:
#!/usr/bin/env bash
# reap-merged-trees.sh — remove worktrees whose branches are merged into origin/main
set -euo pipefail
git fetch origin --prune
MAIN="$(git rev-parse --show-toplevel)"
git worktree list --porcelain | awk '/^worktree /{print $2}' | while read -r TREE; do
[ "$TREE" = "$MAIN" ] && continue
BRANCH="$(git -C "$TREE" branch --show-current)"
[ -z "$BRANCH" ] && continue # detached HEAD — leave it alone
if git merge-base --is-ancestor "$BRANCH" origin/main; then
git worktree remove "$TREE" && git branch -d "$BRANCH"
echo "reaped: $TREE ($BRANCH)"
fi
done
git worktree prune
Note what the script does not do: it never forces. A tree with uncommitted changes makes git worktree remove fail loudly, which is the correct outcome — unreviewed agent work should require a human decision to destroy.
Every article here is written by a Verified Builder. Want your name on the next one?
AI Tech Connect lists AI engineers, founders and researchers across India and the UK — and the people hiring browse it to find them. Adding your profile is free.
Become a Verified Builder →When not to parallelise
Worktrees isolate directories; they cannot isolate tasks that are inherently sequential. Recognising those before briefing the agents is the difference between four clean merges and an afternoon of conflict archaeology.
- Database migrations. The canonical trap. Most frameworks — Django, Rails, Prisma, Alembic — order migrations by timestamp or sequence, and the ordering is global to the repository. Two agents generating migrations in parallel produce two "next" migrations that conflict on number, on the schema snapshot, or both. Route every schema change through a single designated tree, and fence the others off from the migrations directory in their briefs.
- Dependency and lockfile churn.
package.jsonpluspackage-lock.json(orpnpm-lock.yaml,uv.lock,Cargo.lock) is one shared, machine-generated file pair that conflicts on almost any concurrent edit — and lockfile conflicts are miserable to resolve by hand. Give dependency work to exactly one agent, and tell the rest not to install anything. - Tightly coupled refactors. Renaming a shared interface, changing an API contract and its client together, touching generated code — if two tasks would edit the same module, they are one task. Run them sequentially in one tree.
- Work that shares mutable external state. Two trees pointed at one development database, one Redis, one S3 bucket will interfere in ways git never sees. Either split the state per tree or do not split the work. (The failure patterns are the same races that bite multi-agent systems generally — concurrency bugs in multi-agent systems.)
The subtlest version of this failure is the agent that "helpfully" strays out of lane — the docs agent that bumps a dependency to fix a build warning, the test agent that edits source to make a test pass. Both create exactly the cross-tree conflicts the layout was designed to prevent. Explicit do-not-touch lists in each brief, plus a CI gate on protected paths, catch it early.
Pitfalls that waste an afternoon
The same-branch-twice error. Sooner or later you will see fatal: 'agent/payments' is already checked out at '…/acme-api-payments'. This is not a bug — git refuses to check one branch out in two trees because both would share a single branch ref, and a commit in either tree would silently strand the other. Do not reach for --force. Run git worktree list, find where the branch lives, and either work there, remove that tree, or create a fresh branch name. If the directory was deleted manually, git worktree prune clears the stale registration that is causing the refusal.
Detached-HEAD confusion. Create a tree with a commit-ish but no -b flag (git worktree add ../scratch origin/main) and you get a detached HEAD — the tree is not on any branch, and an agent can commit hours of work onto it that no ref points to. Some agent harnesses create detached trees deliberately for throwaway runs; fine, as long as it is deliberate. Habit fix: always pass -b for agent trees, and make your scripts check git branch --show-current prints something non-empty before starting an agent.
The .git file gotcha. In a linked worktree, .git is a plain file containing gitdir: /path/to/main/.git/worktrees/<name>, not a directory. Scripts and tools that assume a .git directory — hand-rolled deploy scripts, some older watchers and Docker volume configurations — misbehave quietly. The portable fix is git rev-parse --git-dir (and --git-common-dir for the shared side) instead of hard-coded paths. Related: if you move a tree by hand rather than with git worktree move, the linkage breaks — git worktree repair reconnects it.
Hooks are shared — mostly. Hooks live in the shared git directory, so your pre-commit hook runs in every tree — but hook managers (husky, pre-commit) often need their install step run per-tree because they depend on the tree's own dependencies. If commits from a fresh worktree bypass checks that work in the main tree, run the hook installer inside that tree. Worktree-specific configuration, where you genuinely need it, is git config extensions.worktreeConfig true followed by git config --worktree <key> <value>.
Disk creep. Git objects are shared, but four trees times 500 MB of node_modules is 2 GB that du will eventually surface. The reaper script above is the fix; running it at the end of each day keeps the sibling directory honest.
Case study: scaling one agent to four
A concrete run, condensed from one team's real week and anonymised. The repository is a ~90k-line TypeScript payments API — the shape of codebase you would find at a fintech in Bengaluru or a London scale-up alike. The sprint goal: integrate a new payment provider, and pay down a backlog of test debt, stale OpenAPI docs and overdue dependency bumps. One engineer, one afternoon of setup, four days of execution.
The split. Four tasks, chosen precisely because their file footprints barely intersect:
| Tree | Branch | Task | Fenced off from |
|---|---|---|---|
acme-api-provider | agent/provider-integration | New provider under src/payments/providers/ | Migrations, package.json, shared types |
acme-api-tests | agent/test-backfill | Unit tests for the refunds module | All of src/ except test files |
acme-api-docs | agent/openapi-docs | Regenerate and correct the OpenAPI spec | Everything outside docs/ and route annotations |
acme-api-deps | agent/dep-bumps | All dependency updates — the only tree allowed to touch the lockfile | Feature code |
The rhythm. Day one: scripts create the four trees (about ten minutes each including npm ci; ports 4010–4040), agents briefed with lane fences, all four running by mid-morning. The engineer's day becomes a rotation: twenty to thirty minutes per tree — read the diff so far, run the tests, answer the agent's questions, redirect where needed. Docs and tests finish first and merge on day two, exactly as the review-order discipline predicts. After each merge, the surviving trees rebase onto origin/main and re-run their suites before continuing. The dependency branch lands day three — its only conflict was with a test file, one commit old thanks to the rebase cadence, resolved by the agent itself in minutes. The provider integration, the genuinely hard branch, gets the deepest review and merges on day four.
The honest accounting. Four branches merged in four days, an estimated seven to eight working days of sequential effort compressed into four — not the 4x of the marketing copy, because review time is irreducible and roughly 40% of the engineer's week went on it. The near-miss is instructive too: the test agent wanted to "fix" a refunds edge case it had exposed, which would have collided head-on with the provider branch. The stop-and-say-so clause in its brief turned that into a question instead of a conflict; the fix was queued as a follow-up task in the same tree after the provider branch merged. That is the pattern working as designed — and the honest ceiling it implies: worktrees remove the filesystem as a constraint, so the constraint becomes you. Three or four supervised agents is where most experienced practitioners settle, and past that, adding trees just relocates work from the agents' queue to yours.
Start with two on your next backlog-clearing day — one feature tree, one tests-or-docs tree — and let the merge queue teach you your own number. The commands are seven lines of muscle memory; the discipline is the product.