You're working on a new feature with Claude Code. The plan is set, half the flow is done, Claude has the context loaded. Then an urgent production bug comes in.
So you open a second terminal and run claude there — in the same project directory. Now two sessions are running on the same checkout: one builds the feature, the other fixes the bug. Sooner or later they overwrite the same files. Both states end up mixed, and the first session's context is gone.
This is exactly what worktrees solve. Since native CLI support landed (introduced February 20261), you no longer wire any of this up by hand — one flag does it.
What a worktree actually is
Per Anthropic's docs, a git worktree is "a separate working directory with its own files and branch"2 — an additional working directory with its own branch that shares the same .git history and remotes as your main checkout. You're not re-cloning the repo; you get a second, isolated working copy. Edits in one copy never touch the files in the other. That's the key: two Claude sessions can run in parallel without rewriting each other's files.
Quickstart
The --worktree flag (short form -w) creates the worktree and starts Claude inside it:
claude --worktree feature-checkout
By default the worktree lands under .claude/worktrees/<name>/ at your repo root, on a new branch named worktree-<name> — so here, .claude/worktrees/feature-checkout/ on worktree-feature-checkout.2
Three things worth knowing up front:
- Omit the name:
claude --worktreewith no argument generates a random name likebright-running-fox.2 - Update
.gitignore: Add.claude/worktrees/to your.gitignore, otherwise worktree contents show up as untracked files in your main checkout.2 - Trust dialog: Before
--worktreeworks interactively in a directory for the first time, runclaudethere once and accept the workspace trust dialog. Otherwise--worktreeexits with an error. Non-interactive runs with-pskip the trust check.2
The real-world case: two tracks in parallel
Back to the opening scenario. Here's the clean version. Terminal 1 stays on the feature:
# Terminal 1 — feature
claude --worktree feature-checkout
Terminal 2 gets its own isolated track for the hotfix:
# Terminal 2 — hotfix
claude --worktree fix-cart-total
The repo now looks like this:
shop-web/
├── .claude/worktrees/
│ ├── feature-checkout/ # branch: worktree-feature-checkout
│ └── fix-cart-total/ # branch: worktree-fix-cart-total
└── src/ # your main checkout, untouched
Both sessions run at the same time, each with its own file state and branch. The bugfix lands cleanly on worktree-fix-cart-total; the feature keeps growing undisturbed on worktree-feature-checkout. No stash, no branch switch, no lost context.
Which branch do worktrees fork from? By default they branch from origin/HEAD, the remote's default branch — so you start from a clean tree matching the remote. If no remote is configured or the fetch fails, Claude falls back to your local HEAD.2 To always branch from local HEAD instead (including your unpushed commits), set this in settings:
{
"worktree": {
"baseRef": "head"
}
}
The setting accepts only "fresh" or "head", not arbitrary git refs.2 "head" is mainly useful when isolating subagents that need to operate on in-progress, unpushed work.
Working from a PR: Pass a PR number prefixed with #, and Claude fetches pull/<number>/head from origin and creates the worktree under .claude/worktrees/pr-<number>:2
claude --worktree "#1234"
Carry gitignored files over: .worktreeinclude
A worktree is a fresh checkout. That means untracked files like .env or .env.local aren't present — and without them the web app simply won't start in the worktree. That's what .worktreeinclude in your project root is for. It uses .gitignore syntax, and only files that both match a pattern and are gitignored get copied. Tracked files are never duplicated.2
# .worktreeinclude
.env
.env.local
config/secrets.json
This applies to --worktree, to subagent worktrees, and to parallel sessions in the desktop app.2
Isolating subagents
Worktrees aren't only for whole sessions. Subagents can run in their own worktrees too, so parallel edits don't conflict. Two ways:
- Ad hoc: ask Claude to "use worktrees for your agents".
- Permanently: set
isolation: worktreein a custom subagent's frontmatter.2
Each subagent then gets a temporary worktree that's removed automatically when the subagent finishes without changes. The base-branch logic is the same as for --worktree.2
Switching mid-session
You don't have to decide at launch. Tell Claude "work in a worktree" mid-session and it creates one via the EnterWorktree tool and moves the session into it — handy when you realize halfway through that your changes need isolation. From inside a worktree, Claude can switch directly to another one under .claude/worktrees/ via EnterWorktree; the previous one stays on disk, untouched.2
Cleanup
What happens when you leave a worktree session depends on whether there were changes:2
- Nothing uncommitted, no untracked files, no new commits: the worktree and its branch are removed automatically. If the session has a name, Claude prompts instead so you can keep the worktree for later.
- Uncommitted changes, untracked files, or new commits: Claude prompts to keep or remove. Removing deletes the directory and branch — including any uncommitted changes.
- Non-interactive
-pruns: worktrees from--worktreearen't cleaned up automatically here (there's no exit prompt). Remove them manually withgit worktree remove.
Worktrees Claude created for subagents or background sessions are removed automatically once they're older than your cleanupPeriodDays setting — provided they're clean. Worktrees from --worktree are never touched by that sweep.2 While an agent is running, Claude locks its worktree via git worktree lock so concurrent cleanup can't remove it.2
What nobody tells you: file isolation isn't runtime isolation
Here's the honest part. For a library or a CLI tool, --worktree alone is the whole story: source in, build in isolation, done. For a real web app, it isn't.
Worktrees isolate files, not runtime state. Two typical traps that no marketing snippet mentions:34
- Port conflict: your dev server runs on port 3030. The second worktree wants the same port. Without intervention, the second instance won't start.
- Shared database: both worktrees point at the same Postgres instance. Agent A runs a migration — and Agent B is suddenly sitting on an inconsistent schema.
File isolation is necessary but not sufficient. The clean fix runs through a WorktreeCreate hook that replaces the entire worktree-creation logic: it reads the worktree name from stdin, creates the directory, writes a .env.local with a unique DB name and port derived from the branch name, calls your setup script, and prints the path to stdout — Claude then uses that path as the working directory.3 A matching WorktreeRemove hook tears down the DB and directory at the end. This same hook mechanism is also the official path for non-git VCS (SVN, Perforce, Mercurial); because the hook replaces the default logic, .worktreeinclude is no longer processed in that case.2
Concrete recommendations by stack
Library / CLI tool (no runtime state). Here --worktree alone is enough. Once, keep the worktree directories out of your main checkout:
echo ".claude/worktrees/" >> .gitignore
After that you start parallel sessions with claude --worktree <name>. No hook, no further config.
Full-stack web app (Node/TypeScript + Postgres). File isolation alone isn't enough — each worktree needs its own DB and its own port. Two levels:
Level 1 — just carry config over. If your parallel agents don't write to the same DB at the same time, .worktreeinclude is often enough to copy the .env files into each worktree:
# .worktreeinclude
.env
.env.local
Level 2 — real runtime isolation via a hook. Once agents actually run concurrently, you need unique DB names and ports. A WorktreeCreate hook handles that. Important to understand: once this hook exists, it replaces the entire default logic — you call git worktree add yourself in the script, and .worktreeinclude is no longer processed (you copy the config inside the hook instead).2
First, the settings pointing at two scripts:
// .claude/settings.json
{
"hooks": {
"WorktreeCreate": [
{ "hooks": [{ "type": "command", "command": "bash .claude/hooks/worktree-create.sh" }] }
],
"WorktreeRemove": [
{ "hooks": [{ "type": "command", "command": "bash .claude/hooks/worktree-remove.sh" }] }
]
}
}
Then the creation script. It reads the worktree name from stdin, creates the worktree, copies config, derives a DB name and port from the branch name, and prints the path to stdout — Claude Code uses that path as the working directory:23
#!/usr/bin/env bash
# .claude/hooks/worktree-create.sh
set -euo pipefail
NAME=$(jq -r .name) # worktree name arrives as JSON via stdin
DIR=".claude/worktrees/$NAME"
DB="shop_${NAME//-/_}"
PORT=$(( 3030 + $(echo -n "$NAME" | cksum | cut -d' ' -f1) % 1000 ))
git worktree add -b "worktree-$NAME" "$DIR" origin/HEAD >&2 # create the worktree yourself
cp .env "$DIR/.env" 2>/dev/null || true # carry gitignored config over
createdb "$DB" >&2 # one DB per branch
cat > "$DIR/.env.local" <<EOF # write unique DB + port
DATABASE_URL=postgres://localhost/$DB
PORT=$PORT
EOF
( cd "$DIR" && npm install && npm run db:migrate ) >&2 # set up the worktree
echo "$DIR" # path = the contract with Claude Code
And the cleanup counterpart that tears the DB and directory back down:
#!/usr/bin/env bash
# .claude/hooks/worktree-remove.sh
set -euo pipefail
NAME=$(jq -r .name)
dropdb --if-exists "shop_${NAME//-/_}" >&2
git worktree remove --force ".claude/worktrees/$NAME" >&2
The exact stdin schema is in the hooks reference; the fields may vary slightly by version.2
Subagent-heavy workflows. A custom subagent is a markdown file at .claude/agents/<name>.md. The block between the two --- lines at the top is the frontmatter — the agent's YAML config; below it sits its system prompt. Add the line isolation: worktree there and the agent automatically gets its own worktree on every run — you no longer have to ask for it each time. Example for a test runner that can be destructive and should therefore run in isolation:
---
name: test-runner
description: Runs the test suite and reports failures back
tools: Read, Bash, Glob, Grep
model: sonnet
isolation: worktree
---
You are a test runner. When invoked, run the full test suite,
summarize any failures, and return only the result.
You create the file yourself (or via the /agents command). name and description are required; isolation: worktree is the one line that forces the agent into its own worktree.2
Desktop app. Here you need none of this: enable worktree mode in the Code tab, and every new session gets its own worktree automatically. Location and branch prefix are configurable in settings. The most convenient route if you don't want to fiddle in the terminal.2
When not to. For sequential, dependent tasks (feature B builds on feature A), parallel worktrees buy you nothing. And for a one-line fix, the overhead isn't worth it.
The pattern underneath is simple: you shift from coder to coordinator. Each worktree is its own track, each track its own branch, each branch its own cleanly reviewable change. The craft isn't in the flag — it's in adding the runtime isolation the flag doesn't hand you.
FAQ
Can you switch the model inside a worktree?
Yes. A worktree is just a directory plus a branch — the session inside it is a normal Claude Code session. Switch the model mid-session with /model, or start with claude --worktree <name> --model <model>. The worktree doesn't tie you to a model. If you use isolated subagents, each subagent can set its own model in the frontmatter (model: ...), independent of the main session.
Can multiple sessions exist in one worktree?
It helps to separate timeline from concurrency. Sequentially, no problem: you can start several sessions in the same worktree directory, resume one, or name another — so over a worktree's lifetime multiple sessions can belong to it. Concurrently it's technically possible (two terminals in the same directory), but it recreates exactly the file collisions worktrees are meant to prevent. So the design principle is: one concurrent session per worktree, and for real parallelism each session gets its own worktree — which is what the desktop app does automatically. The clean exception is subagents with isolation: worktree: those don't run in the same worktree but each in its own.
Footnotes
-
Verdent Guides, "Claude Code Worktree Setup Guide" — references native
--worktreesupport as of Claude Code v2.1.49 (2026-02-19). https://www.verdent.ai/guides/claude-code-worktree-setup-guide (accessed 2026-06-15) ↩ -
Anthropic, "Run parallel sessions with worktrees", Claude Code Docs. https://code.claude.com/docs/en/worktrees (accessed 2026-06-15) ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15 ↩16 ↩17 ↩18 ↩19 ↩20 ↩21 ↩22
-
D. Galarza, "Extending Claude Code Worktrees for True Database Isolation". https://www.damiangalarza.com/posts/2026-03-10-extending-claude-code-worktrees-for-true-database-isolation/ (accessed 2026-06-15) ↩ ↩2 ↩3
-
Trigger.dev, "We ditched worktrees for Claude Code. Here's what we use instead". https://trigger.dev/blog/parallel-agents-gitbutler (accessed 2026-06-15) ↩