Git worktree is a hidden gem that lets you check out multiple branches simultaneously in separate directories. Instead of constantly switching branches, stashing changes, and losing context, you can have multiple working directories from the same repository.
Understanding Git Worktree
Traditional Git workflow forces you to work on one branch at a time. Git worktree breaks this limitation by creating additional working directories linked to your repository:
my-project/ # Main worktree (main branch)
my-project-feature/ # Additional worktree (feature branch)
my-project-hotfix/ # Additional worktree (hotfix branch)
Each directory is a fully functional working tree with its own index and HEAD.
Basic Worktree Operations
Creating a Worktree
# Add a new worktree for existing branch
git worktree add ../project-feature feature-branch
# Create new branch and worktree
git worktree add -b new-feature ../project-new-feature
# Create worktree in specific location
git worktree add /tmp/emergency-fix hotfix-branch
Listing Worktrees
# List all worktrees
$ git worktree list
/Users/dev/project abc1234 [main]
/Users/dev/project-feature def5678 [feature-branch]
/Users/dev/project-hotfix ghi9012 [hotfix-branch]
# Verbose listing
git worktree list --verbose
Removing Worktrees
# Remove a worktree
git worktree remove ../project-feature
# Force remove (if there are uncommitted changes)
git worktree remove --force ../project-feature
# Clean up stale worktrees
git worktree prune
Practical Use Cases
1. Parallel Feature Development
Work on multiple features without context switching:
# Main repository for stable development
cd ~/projects/app
git checkout main
# Feature A in separate directory
git worktree add ../app-feature-a -b feature/payment-integration
# Feature B in another directory
git worktree add ../app-feature-b -b feature/user-dashboard
# Work on features independently
cd ../app-feature-a
npm install
npm run dev # Port 3000
cd ../app-feature-b
npm install
npm run dev -- --port 3001 # Different port
2. Emergency Hotfixes
Handle urgent fixes without disrupting current work:
# Working on a feature
cd ~/project-feature
# ... in the middle of complex changes ...
# Emergency! Production bug reported
# No need to stash, just create new worktree
git worktree add ~/project-hotfix -b hotfix/critical-bug origin/main
cd ~/project-hotfix
# Fix the bug
git add .
git commit -m "Fix: Critical production issue"
git push origin hotfix/critical-bug
# Return to feature work - exactly where you left off
cd ~/project-feature
3. Code Review Workflow
Review PRs without disrupting your work:
#!/bin/bash
# review-pr.sh - Script to review pull requests
PR_NUMBER=$1
BRANCH_NAME="pr-$PR_NUMBER"
# Fetch PR branch
git fetch origin pull/$PR_NUMBER/head:$BRANCH_NAME
# Create worktree for review
git worktree add ../project-review-$PR_NUMBER $BRANCH_NAME
echo "PR #$PR_NUMBER ready for review in ../project-review-$PR_NUMBER"
cd ../project-review-$PR_NUMBER
# Run tests
npm install
npm test
# After review, cleanup
cd ..
git worktree remove project-review-$PR_NUMBER
git branch -D $BRANCH_NAME
4. Testing Multiple Versions
Test different versions simultaneously:
# Test current release
git worktree add ../app-v2.0 tags/v2.0.0
cd ../app-v2.0
npm test
# Test previous release
git worktree add ../app-v1.9 tags/v1.9.0
cd ../app-v1.9
npm test
# Compare performance
echo "Running benchmark on v2.0..."
cd ../app-v2.0 && npm run benchmark
echo "Running benchmark on v1.9..."
cd ../app-v1.9 && npm run benchmark
Advanced Worktree Patterns
Worktree Configuration
# Set up worktree-specific config
cd ~/project-feature
git config user.email "feature-test@example.com"
# This config only applies to this worktree
# Main worktree keeps its own configuration
Automated Worktree Management
#!/bin/bash
# worktree-manager.sh
create_feature_worktree() {
FEATURE_NAME=$1
WORKTREE_DIR="../project-$FEATURE_NAME"
if [ -d "$WORKTREE_DIR" ]; then
echo "Worktree already exists: $WORKTREE_DIR"
return 1
fi
git worktree add -b "feature/$FEATURE_NAME" "$WORKTREE_DIR"
cd "$WORKTREE_DIR"
# Setup environment
npm install
cp ../.env.example .env
echo "Worktree created: $WORKTREE_DIR"
echo "Branch: feature/$FEATURE_NAME"
}
cleanup_old_worktrees() {
# Remove worktrees older than 30 days
git worktree list --porcelain | grep worktree | cut -d' ' -f2 | while read dir; do
if [ ! -d "$dir" ]; then
git worktree prune
fi
done
}
Worktree for Different Environments
# Production debugging
git worktree add ../app-prod production
# Staging environment
git worktree add ../app-staging staging
# Development environment
git worktree add ../app-dev develop
# Each can run with different configs
cd ../app-prod && npm start -- --env=production
cd ../app-staging && npm start -- --env=staging
cd ../app-dev && npm start -- --env=development
Worktree Best Practices
1. Naming Conventions
# Use consistent naming patterns
git worktree add ../project-feature-name feature/name
git worktree add ../project-bugfix-issue bugfix/issue
git worktree add ../project-release-2.0 release/2.0
2. Location Strategy
# Option 1: Sibling directories
project/
project-feature/
project-hotfix/
# Option 2: Subdirectory
project/
project-worktrees/
├── feature/
├── hotfix/
└── release/
# Option 3: Temporary location for short-lived work
/tmp/project-quick-fix/
3. Cleanup Script
#!/bin/bash
# cleanup-worktrees.sh
echo "Current worktrees:"
git worktree list
# Find and remove orphaned worktrees
git worktree prune -v
# Interactive cleanup
git worktree list | tail -n +2 | while read -r line; do
dir=$(echo $line | awk '{print $1}')
branch=$(echo $line | awk '{print $3}' | tr -d '[]')
read -p "Remove worktree $dir [$branch]? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
git worktree remove "$dir"
fi
done
Integration with Development Tools
VS Code Multi-Window Setup
#!/bin/bash
# open-worktrees-vscode.sh
# Open each worktree in separate VS Code window
git worktree list | while read -r line; do
dir=$(echo $line | awk '{print $1}')
code "$dir"
done
Docker Development
# docker-compose.yml for worktree
version: '3'
services:
app-main:
build: .
volumes:
- ./:/app
ports:
- "3000:3000"
app-feature:
build: ../project-feature
volumes:
- ../project-feature:/app
ports:
- "3001:3000"
tmux Session Management
#!/bin/bash
# tmux-worktrees.sh
# Create tmux session for each worktree
git worktree list | while read -r line; do
dir=$(echo $line | awk '{print $1}')
branch=$(echo $line | awk '{print $3}' | tr -d '[]')
session_name="wt-$branch"
tmux new-session -d -s "$session_name" -c "$dir"
tmux send-keys -t "$session_name" "npm run dev" Enter
done
# Attach to main
tmux attach -t wt-main
Common Pitfalls and Solutions
1. Submodule Complications
# Worktrees with submodules need special handling
git worktree add ../project-with-submodules feature-branch
cd ../project-with-submodules
git submodule update --init --recursive
2. Large File Issues
# For repos with large files, consider shallow worktrees
git worktree add --no-checkout ../project-quick-look feature-branch
cd ../project-quick-look
git sparse-checkout init
git sparse-checkout set src/ tests/
git checkout
3. Branch Locking
# Can't checkout same branch in multiple worktrees
$ git worktree add ../another-main main
fatal: 'main' is already checked out at '/path/to/project'
# Solution: Create a tracking branch
git branch main-copy main
git worktree add ../another-main main-copy
Performance Considerations
Shared Repository Objects
# All worktrees share the same .git directory
# Check object database size
du -sh .git/objects
# Pack objects for better performance
git gc --aggressive
# This benefits all worktrees
Worktree-Specific Performance
# Each worktree has its own index
# Optimize individually if needed
cd ../project-feature
git update-index --refresh
git status # Warms up file system cache
Scripting and Automation
Feature Development Script
#!/bin/bash
# feature-dev.sh
FEATURE_NAME=$1
BASE_BRANCH=${2:-main}
if [ -z "$FEATURE_NAME" ]; then
echo "Usage: $0 <feature-name> [base-branch]"
exit 1
fi
WORKTREE_DIR="../project-$FEATURE_NAME"
BRANCH_NAME="feature/$FEATURE_NAME"
# Create worktree
git worktree add -b "$BRANCH_NAME" "$WORKTREE_DIR" "$BASE_BRANCH"
# Setup development environment
cd "$WORKTREE_DIR"
npm install
cp ../.env.development .env
echo "PORT=30$(echo $FEATURE_NAME | wc -c)" >> .env
# Create feature documentation
cat > FEATURE.md << EOF
# Feature: $FEATURE_NAME
Base: $BASE_BRANCH
Created: $(date)
## Description
TODO: Add feature description
## Tasks
- [ ] Implementation
- [ ] Tests
- [ ] Documentation
EOF
echo "Feature worktree ready at: $WORKTREE_DIR"
echo "Branch: $BRANCH_NAME"
echo "Start development with: cd $WORKTREE_DIR && npm run dev"
Parallel Testing Script
#!/bin/bash
# parallel-test.sh
# Run tests in all worktrees simultaneously
git worktree list | while read -r line; do
dir=$(echo $line | awk '{print $1}')
branch=$(echo $line | awk '{print $3}' | tr -d '[]')
echo "Testing $branch in $dir"
(cd "$dir" && npm test) &
done
# Wait for all tests to complete
wait
echo "All tests completed"
Conclusion
Git worktree is a game-changer for developers who juggle multiple tasks. It eliminates the friction of branch switching, enables true parallel development, and makes complex workflows manageable.
Whether you're handling emergency fixes, reviewing code, or developing multiple features, worktrees let you maintain multiple working states without the overhead of cloning repositories or complex stashing strategies.
Start with simple use cases like code reviews and emergency fixes, then gradually incorporate worktrees into your daily workflow. Once you experience the freedom of having multiple branches checked out simultaneously, you'll wonder how you ever lived without it.
Share this article
David Childs
Consulting Systems Engineer with over 10 years of experience building scalable infrastructure and helping organizations optimize their technology stack.