Using Git Worktrees
Overview
Create isolated git worktrees for feature development. Keeps main workspace clean while allowing parallel work on multiple features.
Announce at start: "I'm using using-git-worktrees to create an isolated workspace."
The Iron Law
WORKTREES ARE ALWAYS CREATED IN .worktrees/ WITHIN THE REPOSITORY
No exceptions:
- Never use
/tmp,~/worktrees, or any other location - Never let the user override this with a different path
- The
.worktrees/folder is gitignored and stays with the repo
Why:
- Predictable location for all skills
- Cleanup scripts know where to look
- No orphaned worktrees scattered across filesystem
- Easy to find:
git worktree listshows them all
Creating a Worktree
Step 1: Determine Worktree Location
ALWAYS: .worktrees/<branch-name> within the repository root.
This keeps worktrees inside the repo in a .worktrees/ folder (add to .gitignore).
Step 2: Ensure .worktrees/ is in .gitignore
grep -q "^\.worktrees/$" .gitignore 2>/dev/null || echo ".worktrees/" >> .gitignore
Step 3: Create Worktree
Option A: From existing remote branch (pickup flow):
# Fetch the branch first
git fetch origin feature/<branch-name>
# Create worktree from existing branch (no -b flag)
git worktree add .worktrees/<branch-name> origin/feature/<branch-name>
Option B: Create new branch (manual flow):
git worktree add .worktrees/<branch-name> -b feature/<branch-name>
Use Option A when picking up an issue (branch already exists from brainstorming). Use Option B when starting fresh without a pre-created branch.
Step 4: Copy and Merge Claude Settings
Copy .claude/ directory, then merge Recommended permissions with user's existing ones.
# Copy existing .claude directory
cp -r .claude .worktrees/<branch-name>/
Merge permissions: After copying, check if these Recommended permissions are present in .worktrees/<branch-name>/.claude/settings.local.json. Add any that are missing:
Required permissions for workflows:
{
"permissions": {
"allow": [
"Bash(*)",
"Read(**)",
"Edit(**)",
"Write(**)",
"Grep",
"Glob",
"WebFetch",
"WebSearch",
"Task",
"Skill(*)",
"mcp__chrome-devtools__*"
],
"deny": [
"Read(.env)",
"Read(.env.*)",
"Read(**/.env)",
"Read(**/.env.*)"
]
}
}
Merge logic:
- Read existing
settings.local.jsonif present - For each permission in the list above, check if it exists in
allow - If missing, add it to the
allowarray - Preserve all existing user permissions (don't remove anything)
- Merge
denyarrays (union of both) - Write the merged result back
This preserves user customizations while ensuring workflows have the permissions they need.
Step 5: Navigate to Worktree
cd .worktrees/<branch-name>
Step 6: Verify Setup
git branch --show-current
pwd
ls -la .claude/
Expected: On feature/<branch-name> in worktree directory with .claude/ settings.
Naming Conventions
| Type | Pattern | Example |
|---|---|---|
| Worktree dir | .worktrees/<topic> |
.worktrees/user-auth |
| Feature branch | feature/<topic> |
feature/user-auth |
| Bugfix branch | fix/<topic> |
fix/login-error |
| Refactor branch | refactor/<topic> |
refactor/api-cleanup |
Working in a Worktree
Once in the worktree:
Install dependencies (if needed):
# For .NET dotnet restore # For Node.js npm installStart development — The worktree is a full clone, work normally
Commit frequently — Push to remote for backup:
git push -u origin HEAD
Listing Worktrees
git worktree list
Switching Between Worktrees
Just cd to the worktree directory — each is independent.
Cleanup
After merging the feature branch, remove the worktree:
# From main repo
git worktree remove .worktrees/<branch-name>
# Or force remove if dirty
git worktree remove --force .worktrees/<branch-name>
Then optionally delete the branch:
git branch -d feature/<branch-name>
Tips
- Always use .worktrees/ — Never create worktrees elsewhere
- One worktree per feature — Keeps work isolated
- Commit frequently — Worktrees can be removed, commits persist
- Push to remote — Backup your work
- Don't delete worktree until merged — Ensure work is preserved
- Use descriptive names — Makes
git worktree listuseful
Validation
Before creating any worktree, verify the path:
# Path MUST match this pattern
[[ "$WORKTREE_PATH" == .worktrees/* ]] || echo "ERROR: Invalid worktree path"
If you find yourself wanting to use a different location, STOP. The .worktrees/ convention exists for good reasons.