You're knee-deep in a feature branch when urgent production fire hits. Instead of stashing, switching, and losing your mental context, what if you could just... open another folder and work on the hotfix immediately? Git worktrees make this possible, and once you experience the workflow, you'll wonder how you ever lived without them.
What Are Git Worktrees?
Git worktrees let you check out multiple branches of the same repository into separate directories simultaneously. Think of it as having multiple working copies of your repo, each locked to a different branch, all sharing the same .git
folder and commit history.
Here's the mental shift: instead of switching branches in one directory, you switch directories for different branches.
The difference is dramatic: traditional workflow forces sequential context switching, while worktrees enable parallel development.
Why Worktrees Change Everything
No More Stash Juggling
You know the drill: you're in flow state, deep in complex changes, when urgent work appears. With traditional git, you stash (hoping you don't forget what you were doing), switch branches, work, switch back, unstash, and spend 10 minutes remembering where you left off.
Worktrees eliminate this entirely. Your feature work stays exactly as you left it, in its own directory.
IDE State Preservation
Your IDE doesn't lose track of open files, breakpoints, or terminal sessions when you switch between worktree directories. Each branch maintains its own IDE workspace state.
Parallel Development
Need to compare implementations across branches? Open both directories in separate IDE windows. Want to copy a function from one branch to another? Simple file copy. Testing how your feature branch handles different scenarios? Keep multiple environments running simultaneously.
Setting Up Your First Worktree
Let's walk through the practical setup. I'll use a typical web app scenario where you maintain main, develop features, and handle production hotfixes.
Initial Repository Setup
Start with your existing repository:
cd ~/projects/myapp
git status # confirm you're in the main branch and clean
Creating Worktrees
Here's the playbook I'd run for a typical multi-branch workflow:
# Create a worktree for your main branch (if not already there)
git worktree add ../myapp-main main
# Create worktree for a new feature branch
git worktree add ../myapp-auth -b feature-auth
# Create worktree for hotfix work
git worktree add ../myapp-hotfix -b hotfix-critical-bug
# Create worktree for an existing remote branch
git worktree add ../myapp-staging origin/staging
Your directory structure now looks like:
~/projects/
├── myapp/ # original clone
├── myapp-main/ # main branch worktree
├── myapp-auth/ # feature-auth branch worktree
├── myapp-hotfix/ # hotfix-critical-bug branch worktree
└── myapp-staging/ # staging branch worktree
Worktree Management Commands
# List all worktrees
git worktree list
# Remove a worktree (deletes the directory)
git worktree remove ../myapp-hotfix
# Remove worktree reference (if you manually deleted the directory)
git worktree prune
# Move a worktree to different location
git worktree move ../myapp-auth ../features/myapp-auth
Real-World Scenarios
Scenario 1: The Production Emergency
You're implementing OAuth integration when a critical security vulnerability needs immediate patching.
Your OAuth work remains untouched, IDE state preserved, no mental context lost.
Scenario 2: Testing Across Environments
You need to verify your feature works with the current production state:
# Keep feature development going in myapp-auth
# Test integration in production-like environment
cd ../myapp-main
git pull origin main # Get latest production state
npm start # Run production version
# In another terminal
cd ../myapp-auth
npm run dev # Run feature version
# Compare behaviors, check for conflicts, verify compatibility
Scenario 3: Code Review Workflow
Someone requests changes on your PR while you're already working on the next feature:
# Current work in myapp-next-feature
# PR feedback for feature-auth branch
cd ../myapp-auth
# Address review comments
git add .
git commit -m "refactor: address PR feedback"
git push
# Immediately back to new feature work
cd ../myapp-next-feature
Advanced Worktree Strategies
The Branch-Per-Directory Pattern
Organize worktrees by purpose rather than branch name:
~/projects/myapp/
├── current/ # Your main development branch
├── production/ # Production/main branch
├── staging/ # Staging branch
├── feature/ # Current feature work
├── hotfix/ # Emergency fixes
└── experiment/ # Experimental branches
Shared Configuration
Worktrees share the same .git
directory, which means:
This is powerful for consistency but requires awareness for hooks and config that affect working directory state.
Common Gotchas and Solutions
Issue: Accidentally Committing to Wrong Branch
Problem: You forgot which worktree you're in and accidentally committed to the wrong branch.
Solution: Make your current branch and worktree obvious in your terminal or editor:
- Customize your shell prompt to display the current branch (most modern shells and plugins support this).
- Use directory naming conventions that include the branch or purpose.
- Many IDEs and editors (like VS Code, JetBrains, etc.) show the current branch in the status bar—keep an eye on it before committing.
- Consider using tools like
git status
orgit branch --show-current
to double-check your context before making changes. - For extra safety, set up pre-commit hooks that warn you if you're about to commit to a protected or unexpected branch.
Issue: Worktree Directory Confusion
Problem: Too many similar directory names cause confusion.
Solution: Use descriptive names and consistent patterns:
# Instead of
myapp-feature-auth
myapp-feature-billing
myapp-hotfix-security
# Use
myapp-auth-feature
myapp-billing-feature
myapp-security-hotfix
Issue: Large Repository Performance
Problem: Multiple worktrees of large repositories consume disk space.
Solution: Use sparse-checkout for focused worktrees:
git worktree add ../myapp-frontend -b frontend-only
cd ../myapp-frontend
git sparse-checkout init --cone
git sparse-checkout set frontend/ shared/
Workflow Optimization Tips
1. IDE Project Templates
Create VS Code workspace files for common worktree combinations:
// myapp-dev.code-workspace
{
"folders": [
{ "path": "./myapp-main" },
{ "path": "./myapp-feature" },
{ "path": "./myapp-staging" }
],
"settings": {
"git.defaultCloneDirectory": "./worktrees"
}
}
2. Automated Worktree Setup
Script common worktree patterns:
- Powershell
- bash
# setup-feature-work.ps1
param([string]$FeatureName)
git worktree add "../myapp-$FeatureName" -b "feature-$FeatureName"
code "../myapp-$FeatureName"
Set-Location "../myapp-$FeatureName"
# setup-feature-work.bash
FEATURE_NAME="$1"
git worktree add "../myapp-$FEATURE_NAME" -b "feature-$FEATURE_NAME"
code "../myapp-$FEATURE_NAME"
cd "../myapp-$FEATURE_NAME"
3. Cleanup Automation
- Powershell
- bash
# clean-merged-worktrees.ps1
git worktree list --porcelain |
Select-String "branch refs/heads/" |
ForEach-Object {
$branch = ($_ -split "/")[-1]
if (git branch --merged main | Select-String $branch) {
Write-Host "Removing merged worktree: $branch"
git worktree remove "../myapp-$branch" --force
}
}
# clean-merged-worktrees.bash
# List all worktrees and extract branch names
git worktree list --porcelain | \
grep "branch refs/heads/" | \
awk -F'/' '{print $NF}' | while read branch; do
# Check if branch is merged into main
if git branch --merged main | grep -qw "$branch"; then
echo "Removing merged worktree: $branch"
git worktree remove "../myapp-$branch" --force
fi
done
When NOT to Use Worktrees
Worktrees aren't always the answer:
- Simple, linear workflows: If you rarely work on multiple branches simultaneously
- Disk space constraints: Each worktree requires full working directory space
- Team unfamiliarity: If your team isn't comfortable with the concept
- Complex build systems: Some build tools don't handle multiple working directories well
The Bottom Line
Git worktrees transform your development workflow from sequential branch switching to parallel branch development. You maintain mental context, preserve IDE state, and eliminate the friction of constant stashing and switching.
Here's the playbook I'd run for getting started:
- Start small: Create one additional worktree for your most common secondary task (hotfixes, code review, etc.)
- Establish naming conventions: Consistent directory naming prevents confusion
- Integrate with your shell: Add branch info to prompts and create helper functions
- Build cleanup habits: Regularly remove completed worktrees to avoid clutter
The mental shift takes a few days, but once worktrees become part of your muscle memory, you'll find yourself significantly more productive and less frustrated with context switching.
Your future self will thank you for making this change.