The Problem
Why worktrees exist
Without worktrees
Working on feature-auth…
Urgent bug comes in
git stash
git checkout fix/bug
Fix the bug, commit, push
git checkout feature-auth
git stash pop
Lost context. Where was I?
With worktrees
Terminal 1
~/app-feature-auth/
Working…
Still working…
Never interrupted
Terminal 2
~/app-fix-bug/
Fix bug
Commit & push
Done
Architecture
How worktrees connect to your repo
my-project/
main
Main worktree (original clone)
.git/ directory
commits · branches · tags
remotes · stashes
the real database lives here
points to
my-project-auth/
feature-auth
Linked worktree
.git ← tiny file, not a directory
points to
my-project-pay/
feature-payments
Linked worktree
.git ← tiny file, not a directory
Boundaries
What's shared vs. what's isolated
Shared across all worktrees
  • Git commit history
  • Branches & tags
  • Remote connections (origin, etc.)
  • Stashes
  • Git hooks
  • Git config
Isolated per worktree
  • Working directory files
  • Checked-out branch
  • Staged changes (index)
  • node_modules / vendor / deps
  • .env and config files
  • Build artifacts / caches
Workflow
Step-by-step lifecycle
1
Create worktrees
# From your main repo git worktree add ../app-feature -b feature-auth git worktree add ../app-bugfix -b fix/crash
2
Set up each environment
# Copy env, install deps cd ../app-feature cp ../my-app/.env .env npm install # or go mod download, etc.
3
Work in parallel
Open separate terminals. Run Claude Code, your editor, or dev servers independently in each worktree. They won't interfere.
# Terminal 1 # Terminal 2 cd ../app-feature cd ../app-bugfix claude claude
4
Push & create PRs
git add . && git commit -m "feat: auth flow" git push origin feature-auth gh pr create
5
Clean up after merge
# Back in main repo cd ~/projects/my-app git pull git worktree remove ../app-feature git branch -d feature-auth
Reference
Command cheatsheet
Create
git worktree add <path> -b <branch> New worktree + new branch
git worktree add <path> <branch> New worktree from existing branch
git worktree add <path> -b <br> origin/main New branch from a specific start point
Manage
git worktree list Show all worktrees and their branches
git worktree remove <path> Delete a worktree (must be clean)
git worktree remove --force <path> Force delete (discard changes)
Maintain
git worktree prune Clean up stale references
git worktree move <old> <new> Move worktree to new location
git worktree lock <path> Prevent accidental pruning
Sync
git fetch origin Fetch latest from remote
git rebase origin/main Rebase worktree branch onto main
git worktree list | grep <branch> Check if a branch is in use
Automation
Shell helper functions
Drop these into your ~/.zshrc to create and remove worktrees with a single command. They wrap the manual steps from the Workflow tab into reusable functions.
wt-add

Create a worktree as a sibling directory with a new branch.

# Usage: wt-add feature-auth
wt-add() {
  local branch="$1"
  if [[ -z "$branch" ]]; then
    echo "Usage: wt-add <branch-name>" >&2
    return 1
  fi

  # Get repo root name for the sibling path
  local repo
  repo="$(basename "$(git rev-parse --show-toplevel)")"
  local target="../${repo}-${branch}"

  git worktree add "$target" -b "$branch" \
    && cd "$target" \
    && echo "Worktree ready → $target"
}
wt-rm

Remove a worktree and delete its branch.

# Usage: wt-rm feature-auth
wt-rm() {
  local branch="$1"
  if [[ -z "$branch" ]]; then
    echo "Usage: wt-rm <branch-name>" >&2
    return 1
  fi

  # Resolve the worktree path from branch name
  local repo
  repo="$(basename "$(git rev-parse --show-toplevel)")"
  local target="../${repo}-${branch}"

  # Return to main worktree if inside the one being removed
  if [[ "$(pwd)" == "$(cd "$target" && pwd)" ]]; then
    cd "$(git rev-parse --show-toplevel)"
  fi

  git worktree remove "$target" || return 1

  if git branch -d "$branch" 2>/dev/null; then
    echo "Removed worktree + branch: $branch"
  else
    echo "Worktree removed. Branch '$branch' has unmerged commits."
    echo "  Force delete: git branch -D $branch"
  fi
}
How it works: wt-add feature-auth creates ../my-project-feature-auth/ as a sibling of your repo and checks out a new branch. wt-rm feature-auth removes the worktree and deletes the local branch. If the branch has unmerged commits, it tells you how to force-delete.
Constraints
Rules to remember
One branch per worktree. Can't have the same branch checked out in two places.
Keep worktrees as siblings. Don't nest them inside each other or inside the main repo.
Dependencies aren't shared. Run install commands in each new worktree.
.env files are untracked. Copy them manually or script the setup.
Main worktree is special. You can't remove the original clone directory.
Stashes are global. They live in the shared .git and are visible everywhere.

Related Topics