Git Work Trees
A Step-by-Step Guide to Git Work Trees
Introduction
Git work trees are a powerful feature that allows you to work with multiple branches simultaneously without switching between them. This guide will take you from basic concepts to advanced workflows, using practical examples with a repository structure in your ~/GitHub
folder.
Prerequisites
- Basic understanding of Git concepts (commits, branches, remotes)
- Git installed on your system
- A GitHub folder structure like:
~/GitHub/
Basic Concepts
What is a Git Work Tree?
A Git work tree is a working directory that contains a copy of your repository files. By default, Git has one work tree per repository, but you can create additional work trees to work on different branches simultaneously.
Why Use Work Trees?
- Parallel Development: Work on multiple branches without switching
- Build Testing: Test different branches in separate directories
- Code Review: Review code in one branch while working on another
- Performance: Avoid the overhead of switching branches
Level 1: Basic Work Tree Operations
Step 1: Understanding Your Repository Structure
First, let’s understand the typical structure:
~/GitHub/
├── my-repo/ # Your main repository
│ ├── .git/ # Git metadata
│ ├── src/ # Source code
│ ├── docs/ # Documentation
│ └── README.md # Project readme
Step 2: Creating Your First Work Tree
Navigate to your repository and create a work tree for a feature branch:
# Navigate to your repository
cd ~/GitHub/my-repo
# Create a new branch (if it doesn't exist)
git checkout -b my-feature
# Create a work tree for this branch
git worktree add ../my-repo-my-feature my-feature
Step 3: Understanding the Result
After creating the work tree, your structure becomes:
~/GitHub/
├── my-repo/ # Main repository (main branch)
│ ├── .git/ # Git metadata
│ ├── src/ # Source code
│ └── README.md # Project readme
└── my-repo-my-feature/ # Work tree for my-feature branch
├── .git # Git worktree reference
├── src/ # Source code (my-feature version)
└── README.md # Project readme (my-feature version)
Step 4: Working with Multiple Work Trees
Now you can work on both branches simultaneously:
# In one terminal - work on main branch
cd ~/GitHub/my-repo
echo "Working on main branch" >> main-work.txt
git add main-work.txt
git commit -m "Add main branch work"
# In another terminal - work on feature branch
cd ~/GitHub/my-repo-my-feature
echo "Working on feature branch" >> feature-work.txt
git add feature-work.txt
git commit -m "Add feature branch work"
Level 2: Intermediate Work Tree Management
Step 5: Listing and Managing Work Trees
View all your work trees:
cd ~/GitHub/my-repo
git worktree list
Expected output:
~/GitHub/my-repo main [main]
~/GitHub/my-repo-my-feature my-feature [my-feature]
Step 6: Removing Work Trees
When you’re done with a work tree:
# Remove the work tree directory
git worktree remove ../my-repo-my-feature
# Or if you want to keep the directory but remove the Git association
git worktree remove --force ../my-repo-my-feature
Step 7: Creating Work Trees for Existing Branches
Create work trees for branches that already exist:
# Create work tree for an existing branch
git worktree add ../my-repo-existing-branch existing-branch
# Create work tree for a remote branch
git fetch origin
git worktree add ../my-repo-remote-feature origin/remote-feature
Level 3: Advanced Work Tree Scenarios
Step 8: Temporary Work Trees
Create temporary work trees that are automatically cleaned up:
# Create a temporary work tree (automatically removed when branch is deleted)
git worktree add --detach ../my-repo-temp HEAD
# Work on a temporary fix
cd ../my-repo-temp
echo "Quick fix" > quick-fix.txt
git add quick-fix.txt
git commit -m "Quick fix"
# Merge back to main
cd ../my-repo
git merge ../my-repo-temp
git worktree remove ../my-repo-temp
Step 9: Work Trees with Different Base Directories
Organize work trees in a structured way:
# Create a work tree in a specific directory
git worktree add ~/GitHub/worktrees/my-repo-feature my-feature
# Your structure now looks like:
~/GitHub/
├── my-repo/ # Main repository
├── my-repo-my-feature/ # Feature work tree
└── worktrees/ # Organized work trees
└── my-repo-feature/ # Another feature work tree
Step 10: Work Trees for Different Git Operations
Use work trees for specific Git operations:
# Create work tree for testing
git worktree add ../my-repo-test test-branch
# Create work tree for documentation
git worktree add ../my-repo-docs docs-branch
# Create work tree for hotfix
git worktree add ../my-repo-hotfix hotfix-branch
Level 4: Complex Workflows and Best Practices
Step 11: Work Tree for Continuous Integration
Set up a work tree for CI/CD testing:
# Create a work tree for CI testing
git worktree add ../my-repo-ci ci-test
# Set up CI environment
cd ../my-repo-ci
npm install
npm run test
npm run build
# If tests pass, merge back
cd ../my-repo
git merge ci-test
git worktree remove ../my-repo-ci
Step 12: Work Tree for Code Review
Use work trees for thorough code review:
# Create work tree for review
git worktree add ../my-repo-review review-branch
# Review code in the work tree
cd ../my-repo-review
code . # Open in VS Code
# Review files, run tests, check formatting
# Make review comments
git commit -m "Address review comments"
# Clean up
cd ../my-repo
git worktree remove ../my-repo-review
Step 13: Work Tree for Parallel Development
Work on multiple features simultaneously:
# Create work trees for different features
git worktree add ../my-repo-feature-a feature-a
git worktree add ../my-repo-feature-b feature-b
git worktree add ../my-repo-feature-c feature-c
# Work on feature A
cd ../my-repo-feature-a
# Make changes, commit
# Switch to feature B
cd ../my-repo-feature-b
# Make changes, commit
# Switch to feature C
cd ../my-repo-feature-c
# Make changes, commit
Level 5: Troubleshooting and Maintenance
Step 14: Common Issues and Solutions
Issue: Work tree is locked
# Check if work tree is locked
git worktree list
# Force remove locked work tree
git worktree remove --force ../my-repo-my-feature
Issue: Work tree directory was deleted manually
# Prune orphaned work tree references
git worktree prune
# Or prune with verbose output
git worktree prune --verbose
Issue: Work tree is out of sync
# Update work tree
cd ../my-repo-my-feature
git pull origin my-feature
# Or reset to match main repository
git reset --hard origin/my-feature
Step 15: Work Tree Maintenance
Regular maintenance tasks:
# List all work trees
git worktree list
# Check work tree status
cd ~/GitHub/my-repo
git worktree list --porcelain
# Prune orphaned work trees
git worktree prune
# Check for work tree conflicts
git worktree list | grep -E "(locked|prunable)"
Level 6: Advanced Integration and Automation
Step 16: Scripting Work Tree Operations
Create scripts for common work tree operations:
#!/bin/bash
# create-worktree.sh
REPO_NAME=$1
BRANCH_NAME=$2
WORKTREE_PATH="../${REPO_NAME}-${BRANCH_NAME}"
if [ -z "$REPO_NAME" ] || [ -z "$BRANCH_NAME" ]; then
echo "Usage: $0 <repo-name> <branch-name>"
exit 1
fi
cd ~/GitHub/$REPO_NAME
# Create branch if it doesn't exist
git checkout -b $BRANCH_NAME 2>/dev/null || git checkout $BRANCH_NAME
# Create work tree
git worktree add $WORKTREE_PATH $BRANCH_NAME
echo "Created work tree at: $WORKTREE_PATH"
echo "Branch: $BRANCH_NAME"
Usage:
chmod +x create-worktree.sh
./create-worktree.sh my-repo my-feature
Step 17: Work Tree with Git Hooks
Set up hooks for work tree management:
# In your main repository
cd ~/GitHub/my-repo
# Create a post-commit hook
cat > .git/hooks/post-commit << 'EOF'
#!/bin/bash
# Update all work trees after commit
git worktree list | while read path branch hash; do
if [ "$path" != "$(pwd)" ]; then
echo "Updating work tree: $path"
cd "$path"
git pull origin "$branch"
fi
done
EOF
chmod +x .git/hooks/post-commit
Step 18: Work Tree with IDE Integration
Configure your IDE to work with multiple work trees:
# Create a VS Code workspace for all work trees
cat > ~/GitHub/my-repo.code-workspace << 'EOF'
{
"folders": [
{
"name": "Main Repository",
"path": "."
},
{
"name": "Feature Branch",
"path": "../my-repo-my-feature"
}
],
"settings": {
"git.autoRepositoryDetection": false
}
}
EOF
Level 7: Custom Shell Functions for Work Tree Management
Step 19: Essential Work Tree Functions
Add these functions to your ~/.zshrc
or ~/.bashrc
for seamless work tree management:
# Function to create a work tree and switch to it
add-wt() {
local branch_name="$1"
if [ -z "$branch_name" ]; then
echo "Usage: add-wt <branch-name>"
echo "Creates a work tree for the specified branch and switches to it"
return 1
fi
# Get the current repository name and path
local repo_path=$(git rev-parse --show-toplevel 2>/dev/null)
if [ $? -ne 0 ]; then
echo "Error: Not in a Git repository"
return 1
fi
local repo_name=$(basename "$repo_path")
local parent_dir=$(dirname "$repo_path")
local worktree_path="$parent_dir/${repo_name}-${branch_name}"
# Check if work tree already exists
if git worktree list | grep -q "$worktree_path"; then
echo "Work tree already exists at: $worktree_path"
echo "Switching to existing work tree..."
cd "$worktree_path"
return 0
fi
# Check if branch exists locally or remotely
if ! git show-ref --verify --quiet "refs/heads/$branch_name" && \
! git show-ref --verify --quiet "refs/remotes/origin/$branch_name"; then
echo "Branch '$branch_name' does not exist. Creating new branch..."
git checkout -b "$branch_name"
else
# Switch to existing branch
git checkout "$branch_name"
fi
# Create the work tree
echo "Creating work tree for branch '$branch_name'..."
if git worktree add "$worktree_path" "$branch_name"; then
echo "✅ Work tree created successfully at: $worktree_path"
echo "Switching to work tree..."
cd "$worktree_path"
# Show current status
echo "Current branch: $(git branch --show-current)"
echo "Work tree path: $(pwd)"
else
echo "❌ Failed to create work tree"
return 1
fi
}
# Function to navigate to an existing work tree
go-wt() {
local branch_name="$1"
if [ -z "$branch_name" ]; then
echo "Usage: go-wt <branch-name>"
echo "Navigates to the work tree for the specified branch"
return 1
fi
# Get the current repository name and path
local repo_path=$(git rev-parse --show-toplevel 2>/dev/null)
if [ $? -ne 0 ]; then
echo "Error: Not in a Git repository"
return 1
fi
local repo_name=$(basename "$repo_path")
local parent_dir=$(dirname "$repo_path")
local worktree_path="$parent_dir/${repo_name}-${branch_name}"
# Check if work tree exists
if [ -d "$worktree_path" ] && git worktree list | grep -q "$worktree_path"; then
echo "Switching to work tree: $worktree_path"
cd "$worktree_path"
# Show current status
echo "Current branch: $(git branch --show-current)"
echo "Work tree path: $(pwd)"
else
echo "❌ Work tree for branch '$branch_name' does not exist"
echo "Use 'add-wt $branch_name' to create it first"
return 1
fi
}
# Helper function to list all work trees with current status
list-wt() {
local repo_path=$(git rev-parse --show-toplevel 2>/dev/null)
if [ $? -ne 0 ]; then
echo "Error: Not in a Git repository"
return 1
fi
local repo_name=$(basename "$repo_path")
local current_path=$(pwd)
echo "📁 Work Trees for repository: $repo_name"
echo "=========================================="
git worktree list | while read path branch hash; do
local status=""
if [ "$path" = "$current_path" ]; then
status="📍 CURRENT"
fi
local branch_name=$(echo "$branch" | sed 's/\[\(.*\)\]/\1/')
printf "%-50s %-20s %s\n" "$path" "$branch_name" "$status"
done
echo ""
echo "💡 Use 'add-wt <branch>' to create a new work tree"
echo "💡 Use 'go-wt <branch>' to navigate to an existing work tree"
}
# Function to remove a work tree
remove-wt() {
local branch_name="$1"
if [ -z "$branch_name" ]; then
echo "Usage: remove-wt <branch-name>"
echo "Removes the work tree for the specified branch"
return 1
fi
# Get the current repository name and path
local repo_path=$(git rev-parse --show-toplevel 2>/dev/null)
if [ $? -ne 0 ]; then
echo "Error: Not in a Git repository"
return 1
fi
local repo_name=$(basename "$repo_path")
local parent_dir=$(dirname "$repo_path")
local worktree_path="$parent_dir/${repo_name}-${branch_name}"
# Check if work tree exists
if [ -d "$worktree_path" ] && git worktree list | grep -q "$worktree_path"; then
echo "Removing work tree: $worktree_path"
# Check if we're currently in the work tree to be removed
if [ "$(pwd)" = "$worktree_path" ]; then
echo "⚠️ You are currently in the work tree to be removed"
echo "Switching to main repository first..."
cd "$repo_path"
fi
# Remove the work tree
if git worktree remove "$worktree_path"; then
echo "✅ Work tree removed successfully"
else
echo "❌ Failed to remove work tree. Use --force if needed:"
echo " git worktree remove --force $worktree_path"
fi
else
echo "❌ Work tree for branch '$branch_name' does not exist"
return 1
fi
}
Step 20: Installing and Using the Functions
Add to your shell configuration:
# Add the functions to your ~/.zshrc or ~/.bashrc echo 'source ~/.git-worktree-functions.sh' >> ~/.zshrc # Or copy the functions directly to your shell config file
Create a dedicated functions file (recommended):
# Create a dedicated file for Git work tree functions cp ~/.zshrc ~/.zshrc.backup echo 'source ~/.git-worktree-functions.sh' >> ~/.zshrc # Reload your shell configuration source ~/.zshrc
Usage examples:
# Create a new feature branch with work tree add-wt feature-login # Navigate to an existing work tree go-wt feature-login # List all work trees list-wt # Remove a work tree when done remove-wt feature-login
Step 21: Advanced Function Features
The functions include several advanced features:
- Automatic branch creation: Creates the branch if it doesn’t exist
- Smart path detection: Automatically determines work tree paths
- Conflict prevention: Checks for existing work trees before creation
- Status information: Shows current branch and path information
- Error handling: Provides helpful error messages and suggestions
- Safety checks: Prevents accidental deletion of current work tree
Step 22: Function Aliases and Shortcuts
For even faster access, add these aliases:
# Add these aliases to your shell configuration
alias wt='add-wt' # Quick work tree creation
alias gwt='go-wt' # Quick work tree navigation
alias lwt='list-wt' # Quick work tree listing
alias rwt='remove-wt' # Quick work tree removal
# Example usage with aliases
wt login-feature # Same as add-wt login-feature
gwt login-feature # Same as go-wt login-feature
lwt # Same as list-wt
rwt login-feature # Same as remove-wt login-feature
Best Practices Summary
Do’s:
- ✅ Use descriptive names for work tree directories
- ✅ Keep work trees organized in a dedicated folder
- ✅ Regularly prune orphaned work trees
- ✅ Use work trees for parallel development
- ✅ Clean up work trees when done
Don’ts:
- ❌ Don’t modify work tree
.git
files manually - ❌ Don’t create too many work trees (Git has limits)
- ❌ Don’t forget to remove work trees when done
- ❌ Don’t work directly in work tree directories for long periods
Performance Considerations:
- Work trees consume disk space
- Each work tree has its own Git metadata
- Consider using temporary work trees for short tasks
- Monitor disk usage with many work trees
Conclusion
Git work trees provide a powerful way to work with multiple branches simultaneously. By following this step-by-step guide, you can:
- Start Simple: Begin with basic work tree creation
- Scale Up: Gradually add more complex workflows
- Maintain: Keep your work trees organized and clean
- Automate: Use scripts and hooks for efficiency
Remember that work trees are tools to enhance your workflow, not replace good Git practices. Use them when they make sense for your development process, and always clean up when you’re done.
Additional Resources
This guide assumes all repositories are in a ~/GitHub/
folder structure. Adjust paths according to your actual setup.