Published:

Tagged: AI-Agents Git Worktrees Claude-Code Agentic-Coding Tools Opinion

Run parallel Claude Code agents without the conflicts

When running several Claude Code instances against one repository, I assumed the hard part would be getting good code out of them. It wasn’t. The hard part was much more mundane: they kept tripping over each other in the same files. The fix was a git feature I’d been using for years without thinking about it.

You could run several Claude Code instances against one repo at once, each on its own task, in parallel. Point two of them at the same checkout, though, and it breaks fast: one commits while the other is mid-edit, git refuses the second write with an index.lock error, and a branch switch in one changes the files under the other.

The fix ships with git and sits one command away: git worktree. That collision isn’t a sign that parallel agents are a mistake, but rather the oldest lesson in concurrency turning up again: run more than one thing at once and you have to draw a boundary around the shared mutable state. Here the shared state is the working tree, and git has been holding a boundary for it all along.

A git worktree is the coordination primitive that makes parallel agents work: one tree per instance, one branch per tree, parallelism by construction.

One tree per instance

A git worktree is a second working directory on the same repository. You’ve been using one all along; git worktree add just lets you create more, each on its own branch.

The split is the whole point: linked worktrees share the object store, refs/, and config through one .git, while each owns its own HEAD, index, and working files. That private index is the boundary: every agent locks its own, so the index.lock collision never happens, yet a commit in one tree is instantly visible in all the others. Isolated edits, shared history.

# assuming a current working tree at /tmp/project-1/main
> cd /tmp/project-1/main

Finder column view of the starting repository: a single working tree at project-1/main holding a .git folder and index.md, with no worktrees yet

# create one worktree per agent, each on its own fresh branch
> git worktree add ../agent-feature -b feature/x
> git worktree add ../app-billing -b feat/billing

Each add creates a directory, branches with -b, and leaves the original tree untouched; git worktree list shows every tree and its branch, the closest thing to a dashboard for a fleet:

# the coordination dashboard
> git worktree list

/private/tmp/project-1/main           7d3f317 [main]
/private/tmp/project-1/agent-feature  7d3f317 [feature/x]
/private/tmp/project-1/app-billing    7d3f317 [feat/billing]

Finder column view showing three sibling worktrees under project-1: main, agent-feature, and app-billing, each its own working directory backed by the same repository

But unlike a clone, which copies the whole object store, a worktree shares it and adds only a working directory, so it’s cheap to spin up, cheap to throw away, and there’s no second repository to keep in sync.

One branch per tree, enforced for you

Try to check out the same branch in two live worktrees and git stops you: fatal: '<branch>' is already used by worktree at '<path>'. That reads like a limitation, but it’s the headline feature: a branch lives in only one worktree at a time, so git enforces the decomposition for free.

One agent, one tree, one branch, with no way to accidentally point two at the same branch. So partition deliberately: one agent on billing, one on auth, one reviewing an existing branch instead of writing a new one.

# check out an existing branch for a review-only agent
> git worktree add ../review-pr existing-branch

# a read-only inspection worker that owns no branch
> git worktree add --detach ../inspect HEAD

Use --detach for a read-only agent: it checks out a detached HEAD with no branch, so the worker can read, analyse, and run while owning nothing it could push.

Also don’t --force a shared branch across live worktrees; that just puts you back at two agents fighting over one target, which is the contention worktrees exist to remove.

Pinning a Claude Code instance to its tree

Claude Code isolates by working directory. From Anthropic’s docs: “edits in one session never touch files in another.” Pin each instance to its own worktree and that guarantee is yours by construction.

The manual path is the one you’d expect: make the tree, change into it, launch.

# manual: create the tree, cd in, launch
> git worktree add ../project-feature-a -b feature-a
> cd ../project-feature-a && claude

Claude Code also ships a flag so you don’t have to wire it up by hand. claude --worktree feature-auth creates a worktree under .claude/worktrees/feature-auth/, on a new branch worktree-feature-auth, and starts Claude inside it. Omit the name and it generates one. The short form is -w.

> claude --worktree feature-auth     # tree at .claude/worktrees/feature-auth/, branch worktree-feature-auth
> claude --worktree bugfix-123       # a second isolated session in another terminal
> claude --worktree "#1234"          # branch from a PR -> .claude/worktrees/pr-1234

Open a second terminal, run claude --worktree bugfix-123, and you have two sessions that can’t touch each other’s files. Pass a PR reference like "#1234" and Claude Code fetches the PR into its own tree at .claude/worktrees/pr-1234, ready to review or extend.

Non-interactive worktrees for headless agents

The shape that matters most for a fleet is the headless one. claude -p runs in print mode, non-interactively, and skips the trust prompt, which is exactly what you want for a worker nobody is sitting in front of. Combine it with --worktree and each worker gets its own isolated tree with no human in the loop.

# headless worker, one task, its own tree
> claude -p --worktree feature-x "implement the feature"

# headless worker reading its task from a file
> claude -p "implement the change described in TASK.md and run the tests"

One caveat to file away now: worktrees created with --worktree alongside -p aren’t auto-cleaned, because there’s no interactive exit to prompt you. Remove them yourself with git worktree remove when the worker is done.

Code isolation is not runtime isolation

Worktrees give you code isolation, not runtime isolation. What stays shared is the host:

There’s a deeper ceiling too: the shared .git. Worktrees remove the working-tree and index collision, but ref and object writes still funnel through one .git. With isolation: worktree and thirteen agents committing at once, one reported incident (GitHub issue #55724) saw five succeed and eight fail on lock contention. Take those as the reporter’s figures, not a benchmark; the fix is operational, retry with backoff and jitter, not abandoning worktrees.

Some of this has a native fix. Drop a .worktreeinclude file at the project root, in .gitignore syntax, and Claude Code copies the gitignored files it lists, such as .env, into every new worktree. That clears the empty-environment problem, but it won’t carry an installed node_modules, so the install step still belongs in each worker’s setup.

Use worktrees when agents just need to edit code in parallel. Reach for containers only when you need real isolation: separate ports, a database per agent, or a sandbox for untrusted code.

The setup that does not bite back

The housekeeping is light: name your trees so ownership is obvious, and treat git worktree list as the dashboard. When something goes sideways, list the trees and see who owns what.

One gotcha cost me real time on Slink, my Cloudflare Workers link tracker: worktree paths with spaces break the workerd runtime, and symlinks don’t save you. So keep any Workers worktree on a space-free path.

# keep Workers worktrees off paths with spaces
> git worktree add ~/worktrees/app-auth -b feat/auth

# tear down cleanly when the work is merged
> git worktree remove ../agent-feature

# clear bookkeeping after a directory was deleted by hand
> git worktree prune

Tear down with git worktree remove, not rm; if you delete a tree by hand, run git worktree prune to clear the stale bookkeeping. And when remove refuses because the tree has uncommitted changes, that’s a safety feature, not an obstacle.

Two limits to know, both in the git-worktree man page’s BUGS section: multiple checkout is still experimental, and it’s not recommended for a superproject, so keep submodule-heavy repos off this pattern.

The real question was never how many agents you can launch, but where they run. Once every agent has its own working tree and its own branch, git stops being the thing they fight over. The worktree was the missing primitive, and parallelism stops being something you fight git for and becomes something git does for you.