Skip to content

Worktrees

Separate working directories per branch sharing one repository for parallel development without file system conflicts.

Tool Selection: Native vs. Manual Worktree Management

Before following the manual git commands in this skill, check whether your runtime provides a native worktree tool:

  • Claude Code: If the EnterWorktree tool is available, use it. It handles worktree creation, branch setup, directory switching, and cleanup automatically. You can also be started with --worktree <name> which pre-configures isolation. Only fall back to the manual instructions below if EnterWorktree is not available or is denied in permissions.

  • VS Code + Copilot background agents: If you are running as a background agent, the runtime automatically creates a worktree for your session. You do not need to manage worktrees manually.

  • OpenAI Codex (app): Worktree creation is handled by the platform. Defer to its built-in worktree management.

  • All other environments (Cursor, Aider, Cline, Roo, generic CLI, etc.): No native worktree tool exists. Follow the manual instructions in this skill to create and manage worktrees using git commands directly.

Decision rule: If a native tool exists, use it. If not, follow this skill's instructions. Do not duplicate effort by running manual git worktree commands when a native tool has already handled isolation.

When to use

Use worktrees when you need parallel work, isolation, or want to avoid disrupting the user's current state. Common scenarios:

  • Working on a feature while reviewing a PR on another branch
  • Comparing branches side-by-side
  • Running risky refactors in isolation
  • Multi-agent coordination where each agent needs its own branch
  • Avoiding constant stash/pop cycles

For simple, single-branch tasks, working directly in the project root on the default branch is fine. Worktrees are for when you need more than one branch checked out at the same time.

The pattern

Quick reference

ActionCommand
Creategit worktree add .worktrees/<name> <name>
New branchgit worktree add .worktrees/<name> -b <name> origin/main
Listgit worktree list
Removegit worktree remove .worktrees/<name>
Prunegit worktree prune

Core structure

The project root is a normal git checkout on the default branch (main, master, etc.). Worktrees live in a .worktrees/ directory inside the project root:

project/                        # Normal checkout on default branch
├── .git/
├── .gitignore                  # Contains `.worktrees/`
├── .worktrees/
│   ├── feature/
│   │   └── auth/               # Worktree for feature/auth branch
│   │       ├── src/
│   │       ├── package.json
│   │       └── node_modules/
│   └── bugfix/
│       └── login-null/         # Worktree for bugfix/login-null branch
│           └── ...
├── src/                        # Root working files (default branch)
├── package.json
└── node_modules/

Key principles

  1. Root stays on default branch — The project root is a normal clone checked out to main (or whatever the default branch is). It is never a worktree itself.
  2. Name-path-branch identity — The worktree directory path under .worktrees/, the worktree name, and the branch name are all the same string. No prefixes, no slug transformations. feature/auth means directory .worktrees/feature/auth/ and branch feature/auth.
  3. Complete isolation — Each worktree has its own working files and needs its own dependency install (npm install, pip install, etc.) since node_modules, venv, and similar directories are per-worktree.
  4. Zero conflicts — Agents and humans work in parallel without file collisions.

First-time setup

Before creating the first worktree in a project, ensure .worktrees/ is excluded from version control:

  1. Check if .worktrees/ is already in .gitignore. If not, add it:

    echo '.worktrees/' >> .gitignore
  2. Commit the .gitignore change if needed.

That's it. No re-cloning, no bare-repo conversion. The project stays as-is.

Creating a worktree

Required information

  1. Branch name — The name for both the branch and the worktree directory (e.g., feature/user-auth, bugfix/login-issue, quick-fix)
  2. Base branch — Branch to create from if making a new branch (typically origin/main)
  3. Branch existence — Whether the branch already exists at origin or needs creation

Steps

  1. Ensure .worktrees/ is in .gitignore (see First-time setup above).

  2. Determine branch status:

    • Locally: git branch -l <name>
    • At origin: git ls-remote --heads origin <name>
  3. Create the worktree:

    • New branch: git worktree add .worktrees/<name> -b <name> origin/main
    • Existing branch: git worktree add .worktrees/<name> <name>

    git worktree add creates any intermediate directories automatically, so branch names with slashes (like feature/auth) work without extra steps.

  4. Install dependenciescd .worktrees/<name> then run npm install, pip install -r requirements.txt, or whatever the project requires. Each worktree needs its own install because dependency directories are not shared.

Naming examples

The worktree directory path, worktree name, and branch name are always the same string:

BranchWorktree Path
feature/auth.worktrees/feature/auth/
bugfix/login-null.worktrees/bugfix/login-null/
experiment/new-router.worktrees/experiment/new-router/
quick-fix.worktrees/quick-fix/

No renaming, no slug transformation, no prefixing.

Issue-backed branches

When the work corresponds to a tracked issue, use the pattern:

issue-<zero-padded-3-digit-number>-<slug>

Examples:

IssueBranchWorktree Path
#9issue-009-integrate.worktrees/issue-009-integrate/
#42issue-042-cache-headers.worktrees/issue-042-cache-headers/
#286issue-286-nested-fences.worktrees/issue-286-nested-fences/

Rules:

  • Zero-pad issue numbers to at least 3 digits so branches sort stably in alphabetical listings (issue-009 sorts before issue-042, not after).
  • The slug describes the work (a verb or short phrase), not the artifact. Prefer integrate, refactor, fix, audit over release names or version numbers (1.0.0 is what the PR produces, not what it is).
  • Name-path-branch identity still holds: the full string issue-009-integrate is the branch name, the worktree directory, and the identifier everywhere.

Working in a worktree

Once created, a worktree is a normal working directory. Run commands from inside it:

sh
cd .worktrees/feature/auth
# edit files, run tests, build, etc.
git add -A
git commit -m "Add auth middleware"
git push -u origin feature/auth

The root checkout and other worktrees are unaffected by changes in this worktree.

Merging back

When work in a worktree is complete and pushed:

  1. Create a pull request from the worktree's branch into the default branch, or merge directly if appropriate.
  2. Pull changes into root (if merged directly):
    sh
    cd /path/to/project   # back to project root
    git pull origin main
  3. Clean up the worktree (see below).

Removing a worktree

Required information

  1. Branch name — Name of the worktree/branch to remove
  2. Delete branch decision — Whether to delete the branch after removal
  3. Current location — Ensure you're not inside the worktree being removed

Steps

  1. Verify worktree exists by running git worktree list.

  2. Check current directory — verify you're not inside the worktree being removed (pwd). If you are, change to the project root first.

  3. Check for uncommitted work:

    • Uncommitted changes: git -C .worktrees/<name> status --porcelain
    • Unpushed commits: git log <name> --not --remotes --oneline
    • If either exists, warn the user before proceeding.
  4. Remove the worktree: git worktree remove .worktrees/<name>. Use --force only after user confirmation if there are uncommitted changes.

  5. Delete the branch (if requested):

    • Merged: git branch -d <name>
    • Unmerged (force): git branch -D <name>
    • Remote: git push origin --delete <name>
  6. Prune stale references: git worktree prune to clean up any stale worktree metadata.

  7. Verify cleanup:

    • git worktree list to confirm worktree removal
    • git branch -l <name> to confirm branch deletion

Note: <name> may contain slashes (e.g., feature/auth), which is normal. The nested directory structure under .worktrees/ is intentional.

Common issues

  • Modified files error: Commit/stash changes or use --force (loses changes)
  • "Branch not fully merged": Use -D instead of -d after user confirms
  • Remote deletion fails: May lack permissions or branch is protected
  • Parent directories left behind: After removing .worktrees/feature/auth/, the empty .worktrees/feature/ directory may remain. This is harmless but can be removed manually.

Cleanup rules

  • If a worktree has no changes or commits beyond the base, remove both the worktree and its branch automatically (or offer to).
  • If a worktree has changes or commits, prompt the user whether to keep or remove. Keeping preserves both directory and branch. Removing deletes both and discards uncommitted work.

Important rules

  • STOP and ASK if:
    • Branch name not specified
    • User hasn't specified whether to delete the branch
    • There are unpushed commits or uncommitted changes
    • Currently inside the worktree being removed
  • Never remove without checking current directory first
  • Warn about potential data loss before proceeding

Listing worktrees

Run git worktree list from anywhere in the repository to see all active worktrees and their branches:

/path/to/project                   abc1234 [main]
/path/to/project/.worktrees/feature/auth  def5678 [feature/auth]
/path/to/project/.worktrees/quick-fix     ghi9012 [quick-fix]

To prune stale references (worktrees whose directories were deleted without git worktree remove): git worktree prune.

Trade-offs

ApproachProsCons
WorktreesFull isolation, no switchingDisk space, dependency copies
Branch switchSingle working directoryStash/commit needed to switch
StashingQuick context switchEasy to lose or forget stash