Start two coding agents in one checkout and they share a scratchpad: agent A's half-written file is agent B's input, and whoever runs git switch last wins. A Claude worktree — Claude Code's --worktree flag, or any git worktree — fixes the files. It does not fix the .env that didn't come along, the two dev servers that both want port 3000, or the test runner that now finds your suite four times.

I ran every git command below in a scratch repo on git 2.55.0 and kept the output. Tool details are from Claude Code 2.1.287, Codex CLI 0.160.0, the Codex source at that tag, and Cursor's docs, all checked in October 2026.

The short answer: Give each agent its own git worktree: claude --worktree <name> in Claude Code, codex --worktree in the Codex CLI, agent --worktree <name> in Cursor's CLI, or the Worktree option in each desktop app. A worktree is a separate directory and branch over one shared .git, so agents can't overwrite each other's files. It doesn't copy gitignored files and doesn't separate ports, databases or Docker Compose projects. Copy .env with a .worktreeinclude file (Claude Code, Codex app) or .cursor/worktrees.json (Cursor), give each worktree its own PORT, DATABASE_URL and COMPOSE_PROJECT_NAME, and merge back locally — branches are shared, so no push is needed.

Claude Code, Codex and Cursor Worktrees Compared

All three tools create worktrees for you now.

Claude CodeCodex CLICodex appCursor
Start oneclaude -w <name>codex --worktree, or /worktreeWorktree, under the composerAgents Window, /worktree in the IDE, agent -w <name>
Where it lands.claude/worktrees/<name>, inside the repo~/.codex/worktrees/<4 hex>/<repo>same root, changeable in Settingsunder ~/.cursor/worktrees, fixed (CLI: <repo>/<name>)
Starts fromremote default branch (baseRef: "fresh")your current HEAD committhe branch you pick, plus its uncommitted changesnot stated in the docs
Branchnew worktree-<name>none (detached HEAD)none until "Create branch here"not stated in the docs
Gitignored files.worktreeincludenot copied.worktreeincludesetup-worktree commands
Automatic cleanupon exit; subagent worktrees by a periodic sweepneverkeeps the newest 15keeps the newest 25 per machine

Sources: Claude Code worktree docs, Codex worktree docs, the Codex CLI's worktree crate at rust-v0.160.0, Cursor worktree docs and Cursor CLI docs.

Two rows bite people. Claude Code starts from the remote, so your unpushed commits aren't there; Codex starts from local HEAD with no branch, so you name one before you push. If you're still choosing the agent itself, Codex vs Claude Code covers that; this guide is for running whichever you picked side by side.

Claude Worktree Setup in Claude Code

claude --worktree <name> (short form -w) creates a Claude Code git worktree at .claude/worktrees/<name>/ on a new branch worktree-<name> and starts the session there. Run it again with another name in a second terminal for the second agent. The flag arrived in Claude Code 2.1.49 per the Claude Code changelog, a version published on February 19, 2026 (GitHub release v2.1.49).

bash
claude --worktree feature-auth     # omit the name and Claude picks one, e.g. bright-running-fox
claude -w feature-auth --tmux      # same, in its own tmux session (iTerm2 panes when available)
claude --worktree "#1234"          # check out PR 1234 into .claude/worktrees/pr-1234

Expect three surprises on the first day.

Your unpushed commits are missing. The default worktree.baseRef is "fresh": branch from the remote's default branch. To start from your local HEAD instead, put this in settings.json (the setting arrived in 2.1.133; it takes fresh or head, not a branch name):

json
{ "worktree": { "baseRef": "head" } }

Your .env is missing. Add a .worktreeinclude file at the repo root, in .gitignore syntax. Claude Code copies a file only when it matches a line here and is also gitignored, so tracked files are never duplicated:

text
.env
.env.local
config/secrets.json

Your test runner finds every worktree. .claude/worktrees/ lives inside your repo. The docs tell you to gitignore it, which keeps it out of git status but not out of Vitest — Vitest doesn't read .gitignore. One project's Vitest exclusion PR measured 2,888 test files collected against 374 real ones, with full runs dropping from 400–570 s to 94 s after the fix. Another project's ESLint fix PR traced 244 "No tsconfigRootDir was set" lint errors to ESLint loading each worktree's config. Exclude the folder explicitly:

ts
// vitest.config.ts
import { configDefaults, defineConfig } from 'vitest/config';

export default defineConfig({
	test: { exclude: [...configDefaults.exclude, '**/.claude/worktrees/**'] }
});

In eslint.config.js, add { ignores: ['.claude/worktrees/**'] }. Spread configDefaults.exclude rather than replacing it, or you drop Vitest's own node_modules exclusion.

On exit, Claude Code removes a clean, unnamed worktree on its own and asks about a named or dirty one; keeping it prints a claude --worktree <name> --resume command for coming back. Headless claude -p --worktree runs get no prompt: their worktrees stay on disk, locked, so you git worktree unlock before git worktree remove. Subagents can get the same isolation with isolation: worktree in their frontmatter. And if the repo uses Git LFS installed with --local, a fresh worktree holds pointer files until you run git lfs pull inside it.

Think twice before putting hand-made worktrees in .claude/worktrees/. Claude Code issue #89199 reported in August 2026 that the retention sweep deleted two git worktree add checkouts there, about 13 GB of state. The changelog lists the fix in 2.1.246 (GitHub release v2.1.246, August 25, 2026), though the issue is still open. On 2.1.246 or later the sweep should leave your own worktrees alone; on anything older, keep them in another directory.

Codex Worktree: the CLI Flag and the App

The Codex CLI got --worktree and the /worktree command as an experiment in 0.154.0 on September 9, 2026 (release notes) and switched them on by default in 0.156.0 on September 22 (release notes). In 0.160.0:

bash
codex --worktree          # new managed worktree, session starts inside it
codex exec --worktree "fix the flaky date test"   # same for a headless run

From the CLI's worktree source, it runs git worktree add --detach at your current HEAD commit, so uncommitted edits stay behind and there's no branch. Create one inside the worktree with git switch -c <name> before you commit and push. Per the worktree settings source, the CLI shares the app's worktree root but never cleans up automatically. OpenAI's docs describe .worktreeinclude for the app's managed worktrees, and I found no copy step in the CLI's worktree code, so bring your .env yourself (the script below does).

In the app, pick Worktree under the composer and choose a starting branch. Codex's docs cover the rest:

  • It carries the branch's uncommitted changes into the new checkout and works in detached HEAD. Create branch here turns it into a branch; Hand off moves the chat to your local checkout.
  • It reads .worktreeinclude, skips symlinks, never overwrites an existing file, and copies an ignored AGENTS.override.md without being told.
  • It keeps your 15 most recent managed worktrees, snapshots one before deleting it, and never deletes pinned, running or permanent ones. A permanent worktree (project three-dot menu) becomes its own project.
  • The root moves under Settings > Worktrees > Worktree root.

One trap is specific to Codex. The checkout directory is named after your repo — .../a1b2/myapp — and Docker Compose names a project after its directory. That makes a Codex worktree the same Compose project as your main checkout; the per-worktree .env section below fixes it.

Cursor Worktree Setup: Agents Window, /worktree and worktrees.json

Cursor creates worktrees from three places. The Agents Window does it natively. In the IDE you type /worktree <task>, /best-of-n sonnet,gpt,composer <task> to race models in separate worktrees, then /apply-worktree or /delete-worktree. The CLI takes -w:

bash
agent --workspace ~/src/my-app --worktree auth-fix "fix the flaky auth test and open a PR"

Worktrees land under ~/.cursor/worktrees; for CLI checkouts the docs give the layout <repo>/<name>. There's no setting to move it: a May 2026 reply in a Cursor forum thread says the path is still hardcoded and suggests symlinking the directory elsewhere.

Setup runs from .cursor/worktrees.json, read from the worktree first and then the project root. Commands run in the new worktree, and $ROOT_WORKTREE_PATH points at your main checkout:

json
{
	"setup-worktree": ["npm ci", "cp $ROOT_WORKTREE_PATH/.env .env", "npm run db:migrate"]
}

setup-worktree-unix and setup-worktree-windows override the generic key per OS, and either can name a script file instead of an array. When setup fails, open the Output panel and pick Worktrees Setup.

Cleanup is machine-wide: by default Cursor keeps the newest 25 worktrees across all workspaces (cursor.worktreeMaxCount), checking on the interval set by cursor.worktreeCleanupIntervalHours (the docs' example uses 6; no default is stated). Anything inside its root counts, including checkouts you add there with git worktree add, so keep your own elsewhere. Cursor's docs also advise against symlinking dependencies into a worktree; they recommend a fast package manager such as pnpm, bun or uv.

What a Worktree Shares, and What Your Agent Won't Find There

A worktree shares the repository — commits, branches, stash, config — and gets its own HEAD, index and checked-out files. Nothing gitignored comes along, and nothing on your machine gets split: ports, database servers and the Docker daemon are still one of each.

Here is a fresh worktree in a repo whose .gitignore lists .env and node_modules/:

console
$ git worktree add .worktrees/feature-a -b feature-a
Preparing worktree (new branch 'feature-a')
HEAD is now at b23c955 init
$ ls -A .worktrees/feature-a
.git
.gitignore
app.js
$ git -C .worktrees/feature-a rev-parse --path-format=relative --git-dir
../../.git/worktrees/feature-a

No .env. And the worktree's git directory lives inside the main checkout's .git (its .git is a one-line file pointing there); that is why sandboxed agents fail to commit, covered under errors.

In a new worktreeWhat happens
Tracked filesFresh checkout of the start commit
Branches and commitsShared: a commit in one worktree is visible from all of them
StashShared: a stash made in feature-a appears in feature-b's list
HEAD and indexPer worktree
.env, node_modules, build outputAbsent until you copy or install them
Your uncommitted edits in mainAbsent (git, Claude Code, Codex CLI); the Codex app copies them
Ports, DB server, Compose projectSame machine, same names: they collide unless you change them

The stash row surprises people. From my run:

console
$ git stash list
stash@{0}: On feature-a: stash made in feature-a
$ git -C .worktrees/feature-b stash list
stash@{0}: On feature-a: stash made in feature-a

The git-worktree docs spell out the rule: refs under refs/ are shared, pseudo-refs like HEAD are per worktree.

Give Each Worktree Its Own .env, Port, Database and Compose Project

Copy the ignored files in, then rewrite three values so nothing on the machine collides. This script does both for any agent. It reads the same .worktreeinclude file Claude Code and the Codex app use, and puts worktrees in a sibling directory, outside the repo, so test runners and linters never see them:

bash
#!/usr/bin/env bash
# wt-new <name> [base]: a sibling worktree with its own .env, port, database and Compose project
set -euo pipefail
name=$1 base=${2:-HEAD}
root=$(git rev-parse --show-toplevel)
git -C "$root" check-ignore -q .env || { echo "wt-new: add .env to .gitignore first" >&2; exit 1; }
dir="$(dirname "$root")/$(basename "$root").worktrees/$name"
git -C "$root" worktree add "$dir" -b "$name" "$base"

# Copy gitignored files that match .worktreeinclude (same file Claude Code and the Codex app read)
if [ -f "$root/.worktreeinclude" ]; then
  git -C "$root" ls-files -z --others --ignored --exclude-from=.worktreeinclude |
    while IFS= read -r -d '' f; do
      git -C "$root" check-ignore -q -- "$f" || continue
      mkdir -p "$dir/$(dirname "$f")" && cp -p "$root/$f" "$dir/$f"
    done
fi

# Lowest port no other worktree's .env has claimed
taken=$(git -C "$root" worktree list --porcelain | sed -n 's/^worktree //p' |
  while IFS= read -r wt; do sed -n 's/^PORT=//p' "$wt/.env" 2>/dev/null || true; done)
port=3001
while printf '%s\n' $taken | grep -qx "$port"; do port=$((port + 1)); done

slug=$(printf %s "$name" | tr -c 'a-zA-Z0-9' '_' | tr 'A-Z' 'a-z')
env="$dir/.env"
touch "$env"
db=$(sed -n 's/^DATABASE_URL=//p' "$env")
{
  grep -vE '^(PORT|DATABASE_URL|COMPOSE_PROJECT_NAME)=' "$env" || true
  echo "PORT=$port"
  if [ -n "$db" ]; then echo "DATABASE_URL=${db}_$slug"; fi
  echo "COMPOSE_PROJECT_NAME=$(basename "$root" | tr 'A-Z' 'a-z')_$slug"
} > "$env.tmp" && mv "$env.tmp" "$env"
echo "../$(basename "$root").worktrees/$name"
cat "$env"

Save it as wt-new on your PATH and chmod +x it. It's a bash script; I ran it on macOS. In a repo whose main .env holds PORT=3000 and DATABASE_URL=postgres://localhost:5432/myapp:

console
$ wt-new feature-a
Preparing worktree (new branch 'feature-a')
HEAD is now at c2879c5 compose + worktreeinclude
../myapp.worktrees/feature-a
PORT=3001
DATABASE_URL=postgres://localhost:5432/myapp_feature_a
COMPOSE_PROJECT_NAME=myapp_feature_a
$ wt-new feature-b
Preparing worktree (new branch 'feature-b')
HEAD is now at c2879c5 compose + worktreeinclude
../myapp.worktrees/feature-b
PORT=3002
DATABASE_URL=postgres://localhost:5432/myapp_feature_b
COMPOSE_PROJECT_NAME=myapp_feature_b

Then start any agent inside it. A hand-made git worktree needs nothing special from Claude Code — cd ../myapp.worktrees/feature-a && claude — and the same goes for codex and agent. Octomind is ours and has no worktree flag either: started there with octomind run -n feature-a, it loads that checkout's AGENTS.md and keeps a named session you can resume.

The Compose line matters more than it looks. Compose names a project after the directory holding the Compose file unless something overrides it (Docker Compose project-name docs), and it reads COMPOSE_PROJECT_NAME from a .env in the working directory (Compose environment-variable docs). I checked what each layout resolves to with docker compose config (Compose 5.1.2), against a file publishing ${PORT:-3000}:80:

CheckoutCompose projectHost port
Main checkout myapp/myapp3000
wt-new feature-amyapp_feature_a3001
wt-new feature-bmyapp_feature_b3002
Codex-style path …/a1b2/myapp, no .envmyapp3000
Claude-style .claude/worktrees/feature-cfeature-c3000

The Codex row is the dangerous one: same project name as your main checkout, so docker compose down -v there would remove the main checkout's containers and volumes. The Claude row gets its own project but still fights for port 3000.

The script stops at naming the database. Create it and run migrations as part of setup — Cursor's own example puts npm run db:migrate in worktrees.json — and point any service that hardcodes a port at $PORT. It assumes DATABASE_URL ends in the database name with no query string, and it only avoids ports other worktrees claimed, not ports some unrelated process holds.

node_modules: Install Per Worktree, Share the Disk

Install dependencies in every worktree; don't symlink node_modules between them, since a dependency change on one branch then changes it for every agent. With a warm cache the install is cheap. The disk isn't.

I installed the same seven dev dependencies (vite, vitest, typescript, eslint, typescript-eslint, react, react-dom — 100 top-level packages) into two worktrees with each package manager:

npm 10.9.7, npm cipnpm 12.8.1, hardlink import
Install time per worktree, warm cache0.7 s0.9 s
node_modules in the first worktree87 MB92 MB
Extra disk for the second worktree87 MB0.24 MB

npm writes a full copy per worktree. pnpm links every worktree to one content-addressable store (pnpm docs), so du counted the second worktree's files once. At Cursor's default cap of 25 worktrees, npm's copies of this small toolchain alone would take about 2.1 GB.

On macOS, pnpm's default import method tries clone before hardlink (pnpm settings reference); copy-on-write clones also share blocks, but du can't show it, which is why I forced hardlinks to measure. For a large monorepo, Claude Code's worktree.sparsePaths setting, added in 2.1.76 per the Claude Code changelog, checks out only the directories you list.

Worktree Errors You'll Hit, and the Fix for Each

Every message below came from my scratch repo on git 2.55.0 unless marked otherwise.

  • fatal: 'feature-a' is already used by worktree at '<path>' — the branch is checked out in another worktree. git switch feature-a in your main checkout prints the same thing. Use a new branch (-b), check out --detach, or remove the other worktree. Older git words it is already checked out at. In the Codex app, use Hand off instead.
  • fatal: a branch named 'feature-a' already exists — you passed -b with an existing name. Drop -b to check that branch out.
  • fatal: '.worktrees/feature-a' already exists — the path is taken. Pick another, or remove what's there.
  • error: cannot delete branch 'feature-a' used by worktree at '<path>' — remove the worktree first, then the branch.
  • fatal: '<path>' contains modified or untracked files, use --force to delete it — commit or stash, or pass --force. Gitignored files don't trigger this: my copied .env and a stub node_modules were deleted silently, so never keep anything only there.
  • fatal: cannot remove a locked working tree, lock reason: … — git worktree unlock <path>, then remove. Claude Code locks the worktree while an agent runs, and headless runs leave the lock behind.
  • prunable in git worktree list, or the branch is still "in use" after you rm -rf'd the directory — the metadata survived. git worktree prune -v printed Removing worktrees/feature-b: gitdir file points to non-existent location, and the branch was free again.
  • Failed to resolve base branch "HEAD": git rev-parse failed (Claude Code, per its common-workflows docs) — the repo has no commits. Make one. Plain git 2.55 instead creates an orphan branch.
  • fatal: Unable to create '<main>/.git/worktrees/feature-a/index.lock': Operation not permitted — the agent's sandbox allows writes only inside the worktree, and git writes to the main checkout's .git. I reproduced it with a macOS Seatbelt profile that re-allows writes only under the worktree; adding the main .git made the same commit succeed. Claude Code's docs say its sandbox allows these writes. Codex CLI's --add-dir makes an extra directory writable: pass it $(git rev-parse --path-format=absolute --git-common-dir). Octomind's --sandbox grants writes only to the working directory plus its state and temp paths, so in a linked worktree, commit from your own shell.
  • Test counts multiply, or ESLint says No tsconfigRootDir was set, and multiple candidate TSConfigRootDirs are present — worktrees nested inside the repo. Exclude them (see the Claude Code section) or keep worktrees outside it.

Merge the Agents' Work Back and Clean Up

Branches live in the shared repository, so you merge an agent's work from your main checkout without pushing anything. Preview first: two agent branches can each merge cleanly into main and still conflict with each other.

bash
# Exit 0 = clean, 1 = conflict. Touches no files.
for b in feature-a feature-b; do
  git merge-tree --write-tree --quiet main "$b" && echo "$b: clean" || echo "$b: CONFLICT"
done
git merge-tree --write-tree --name-only feature-a feature-b   # do the agents collide?

In my run, both branches were clean against main, and the pairwise check caught the real problem:

console
$ git merge-tree --write-tree --name-only feature-a feature-b
575eb286349af987878205a4d413cd43c56c876e
app.js

Auto-merging app.js
CONFLICT (content): Merge conflict in app.js

git merge-tree exits 1 on conflicts (git-merge-tree docs), so the loop works in a script. Merge the first branch, then rebase the second inside its own worktree, where the agent that wrote it can resolve the conflict:

bash
git merge feature-a                                  # in the main checkout
git -C ../myapp.worktrees/feature-b rebase main      # conflicts surface here, not in main

Prefer pull requests? Push from inside the worktree — git push -u origin feature-a — exactly as from any checkout. Then tear down:

bash
git worktree remove ../myapp.worktrees/feature-a
git branch -d feature-a          # -D if you're discarding unmerged work
git worktree prune -v            # clears metadata for directories deleted by hand
git worktree list                # should show only the main checkout

Where Worktrees Stop Helping (Honest Version)

Worktrees isolate files. Everything else about parallel agents is still your problem.

Overlapping tasks still conflict. Two agents editing the same module produce the conflict above, just later. Split work by file or module, and run the pairwise preview before merging anything.

The machine is still shared. One Postgres server, one Docker daemon, the same CPU cores. Running four or five Claude agents at once, incident.io named databases, ports and local services as their open problem in 2025 (incident.io engineering blog). Separate names fix collisions, not contention: several agents running full test suites at once will slow each other down.

A worktree is a directory, not a jail. Claude Code blocks edits, commands and git redirects that reach the main checkout from an isolated session. Other agents stay out only if their own sandbox keeps them out. If parallel agents tempt you into running agents without permission prompts, put the sandbox in place first.

Submodules are rough. The git docs still call multiple checkouts experimental and submodule support incomplete, and advise against multiple checkouts of a superproject.

Worktrees aren't sub-agents. They run separate sessions on separate branches. To split one task across helpers inside a session, use sub-agents instead.

[AUTHOR: How many parallel agents do you run before reviewing their branches becomes the bottleneck, and what breaks first?]

Get Octomind — start it in any worktree and it picks up that checkout's AGENTS.md, on any model.

FAQ

What is a Claude worktree?

A Claude worktree is a git worktree that Claude Code creates with claude --worktree <name> (or -w). It lives in .claude/worktrees/<name> on a new branch named worktree-<name>, so the session edits its own copy of the files while sharing the repository's history. By default it branches from the remote's default branch; set worktree.baseRef to "head" to start from your local commits.

Does Codex support git worktrees?

Yes. The Codex CLI has codex --worktree and the /worktree command, on by default since 0.156.0 (September 2026); it creates a detached-HEAD checkout of your current commit under ~/.codex/worktrees/ and never deletes it automatically. The Codex app adds a Worktree mode, permanent worktrees, .worktreeinclude copying, and automatic cleanup that keeps the newest 15.

How do I set up worktrees in Cursor?

Start an agent in a worktree from the Agents Window, with /worktree in the IDE, or with agent --worktree <name> in the CLI. Put setup commands such as npm ci and cp $ROOT_WORKTREE_PATH/.env .env under setup-worktree in .cursor/worktrees.json. Cursor stores worktrees in ~/.cursor/worktrees and keeps the newest 25 per machine.

Why is my .env file missing in the worktree?

Git checks out only tracked files, and .env is normally gitignored, so a new worktree doesn't have it. List it in .worktreeinclude for Claude Code or the Codex app, copy it from $ROOT_WORKTREE_PATH in Cursor's worktrees.json, or use a script like wt-new above. Change its PORT and DATABASE_URL per worktree so parallel agents don't share a server or database.

How do I fix "is already used by worktree"?

The branch is checked out in another worktree, and git allows each branch in only one place at a time. Create a new branch with -b, check out with --detach, or remove the other worktree with git worktree remove. If you deleted that directory by hand, run git worktree prune first to release the branch.