Claude Code Worktrees: Isolating Parallel Sessions

Remote Control’s server mode takes --spawn worktree, and background sessions use worktrees by default. Both lean on the same git feature, and if you do not know how it behaves you will eventually delete work you wanted. This is what is actually happening underneath.

A git worktree is a separate working directory with its own files and branch, sharing the repository history and remote with your main checkout. Two sessions in two worktrees cannot touch each other’s files.

Starting one

claude --worktree feature-auth
claude -w feature-auth

That creates .claude/worktrees/feature-auth/ at your repository root, on a new branch called worktree-feature-auth. Run it again with a different name in another terminal for a second isolated session. Omit the name and Claude generates one such as bright-running-fox.

Two things to do once, before this gets annoying:

  • Add .claude/worktrees/ to your .gitignore, or every worktree shows up as untracked files in your main checkout.
  • Run claude in the directory once to accept the workspace trust dialog. Interactive --worktree runs require it and exit with an error otherwise. Non-interactive -p runs skip the check.

The .env problem, and its fix

A worktree is a fresh checkout, so gitignored files are simply not there. Your .env is missing and nothing works. This catches everyone once.

Add a .worktreeinclude file at your project root, using gitignore syntax:

.env
.env.local
config/secrets.json

Those get copied into every worktree Claude Code creates with git, including subagent worktrees and Desktop parallel sessions. Only files that match a pattern and are gitignored are copied, so tracked files are never duplicated.

What happens when you exit

On exiting an interactive worktree session, Claude checks for changed files, untracked files, and new commits, then behaves differently depending on what it finds:

StateWhat happens
Clean, unnamed sessionWorktree and branch removed automatically
Clean, named sessionYou are prompted first, so you can keep it
Has work in itYou are prompted to keep or remove. Removing deletes the directory, the branch, and the work

Non-interactive -p runs have no exit prompt, so their worktrees are never cleaned up. Remove them yourself with git worktree remove, or they accumulate quietly.

Isolation is enforced, not just suggested

While a session is in a worktree, Claude Code actively blocks attempts to reach the main checkout, and the same enforcement covers every subagent spawned from it:

  • File edits targeting a path in the main checkout are blocked.
  • Commands whose working directory resolves to the main checkout are blocked, including ones it cannot verify stay outside.
  • Git redirects are blocked, whether through git -C, --git-dir, a GIT_DIR or GIT_WORK_TREE variable, or a cd into the main checkout before running git.

Claude sees each refusal as a tool error naming the worktree, so it can adjust rather than silently failing.

Where new worktrees branch from

By default they branch from the repository’s default branch on the remote, not from your current work. That surprises people who expected their in-progress feature branch to carry over. Change it with worktree.baseRef:

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

"fresh" is the default and starts from the remote default branch. "head" branches from your current local HEAD, carrying unpushed commits and feature-branch state. Use "head" when isolating subagents that need to work on what you are currently building.

You cannot set baseRef to a branch name. To start from a specific branch, create the worktree with git directly. You can, however, branch from a pull request:

claude --worktree "#1234"

Quote it so your shell does not read # as a comment. It fetches pull/1234/head and creates the worktree at .claude/worktrees/pr-1234.

Subagents in their own worktrees

Ask Claude to “use worktrees for your agents”, or make it permanent for a custom subagent with frontmatter:

---
name: refactorer
description: Applies mechanical refactors across many files
isolation: worktree
---

Each gets a temporary worktree, removed automatically when the subagent finishes with no changes. One with changes stays on disk until a periodic sweep can remove it without losing work.

The cleanup sweep

A periodic sweep removes worktrees Claude created for subagents and background sessions once they are older than your cleanupPeriodDays setting. It deliberately skips any worktree holding changed files, untracked files, or unpushed commits, and it never touches worktrees you made with --worktree.

While an agent runs, Claude locks its worktree with git worktree lock so concurrent cleanup cannot remove it. The sweep also releases locks left by sessions whose process died, so a killed background session no longer strands its worktree. Locks you set yourself are never released.

To remove one the sweep is keeping, use git worktree remove, adding --force if it has uncommitted changes.

What worktrees share with your main checkout

  • The .git directory. Git commands in a worktree write to the shared repository, which is why git commit works from inside one even with sandboxing on.
  • Project-scope plugins, so you do not reinstall per worktree. Requires v2.1.200 or later.
  • Permission approvals. Choosing “don’t ask again” in a worktree saves to the main checkout’s .claude/settings.local.json, so it applies everywhere and survives the worktree being removed. Before v2.1.211 the approval was saved inside the worktree and lost with it.

Where this meets Remote Control

In server mode, --spawn worktree gives each on-demand session its own worktree instead of sharing one working directory. That is the setting that makes hosting several concurrent sessions actually safe:

claude remote-control --spawn worktree --capacity 6

The default, same-dir, shares one directory and will conflict when two sessions edit the same file. More on running several at once in sessions, and on leaving them running in background sessions.