Skip to content

Git Worktrees

This guide covers using Git worktrees to work on multiple unrelated features in parallel without branch switching or stashing.

What Are Worktrees?

A Git worktree is a linked working directory connected to your repository. Each worktree can have a different branch checked out, allowing you to:

  • Work on two unrelated features simultaneously
  • Keep separate IDE windows for each feature
  • Avoid constant git stash / git checkout cycles
  • Run different branches side-by-side for comparison
# Traditional workflow (single directory)
podcaster-plus-app/          # switch branches constantly
  └── .git/

# With worktrees (multiple directories)
podcaster-plus-app/          # main branch
  └── .git/
worktrees/
  ├── feat-rbac/             # feature branch 1
  └── feat-guest-network/    # feature branch 2

Quick Reference

bash
# Create a worktree for an existing branch
git worktree add ../worktrees/feat-name existing-branch-name

# Create a worktree with a new branch (based on main)
git worktree add ../worktrees/feat-name -b feat/new-feature main

# List all worktrees
git worktree list

# Remove a worktree
git worktree remove ../worktrees/feat-name

# Clean up stale references
git worktree prune

Workflow

1. Create a Worktree

From your main repository:

bash
cd ~/Documents/GitHub/podcaster-plus-app

# For an existing branch
git worktree add ../worktrees/rbac feat/RBAC-and-access-control

# For a new branch (branches from main)
git worktree add ../worktrees/guest-network -b feat/guest-network main

2. Set Up the Worktree

Each worktree needs its own dependencies:

bash
cd ../worktrees/rbac
pnpm install
cp ../podcaster-plus-app/.env .env  # Copy environment file

3. Open in Your Editor

Open the worktree directory in a new editor window:

  • Cursor/VS Code: File → Open Folder → select worktree directory
  • Terminal: Each terminal session can be in a different worktree

4. Work Normally

All standard Git operations work within each worktree:

bash
# In worktree directory
git status          # Shows status for this branch
git add .
git commit -m "feat: add RBAC middleware"
git push origin feat/RBAC-and-access-control

5. Create PR and Merge

Create pull requests as normal. After merging:

bash
# From any directory
git worktree remove ../worktrees/rbac

# Delete the branch (if not auto-deleted)
git branch -d feat/RBAC-and-access-control

# Clean up any stale references
git worktree prune

Handling Merge Conflicts

Updating Your Worktree from Main

When main has changes you need:

bash
cd ../worktrees/feat-rbac

# Fetch latest
git fetch origin

# Option 1: Merge main into your branch
git merge origin/main

# Option 2: Rebase onto main (cleaner history)
git rebase origin/main

Resolving Conflicts

Conflicts are resolved the same as normal Git workflows:

bash
# After conflict occurs
git status                    # See conflicted files

# Edit files to resolve conflicts
code path/to/conflicted-file

# Mark as resolved
git add path/to/conflicted-file

# Continue merge or rebase
git merge --continue
# or
git rebase --continue

Conflict Between Worktrees

If two worktrees modify the same files, conflicts happen when the second branch merges to main. This is normal Git behavior - worktrees don't prevent this, they just let you work in parallel.

Best practice: Coordinate with the team on who's working on what files.

Project-Specific Considerations

Node Modules

Each worktree needs its own node_modules directory. After creating a worktree:

bash
cd ../worktrees/new-feature
pnpm install

Disk Space

Each worktree with node_modules uses ~500MB. Clean up worktrees you're not actively using.

Environment Files

Copy .env from your main directory:

bash
cp ../podcaster-plus-app/.env .env

Or create a symlink (shared config):

bash
ln -s ../podcaster-plus-app/.env .env

Supabase Local Development

All worktrees share the same local Supabase instance if you're running one. This is usually fine, but be aware:

  • Migrations: Running migrations in one worktree affects all
  • Data: Test data is shared across worktrees

For isolated testing, use the remote Supabase project instead.

TypeScript and IDE

Each worktree is independent - your IDE sees them as separate projects. Benefits:

  • Separate TypeScript servers (no interference)
  • Independent ESLint/Prettier configs
  • Isolated terminal sessions

Best Practices

Naming Convention

Use short, descriptive names for worktree directories:

bash
# Good - short and clear
../worktrees/rbac
../worktrees/guest-network
../worktrees/fix-booking-bug

# Avoid - too long
../worktrees/feat-RBAC-and-access-control-implementation

Directory Structure

Keep worktrees in a dedicated directory:

~/Documents/GitHub/
├── podcaster-plus-app/      # Main repo (usually on main)
└── worktrees/               # All worktrees here
    ├── rbac/
    ├── guest-network/
    └── hotfix-123/

Limit Active Worktrees

Maintain 2-3 active worktrees maximum:

  • Reduces disk space usage
  • Prevents context overload
  • Easier to track what you're working on

Clean Up After PRs

Remove worktrees immediately after PR merge:

bash
# List current worktrees
git worktree list

# Remove merged feature
git worktree remove ../worktrees/completed-feature

# Prune stale references
git worktree prune

Common Commands

TaskCommand
List worktreesgit worktree list
Create (existing branch)git worktree add <path> <branch>
Create (new branch)git worktree add <path> -b <new-branch> <base>
Remove worktreegit worktree remove <path>
Force removegit worktree remove --force <path>
Clean stale refsgit worktree prune
Move worktreegit worktree move <old-path> <new-path>

Shell Aliases (Optional)

Add to ~/.zshrc for convenience:

bash
# Git worktree shortcuts
alias gwl="git worktree list"
alias gwp="git worktree prune"

# Create worktree with automatic pnpm install
gwa() {
  local name="$1"
  local branch="${2:-$1}"
  local base="${3:-main}"
  local worktree_dir="../worktrees/$name"

  if [ -z "$name" ]; then
    echo "Usage: gwa <name> [branch] [base]"
    return 1
  fi

  # Check if branch exists
  if git show-ref --verify --quiet "refs/heads/$branch"; then
    git worktree add "$worktree_dir" "$branch"
  else
    git worktree add -b "$branch" "$worktree_dir" "$base"
  fi

  # Install dependencies
  cd "$worktree_dir" && pnpm install

  echo "Worktree ready at: $worktree_dir"
}

# Remove worktree and optionally delete branch
gwr() {
  local name="$1"
  local worktree_dir="../worktrees/$name"

  if [ -z "$name" ]; then
    echo "Usage: gwr <name>"
    git worktree list
    return 1
  fi

  git worktree remove "$worktree_dir"
  git worktree prune
  echo "Removed worktree: $name"
}

After adding, reload your shell:

bash
source ~/.zshrc

Troubleshooting

"fatal: is already checked out"

A branch can only be checked out in one worktree at a time:

bash
# Find where it's checked out
git worktree list

# Either remove that worktree or use a different branch

Worktree Shows Wrong Branch

If the worktree state seems wrong:

bash
cd ../worktrees/problematic
git status
git checkout <correct-branch>  # If needed

"worktree ... is not a valid path"

The worktree directory was deleted outside Git:

bash
git worktree prune  # Cleans up references to deleted directories

IDE Not Recognizing Git

Ensure your IDE is opened at the worktree root, not a subdirectory. The worktree has a .git file (not folder) that points to the main repo.

When NOT to Use Worktrees

  • Small fixes: For quick one-file changes, just commit and switch
  • Dependent features: If feature B depends on feature A, work sequentially
  • Low disk space: Each worktree with node_modules uses significant space

Summary

Git worktrees enable true parallel development. The workflow is:

  1. Create worktree for your feature branch
  2. Install dependencies and copy .env
  3. Work in separate IDE window
  4. Merge via normal PR process
  5. Clean up worktree after merge

Keep worktrees organized, clean up promptly, and coordinate with team members on shared files.

Internal documentation - Not for public distribution