Running Parallel Claude Code Sessions With Git Worktrees: A Step-by-Step Setup

Macro photograph of a populated microcontroller board, representing isolated, parallel hardware components

Claude Code can run several isolated coding sessions against the same repository at once by giving each one its own git worktree: a separate working directory and branch that shares the repository’s history, so edits in one session never touch files in another. The fastest way to start one is claude --worktree <name>, which creates the worktree under .claude/worktrees/<name>/ on a new branch and launches Claude inside it. The rest of this walkthrough covers setup, mid-session switching, subagent isolation, cleanup, and the failures you’ll actually hit.

What isolation actually means here

A worktree isn’t just a convention Claude follows voluntarily — Claude Code enforces it with four checks while a session is isolated in one, and the same checks cover every subagent that session spawns:

  • File edits: an Edit, Write, or NotebookEdit targeting a path in the main checkout is blocked.
  • Command working directory: a Bash, PowerShell, or Monitor command whose working directory resolves to the main checkout — or can’t be verified to stay outside it — is blocked.
  • Git redirects: a command that redirects git into the main checkout via git -C, --git-dir, GIT_DIR/GIT_WORK_TREE, or a cd beforehand is blocked.
  • Command shape: a command Claude Code can’t statically verify stays inside the worktree — a runtime-computed command name, unparseable syntax, or a shell expansion that hides what runs — is blocked outright, with no override.

That last check matters in practice: if a script you ask Claude to run builds a command string dynamically, Claude Code may refuse it from inside a worktree even though the same command would run fine in the main checkout. The fix is to have Claude split it into plain, separate commands rather than fight the check.

Step 1: Start a session in a worktree

claude --worktree feature-auth

Run the same command with a different name in a second terminal to start a parallel session; omit the name and Claude Code generates one, like bright-running-fox. Interactive runs require workspace trust — if you haven’t run Claude in the repository before, run claude there once to accept the trust dialog, or the --worktree flag exits with an error. Non-interactive runs with -p skip that check. Add .claude/worktrees/ to your .gitignore so the worktrees themselves don’t show up as untracked files in your main checkout.

Step 2: Carry your env files in automatically

A worktree is a fresh checkout, so gitignored files like .env aren’t there by default — you’d otherwise have to copy them in by hand every time. A .worktreeinclude file at your project root, written in .gitignore syntax, copies matching gitignored files into every new worktree automatically:

.env
.env.local
config/secrets.json

Only files that are both gitignored and matched by a pattern get copied, so tracked files are never duplicated. This applies to every worktree Claude Code creates with git — --worktree sessions, subagent worktrees, and parallel sessions started from the desktop app.

Step 3: Switch worktrees mid-conversation instead of restarting

You don’t need to relaunch Claude Code to move into a worktree. Ask Claude mid-session to “work in a worktree,” and it creates one itself with the EnterWorktree tool; from inside a worktree it can jump directly to another one under .claude/worktrees/ by calling EnterWorktree again, leaving the previous one untouched on disk. If the target path is outside the repository’s .claude/worktrees/ directory, Claude Code stops and asks for your approval first — the move takes the session’s working directory, write access, and project configuration (including CLAUDE.md) with it, and only bypassPermissions mode skips that prompt. To leave a worktree, Claude calls ExitWorktree.

One detail that trips people up when they wire up custom hooks: hook paths don’t follow the worktree. The CLAUDE_PROJECT_DIR environment variable keeps pointing at the original project root even after Claude enters a worktree, so a hook command built from it still runs out of the main checkout. The worktree’s actual path arrives separately, in the hook’s input JSON as a cwd field — read that field if your hook needs to act on the worktree rather than the main checkout. This is the same input-JSON mechanism our walkthrough of a PreToolUse hook reads from to make a blocking decision.

Step 4: Isolate subagents in their own worktrees

Subagents can get their own worktrees too, so several run in parallel without their edits colliding. Ask Claude to “use worktrees for your agents” for one session, or make it permanent for a specific custom subagent by adding isolation: worktree to its frontmatter:

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

Apply the requested refactor across every affected file, then run the tests
and report the results.

Claude Code removes a subagent’s temporary worktree automatically once it finishes with no changes left behind; one that does leave changes stays on disk until a periodic sweep can remove it without losing work. A subagent in the default worktree location also does not load the worktree’s own CLAUDE.md or .claude/rules/ — it inherits the instruction files its parent conversation started with instead, even if the worktree’s branch has different ones. That’s a subtle gap if you were expecting per-branch instructions to apply automatically; they don’t, by design, for subagent worktrees in the default location.

Step 5: Clean up when you’re done

When you exit an interactive worktree session, Claude Code checks for anything removal would lose — changed or untracked files, uncommitted submodule work, new commits. A clean, unnamed session’s worktree and branch are removed automatically; a named session prompts you first so you can keep it. If you keep it, Claude Code prints the exact resume command to pick it back up later. Non-interactive -p runs skip this prompt entirely and leave the worktree locked until a later stale-lock sweep releases it — to remove one by hand, run git worktree remove, unlocking it first with git worktree unlock if git refuses.

What stays shared with your main checkout

Isolation is about files and branches, not everything. Four things carry over from the main checkout whether you created the worktree with --worktree, git worktree add, or the desktop app:

SharedWhy it matters
The repository’s .git directorygit commit and other git commands in a worktree write to the same shared metadata, and sandboxing allows this.
Project-scope pluginsPlugins installed at project scope from the main checkout load in every worktree of the same repo — no per-worktree reinstall.
Permission approvals“Don’t ask again” for a Bash command in a worktree session is saved to the main checkout’s local settings file, so it applies everywhere and survives the worktree’s removal.
Untracked skills, agents, commandsIf a worktree’s checkout has no .claude/skills of its own, Claude Code reads through to the main checkout’s. A worktree with its own copy uses only that copy.

The permission-approval behavior is worth sitting with: an approval you grant once, in any worktree, becomes a standing rule across the whole repository. That’s convenient for trusted commands and worth remembering before you approve something in a throwaway experimental worktree.

Problems you’ll actually hit, and how each one resolves

These aren’t hypothetical edge cases — they’re the specific failures Anthropic’s own troubleshooting documentation lists, each with a distinct fix:

  1. Git LFS files show up as pointer files instead of real content. This happens when LFS was set up with git lfs install --local, which writes the filter into the repository’s own .git/config rather than your global config. Claude Code deliberately skips a repository’s own filter drivers when creating a worktree, since a filter driver is a shell command that anyone with write access — including Claude — could have planted. Fix: run git lfs pull inside the new worktree to fetch the real files.
  2. Worktree creation refuses outright with a filter-driver error. Four specific git-config conditions block creation rather than silently skipping the filter: an unreadable .git/config, a filter driver whose name can’t be neutralized, an includeIf directive, or a setting that points at a custom LFS transfer agent. Each error names the exact key or condition; the fix is always to resolve that one thing in the repository’s own config and retry, not to repeat the command.
  3. “Refusing to use this directory as an isolation worktree.” Claude Code checked the directory’s git identity and found it actually resolves back into the main checkout — for example its .git file points at the main repository, or a core.worktree redirect sends it there. Running an ordinary command like git reset --hard from such a directory would silently act on your main checkout. The message names the specific cause; the fix ranges from launching from the correct directory to recreating the worktree cleanly.
  4. A symlinked .claude or worktree path. Claude Code refuses to create a worktree when .claude, .claude/worktrees, or the target directory itself is a symlink, naming the exact path. Remove the symlink and retry.
  5. A resumed session can’t get back into its worktree. If the directory is gone, Claude Code clears the binding and continues in the current directory with no action needed from you. If it can’t be verified for a more specific reason, the message matches one of the refusal causes above — match it the same way rather than assuming the worktree is corrupted.

Notice the pattern across all five: Claude Code’s isolation checks are deliberately conservative. When it can’t prove a path, a command, or a filter driver is safe, it refuses rather than guessing — the same philosophy behind the command-shape check described above, and the same reasoning behind auditing anything with shell access, which we cover from a different angle in our checklist for a team-wide instructions file: a tool that fails loud and specific is easier to trust than one that fails silently.

Worktrees aren’t the only way to parallelize Claude Code — subagents split work up inside a single session instead of across checkouts, and cross-session messaging lets separate worktree sessions pass findings to each other once they’re done. If your repository also runs a pre-commit secrets scanner, remember that a worktree is a fresh checkout: a hook installed only through a local .git/hooks symlink in the main checkout, like the one in our Gitleaks pre-commit config walkthrough, needs to be reinstalled or shared explicitly, since .git/hooks isn’t one of the four things worktrees share automatically.

If you’re building a custom Skill that specifically manages worktrees — say, one that creates a worktree per ticket and tears it down on merge — the same SKILL.md structure we walked through separately applies directly: keep the frontmatter description specific about when to trigger it, and put the full list of edge cases above into a reference file rather than the SKILL.md body itself, so it only loads tokens when a worktree task is actually in progress.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top