Once a coding agent can work for twenty minutes without you, the obvious next step is to run two. One session fixes a bug while another builds a feature, and a third writes tests. It works right up until they share a directory.
Two agents in one checkout are like two people typing into the same document. Session A rewrites auth.ts while session B is halfway through reading it. One runs the formatter across the repo and the other's diff fills with noise. One switches branches, and the other's files change underneath it. Test runs pick up half-finished edits from the other session, so a failure tells you nothing. At best you get a confusing diff; at worst one agent quietly undoes the other's work.
The fix is to give each session its own working directory. Git has had a built-in way to do that since 2015: worktrees.
What a git worktree is
A worktree is an extra working directory attached to the same repository. Every worktree has its own files, its own checked-out branch, its own index and its own HEAD. They all share one .git object store, so commits, branches, stashes and remotes are common to all of them.
That's different from cloning the repo again. A second clone duplicates the history and has to fetch and push separately. A worktree only adds the checked-out files, and a commit made in one is visible from every other immediately.
For agents, this is exactly the right shape. Each session gets a private directory to edit, build and test in, and you can still review and merge everything from one place.
The four commands you need
Run these from your main checkout.
Create a worktree
# New branch "fix-login" in a sibling directory
git worktree add -b fix-login ../myapp-fix-login
# Existing branch
git worktree add ../myapp-review feature/search
# Throwaway checkout with a detached HEAD (no branch)
git worktree add -d ../myapp-scratch
With -b, the new branch starts from your current HEAD. To start from somewhere else, add the start point at the end:
git worktree add -b fix-login ../myapp-fix-login origin/main
List them
git worktree list
/Users/me/code/myapp bec18a8 [main]
/Users/me/code/myapp-fix-login bec18a8 [fix-login]
Remove one
git worktree remove ../myapp-fix-login
Git refuses if the worktree has modified or untracked files, which is a useful safety net. --force overrides it, so only use that once you're sure nothing in there matters. Removing a worktree doesn't delete its branch. If the branch is merged or abandoned, delete it separately:
git branch -d fix-login # -D if it was never merged
Prune stale entries
If you delete a worktree folder with rm -rf or Finder instead of git worktree remove, git still has a record of it, and git worktree list marks it as prunable. Clear those records with:
git worktree prune
Naming and placement
Two conventions keep this manageable.
Where to put them. Either use sibling directories (../myapp-fix-login) or a dedicated folder such as ../myapp-worktrees/fix-login. If you'd rather keep them inside the repo, use a folder like .worktrees/ and add it to .gitignore, or every file in it shows up as untracked in your main checkout. Siblings are the least surprising for editors and build tools, since nothing ends up nested inside another project.
What to call them. Name the directory after the branch, and name the branch after the task: fix-login, feat-csv-export, agent/refactor-billing. When you have four terminals open, git worktree list should tell you at a glance which session is doing what.
Set up each worktree before starting an agent
A new worktree contains only tracked files. Anything gitignored is missing, and that's usually the stuff your project needs to run.
Dependencies. node_modules, .venv, target/ and friends aren't there. Install them in each worktree:
cd ../myapp-fix-login
npm ci # or: pnpm install, uv sync, bundle install, cargo build
This costs disk space and time per worktree. Package managers with a shared cache (pnpm's global store, uv's cache) make repeat installs much cheaper.
Environment files. .env and .env.local are gitignored, so copy them over:
cp ../myapp/.env .env
Ports. Two dev servers can't both listen on port 3000. If a session will run the app, give each worktree its own port, usually through the same env file:
echo "PORT=3001" >> .env
Databases and services. If both sessions point at the same local database, one agent's migration or test fixtures can break the other's run. Give each worktree its own database name, or its own SQLite file. If you use Docker Compose, a different COMPOSE_PROJECT_NAME per worktree gives each one separate containers, networks and volumes.
If you repeat these steps often, put them in a small script that creates the worktree, copies env files and installs dependencies.
Running claude or codex in each worktree
The manual approach works with any agent. Open one terminal per worktree and start a session in each:
# Terminal 1
cd ~/code/myapp-fix-login && claude
# Terminal 2
cd ~/code/myapp-csv-export && codex
Each session treats its worktree as the project. Because your CLAUDE.md or AGENTS.md is a tracked file, it's present in every worktree automatically. Give each session one focused task with a clear finish line ("done when the login tests pass and lint is clean"), then let it run.
A terminal multiplexer such as tmux keeps several sessions in one window, and lets you detach and come back later. Codex and Claude Code both resume per directory: claude --continue resumes the most recent conversation in the current directory, and codex resume --last does the same for Codex. Running either from inside a worktree picks up that worktree's session.
Claude Code's built-in worktree support
Current Claude Code versions can create the worktree for you. Pass --worktree (or -w) with a name:
claude --worktree fix-login
According to Anthropic's docs, this creates a worktree at .claude/worktrees/fix-login/ inside your repo, on a new branch named worktree-fix-login, and starts the session there. Run the same command with another name in a second terminal for a second isolated session. Leave out the name and Claude generates one. A few details worth knowing:
-
Add
.claude/worktrees/to.gitignore, as the docs recommend, so those directories don't show up as untracked files in your main checkout. -
The base is your default branch, not your current branch. By default, new worktrees branch from the repository's default branch on the remote (usually
main). To branch from your localHEADinstead, set"worktree": { "baseRef": "head" }in settings. For a specific existing branch, create the worktree with git yourself. -
.worktreeincludecopies gitignored files. Put a file named.worktreeincludeat the project root, using.gitignoresyntax, and Claude copies matching gitignored files such as.envinto every worktree it creates. -
Cleanup happens on exit. When you leave the session, Claude checks the worktree. If it's clean and the session was unnamed, it removes the worktree and its branch. If there are changes or commits, it asks whether to keep or remove it. Non-interactive runs with
-pdon't clean up, so remove those withgit worktree remove. - The repo needs at least one commit, or worktree creation fails.
You can also ask Claude mid-session to "work in a worktree," and custom subagents can run in their own worktree by adding isolation: worktree to their frontmatter.
Codex CLI's worktree support
Recent Codex CLI releases have managed worktrees too, though OpenAI describes this as newer functionality, so check codex --help on your version. In an interactive session, type /worktree to start a new chat, or move the current one, into a managed git worktree. There's also a "Browse worktrees" option for finding existing ones. For scripted runs, --worktree works with new and forked runs:
codex exec --worktree "implement the next step"
codex fork <SESSION_ID> --worktree
The docs say --worktree requires local execution and can't be combined with resume, review or --ephemeral. If you'd rather keep full control over branch names and locations, plain git worktree add plus codex works on every version.
Reviewing and merging the results
Every worktree is a branch, so review works like reviewing a colleague's branch. From your main checkout:
git log --oneline main..fix-login # what it committed
git diff main...fix-login # the full change
Uncommitted work only exists inside its worktree, so ask the agent to commit when it's done, or check with git -C ../myapp-fix-login status.
Then merge whichever way your team does: push the branch and open a pull request, or merge locally:
git switch main
git merge fix-login
Merge one branch at a time and run the tests after each. Two agents that both worked "correctly" in isolation can still conflict, especially when both touched a shared module. When a later branch conflicts, it's often easiest to rebase that branch on the updated main inside its own worktree, and let its session fix the conflicts. It has the context on why it made its changes.
Cleaning up
When a branch is merged or abandoned:
git worktree remove ../myapp-fix-login
git branch -d fix-login
git worktree prune
Check git worktree list every so often. Forgotten worktrees pile up, each with its own node_modules, and keep their branches checked out.
Common pitfalls
The same branch can't be checked out twice. Git won't let two worktrees hold the same branch. If main is checked out in your main directory, git worktree add ../x main fails with 'main' is already used by worktree at .... Create a new branch with -b, use -d for a detached checkout, or remove the worktree that's holding the branch.
Shared lockfiles conflict. If two sessions both add dependencies, both branches change package-lock.json, pnpm-lock.yaml, uv.lock or Cargo.lock, and the merge conflicts. Don't hand-merge lockfiles. Take one side, re-run the install so the package manager regenerates the file, and commit the result. Better yet, keep dependency changes to one session at a time.
Generated files and migrations collide. Two branches that each add a database migration with the next sequence number, or that each regenerate an API client, will clash at merge time even if the code is fine. Assign those tasks to one session.
Submodules need extra care. A new worktree doesn't initialize submodules, so run git submodule update --init --recursive inside it. Git's own docs say submodule support in multiple worktrees is incomplete, and git worktree remove needs --force for a worktree with submodules. If your repo leans heavily on submodules, test the workflow before you rely on it.
Your IDE gets confused. Opening a worktree nested inside the main project can make an editor index everything twice, run duplicate language servers, or show the same file in search results twice. Open each worktree as its own window (code ../myapp-fix-login), and exclude your worktree folder from the main project's indexing if you keep worktrees inside the repo.
Agents wander. An agent running in a worktree can still cd back into your main checkout if nothing stops it. Claude Code blocks edits and git commands that target the main checkout while a session is isolated with --worktree. With manual worktrees, tell the agent which directory it owns, and keep sandbox or permission settings scoped to it.
Parallel sessions burn limits faster
One last thing that's easy to miss: parallel sessions don't come with parallel quotas. Every Claude Code session on your account draws from the same usage limit, and every Codex session draws from the same ChatGPT plan limit. Four agents running for an hour can use up roughly what one agent would use in four hours, and you hit the limit in the middle of the afternoon, with all four sessions stopping at once.
So run as many sessions as you can actually review, not as many as your machine can handle. Give the cheap, mechanical tasks (renames, test scaffolding, lint fixes) to parallel sessions, and save your heavy reasoning work for one focused session. If you're on API billing instead of a subscription, the same logic applies to your bill.
A short checklist
- The repo has at least one commit and a clean main checkout.
- Each task gets its own branch and worktree, named after the task.
- Worktrees live in sibling directories or a gitignored folder.
- Dependencies are installed and
.envfiles copied in each worktree (or listed in.worktreeincludefor Claude Code). - Each worktree has its own port, database and Compose project name if it runs services.
- Each session has one focused task with a clear "done when."
- Dependency, lockfile and migration changes are limited to one session.
- Agents commit their work before you review it.
- Branches are merged one at a time, with tests after each.
- Finished worktrees are removed, their branches deleted, and stale entries pruned.
- You're watching how fast parallel sessions draw down your shared limit.
Worktrees don't make agents smarter. They just stop agents from tripping over each other, which is most of what running several at once requires.
Disclosure: I built TokenBar, a $15 lifetime Mac menu bar app (built on the MIT-licensed CodexBar) that shows your Claude and Codex usage and cost, which helps when several sessions are draining the same limit. It's at tokenbar.site if that's useful.
Top comments (0)