Worktrees Workflow 28 Mar 2026

Worktrees: Isolated Agents, Isolated Work

If you're running multiple Claude Code agents, worktrees should be part of your workflow. Here's how to set them up properly.


Any professional developer using Claude Code should be using worktrees. They isolate the work and allow each agent to properly verify what it's doing. No more agents tripping over each other's changes, no more wondering which file belongs to which task.

What is a worktree?

For anyone who hasn't used them before, a worktree is essentially an isolated branch. When Claude Code creates a worktree, it creates a new branch off the main branch and duplicates the entire directory. You get a completely separate working copy of your project, with its own files, its own changes, and its own branch.

That means you can have one agent working on a feature in one worktree while another agent fixes a bug in a different worktree, and neither of them interferes with the other.

The gotchas

Worktrees are brilliant, but there are two things that catch people out:

Both of these will cause your agents to fail if you don't address them upfront.

Copying gitignored files

Claude Code has a .worktreeinclude file for exactly this. Drop it in your project root and list which gitignored files should be copied into worktrees. It uses .gitignore syntax, and only copies files that match a pattern and are also gitignored. Tracked files are never duplicated.

# .worktreeinclude
.env
.env.local

That's it. Any file matching those patterns that's gitignored gets copied into every new worktree automatically.

Installing dependencies automatically

There are a few ways to handle this, but the most reliable approach I've found is a Claude Code hook on SessionStart. The hook calls a shell script that checks if you're in a worktree, checks if node_modules already exists, and if not, runs your package manager's install command.

First, the hook config in your .claude/settings.json or .claude/settings.local.json:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/worktree-install.sh"
          }
        ]
      }
    ]
  }
}

Then the script at .claude/hooks/worktree-install.sh:

#!/bin/bash
# Auto-install deps when session starts in a worktree
# without node_modules

INPUT=$(cat)
CWD=$(echo "$INPUT" | python3 -c \
  "import sys,json; print(json.load(sys.stdin).get('cwd',''))")
[ -z "$CWD" ] && exit 0

# Detect worktree: --git-dir and --git-common-dir
# differ in worktrees
GIT_DIR=$(git -C "$CWD" rev-parse --git-dir 2>/dev/null) \
  || exit 0
GIT_COMMON=$(git -C "$CWD" rev-parse --git-common-dir \
  2>/dev/null) || exit 0
[ "$GIT_DIR" = "$GIT_COMMON" ] && exit 0

# Skip if deps already installed
[ -d "$CWD/node_modules" ] && exit 0

cd "$CWD" && npm install 2>&1
Note

Swap npm install for pnpm install, bun install, or whatever your project uses. The rest of the script stays the same.

The key bit is the worktree detection. In a regular checkout, --git-dir and --git-common-dir point to the same place. In a worktree, they differ. That's how the script knows it's running in a worktree and not your main directory.

Trust me on this one

Once you start using worktrees and get used to the workflow, you will never go back. The isolation alone is worth it, but combined with the automatic setup from hooks and .worktreeinclude, it just works.