Git worktrees allow you to check out multiple branches at the same time in different directories, all sharing the same repository.
What Are Worktrees?
A worktree is an additional working directory linked to the same repository. Instead of switching branches and losing your current state, you can work on multiple branches in parallel.
Traditional workflow:
# Work on feature
git checkout feature
# ... work work work ...
# Urgent fix needed!
git stash
git checkout main
# Fix, commit
git checkout feature
git stash pop
With worktrees:
# Work on feature in main directory
# ... work work work ...
# Urgent fix needed!
cd ../main-worktree
# Fix, commit
cd ../feature-worktree
# Continue working
Repository Structure
A repository with worktrees:
project/ # Main worktree
├── .git/
│ ├── worktrees/
│ │ ├── feature/ # Linked worktree metadata
│ │ └── hotfix/ # Linked worktree metadata
│ └── ...
└── src/
../project-feature/ # Linked worktree
└── .git # File pointing to main .git
../project-hotfix/ # Linked worktree
└── .git # File pointing to main .git
Basic Operations
Creating a Worktree
Create a new worktree for an existing branch:
git worktree add <path> <branch>
Example:
# Create worktree for feature branch
git worktree add ../project-feature feature-branch
Creating a Worktree with a New Branch
git worktree add -b <new-branch> <path> <base-branch>
Example:
# Create new branch and worktree from main
git worktree add -b new-feature ../project-new-feature main
Creating a Worktree Without Branch Name
Git infers the branch name from the directory:
# Creates and checks out branch 'bugfix-123'
git worktree add ../bugfix-123
Listing Worktrees
Output:
/path/to/project abc123 [main]
/path/to/project-feature def456 [feature-branch]
/path/to/project-hotfix ghi789 [hotfix]
Verbose output:
git worktree list --verbose
Removing a Worktree
git worktree remove <path>
git worktree remove ../project-feature
Or manually delete and prune
rm -rf ../project-feature
git worktree prune
Advanced Usage
Detached HEAD Worktree
Create a worktree at a specific commit:
git worktree add --detach <path> <commit>
Example:
git worktree add --detach ../test-commit abc123
Orphan Worktree
Create a worktree with no commit history:
git worktree add --orphan -b <branch> <path>
Useful for GitHub Pages or separate documentation.
Force Creation
Override safety checks:
# Force create even if branch is checked out elsewhere
git worktree add --force <path> <branch>
# Force twice to override a locked worktree
git worktree add --force --force <path> <branch>
Locking Worktrees
Prevent a worktree from being pruned:
# Lock a worktree
git worktree lock <path> --reason "Working on portable drive"
# Unlock
git worktree unlock <path>
Common Workflows
Emergency Hotfix
You’re deep in feature work when a critical bug is reported:
git worktree add -b hotfix ../project-hotfix main
Switch to hotfix directory
# Fix code
git commit -am "Fix critical bug"
git push origin hotfix
cd ../project
# Continue where you left off
git worktree remove ../project-hotfix
Parallel Development
Work on multiple features simultaneously:
# Main worktree: ongoing feature development
# First linked worktree: code review
git worktree add ../review feature-to-review
# Second linked worktree: experiment
git worktree add -b experiment ../experiment main
# Work in all three directories as needed
Testing Different Versions
# Test current version
make test
# Test release version in parallel
git worktree add --detach ../test-release v1.0.0
cd ../test-release
make test
# Compare results
Long-Running Branches
# Maintain permanent worktrees for stable branches
git worktree add ../project-stable stable
git worktree add ../project-dev develop
git worktree add ../project-main main
# Lock them to prevent accidental removal
git worktree lock ../project-stable --reason "Stable branch worktree"
Managing Worktrees
Moving a Worktree
git worktree move <old-path> <new-path>
Example:
git worktree move ../project-feature ../project-new-feature
Repairing Worktrees
If you manually move a worktree or the main repository:
# Run in the moved worktree
git worktree repair
# Or run in main repository
git worktree repair <path>
Pruning Stale Worktrees
Remove metadata for worktrees that no longer exist:
# Show what would be pruned
git worktree prune --dry-run
# Actually prune
git worktree prune
# Prune older than 3 months
git worktree prune --expire 3.months.ago
Worktree Details
Shared vs. Per-Worktree
Shared between all worktrees:
- Commits and objects
- Branches and tags
- Configuration (except worktree-specific config)
- Most refs under
refs/
Per-worktree:
HEAD position
- Index (staging area)
- Working directory state
refs/bisect/, refs/worktree/, refs/rewritten/
Accessing Other Worktree Refs
# Reference main worktree's HEAD
git show main-worktree/HEAD
# Reference specific linked worktree
git show worktrees/feature/HEAD
Worktree-Specific Configuration
Enable worktree-specific config:
git config extensions.worktreeConfig true
Then set per-worktree config:
Limitations and Caveats
Submodules: Worktrees with submodules cannot be moved automatically. You must use git worktree repair after manual moves.Bare repositories: The main worktree of a bare repository has special restrictions.Same branch limitation: You cannot check out the same branch in multiple worktrees simultaneously (unless using --force).
Working Around Limitations
# To work on the same branch in two places:
# Create a temporary branch pointing to the same commit
git worktree add -b temp-feature ../project-temp feature
# When done, merge or rebase back to feature
cd ../project-temp
git checkout feature
git merge temp-feature
git branch -d temp-feature
Best Practices
-
Organize worktree directories - Keep them in a consistent location:
~/projects/myapp/ # Main worktree
~/projects/myapp-feature/ # Linked worktrees
~/projects/myapp-hotfix/
-
Use descriptive paths - Name worktree directories after their branches
-
Clean up regularly - Remove worktrees when done:
git worktree list
git worktree remove <unused-worktree>
-
Lock portable worktrees - If on removable media:
git worktree lock <path> --reason "On USB drive"
-
Use for parallel tasks - Not for tiny context switches (use stash instead)
-
Check worktree status - Before major operations:
git worktree list --verbose
Worktrees vs. Alternatives
| Approach | Pros | Cons |
|---|
| Worktrees | Multiple branches simultaneously, shared objects | More disk space, complexity |
| Stash | Quick context switch, simple | Can’t work on both branches simultaneously |
| Multiple clones | Complete isolation | Wastes disk space, no shared objects |
| Bare repo + clones | Flexible setup | Complex, more maintenance |
Troubleshooting
Cannot Create Worktree
Error: “branch ‘feature’ is already checked out”
Solution: The branch is checked out in another worktree:
# Find where it's checked out
git worktree list
# Remove that worktree or use a different branch
Worktree Pruned Accidentally
Problem: Administrative files removed, but directory still exists.
Solution:
# Repair the connection
cd <worktree-directory>
git worktree repair
Cannot Remove Worktree
Error: “contains modified or untracked files”
Solution:
# Force remove
git worktree remove --force <path>
# Or clean the worktree first
cd <worktree>
git clean -fd
git reset --hard
cd ..
git worktree remove <worktree>
Commands Reference
# Create worktree
git worktree add <path> [<branch>]
git worktree add -b <new-branch> <path> [<base>]
git worktree add --detach <path> [<commit>]
# List worktrees
git worktree list
git worktree list --verbose
git worktree list --porcelain
# Remove worktree
git worktree remove <worktree>
git worktree remove --force <worktree>
# Move worktree
git worktree move <worktree> <new-path>
# Lock/unlock worktree
git worktree lock <worktree> --reason <string>
git worktree unlock <worktree>
# Repair worktrees
git worktree repair [<path>...]
# Prune stale administrative files
git worktree prune
git worktree prune --dry-run
git worktree prune --verbose
Configuration
Useful configurations:
[worktree]
# Use relative paths for portability
useRelativePaths = true
# Guess remote branch when creating worktree
guessRemote = true
[alias]
# Convenient worktree aliases
wt = worktree
wta = worktree add
wtl = worktree list
wtr = worktree remove