Git Worktree for Parallel Work: Multiple Branches Simultaneously
Master git worktree to work on multiple branches simultaneously without stashing or cloning. Learn parallel development workflows, management commands, and best practices.
Git Worktree for Parallel Work: Multiple Branches Simultaneously
git worktree is the feature that eliminates the “stash and switch” dance. It allows you to check out multiple branches of the same repository in different directories simultaneously, all sharing the same .git database. No cloning, no stashing, no context switching overhead.
Imagine you’re deep into a feature branch when a critical production bug report comes in. Normally, you’d stash your changes, switch to main, fix the bug, push, switch back, and unstash. With worktrees, you just create a new directory for the hotfix, fix it, push, and delete the directory. Your feature branch keeps running in the original directory, untouched.
This post covers the complete worktree toolkit: creating and managing worktrees, handling shared state, automation scripts, and the scenarios where worktrees save hours of productivity.
Introduction
git worktree solves the everyday problem of needing two branches checked out at once. Instead of stashing, cloning, or juggling directories, worktrees give you parallel workspaces from a single repo — ideal for code review, hotfixes, and context switching. Understanding how it works and when to apply it can significantly improve your team’s collaboration, code quality, and release processes.
In this article, we’ll explore when to use worktrees and when not to, practical workflows, common patterns and pitfalls, and actionable guidance you can apply to your projects right away. By the end, you’ll have a solid foundation for making informed decisions about parallel development with Git worktree.
git worktree creates linked working directories that share a single repository database. The architecture is elegant: all worktrees share the same .git objects and refs, but each has its own working directory with independent HEAD and index. This means creating a worktree is nearly instant — Git just creates a new directory and checks out a branch there, without duplicating any objects.
When to Use / When Not to Use
Use Git Worktree When
- Hotfixes during active development — Fix production bugs without stashing feature work
- Code review — Check out a PR branch in a separate directory to test it locally
- Long-running builds/tests — Keep working while a build runs in another worktree
- Comparing branches — Diff two branches side-by-side in your file explorer
- Experimentation — Try a risky refactor in a separate worktree without risking your main work
Do Not Use Git Worktree When
- Simple context switches — If you’re just checking a file,
git showis faster - Limited disk space — Each worktree consumes disk space for checked-out files
- Shared network drives — Worktrees can have issues on NFS or network mounts
- IDE conflicts — Some IDEs get confused by multiple instances of the same repo
Core Concepts
Git worktrees create linked working directories that share a single repository database:
| Concept | Description |
|---|---|
| Main worktree | The original directory where you cloned the repo |
| Linked worktree | Additional directories linked to the same .git |
Shared .git | All worktrees share the same object database and refs |
| Separate HEAD | Each worktree has its own HEAD and index |
| Branch locking | A branch checked out in one worktree cannot be checked out in another |
The architecture looks like this:
repo/
.git/ # Shared repository database
src/ # Main worktree (branch: main)
../hotfix/ # Linked worktree (branch: hotfix/urgent)
../review/ # Linked worktree (branch: pr/123)
graph TD
A[.git Directory] --> B[Main Worktree]
A --> C[Linked Worktree 1]
A --> D[Linked Worktree 2]
B --> E[HEAD: main]
C --> F[HEAD: feature/auth]
D --> G[HEAD: hotfix/bug]
A --> H[Shared Objects]
A --> I[Shared Refs]
Architecture and Flow Diagram
The worktree lifecycle from creation through cleanup:
graph TD
A[Main Repository] -->|git worktree add| B[New Directory]
B -->|Checkout Branch| C[Linked Worktree]
C -->|Independent Work| D[Commits & Pushes]
D -->|Shared Database| A
C -->|Done| E[git worktree remove]
E --> F[Directory Deleted]
A -->|Cleanup| G[git worktree prune]
Step-by-Step Guide
1. Creating a Worktree
Create a new linked worktree for a different branch:
# Create a worktree for a hotfix
git worktree add ../hotfix-urgent hotfix/urgent
# Create a worktree for a PR review
git worktree add ../pr-review pr/feature-auth
# Create a worktree with a new branch
git worktree add ../experiment -b experiment/new-algo
# Create a worktree with a detached HEAD
git worktree add ../detached --detach v1.0.0
The command creates the directory, checks out the branch, and links it to the main repository.
2. Working in Parallel
Each worktree operates independently:
# In main worktree (~/project)
git checkout feature/user-dashboard
# Edit files, run tests, continue development
# In hotfix worktree (~/hotfix-urgent)
cd ../hotfix-urgent
# Fix the bug
git add .
git commit -m "fix: resolve payment timeout"
git push origin hotfix-urgent
# Back to main worktree
cd ~/project
# Feature work is untouched, no stash needed
3. Listing and Managing Worktrees
Keep track of your active worktrees:
# List all worktrees
git worktree list
# /home/user/project abc1234 [main]
# /home/user/hotfix-urgent def5678 [hotfix/urgent]
# /home/user/pr-review ghi9012 [pr/feature-auth]
# Remove a worktree (directory must be clean)
git worktree remove ../hotfix-urgent
# Force remove (even if dirty)
git worktree remove ../experiment --force
# Prune stale worktree references
git worktree prune
4. Moving Worktrees
If you need to relocate a worktree:
# Lock the worktree (prevents pruning)
git worktree lock ../experiment
# Move the directory
mv ../experiment ../new-location/experiment
# Update the path
git worktree move ../new-location/experiment ../experiment
# Unlock
git worktree unlock ../experiment
5. Cleaning Up
Remove worktrees you no longer need:
# Remove and delete the directory
git worktree remove ../pr-review
# If the worktree has uncommitted changes:
git worktree remove ../experiment --force
# Prune references to deleted worktrees
git worktree prune
# Complete cleanup
rm -rf ../old-worktree
git worktree prune
Production Failure Scenarios
| Scenario | What Happens | Mitigation |
|---|---|---|
| Branch checkout conflict — Trying to checkout a branch already used in another worktree | Git prevents this to avoid corruption | Use git worktree list to find where the branch is checked out |
Stale worktree references — Deleting a directory without git worktree remove | git worktree list shows ghost entries | Run git worktree prune regularly |
Shared lock files — Two worktrees try to run git gc simultaneously | Lock file conflicts | Git handles this automatically; retry after a moment |
| IDE confusion — IDE indexes both worktrees as separate projects | Duplicate search results, high memory usage | Exclude linked worktrees from IDE indexing |
| Disk space exhaustion — Too many worktrees with large build artifacts | Out of disk space | Clean build artifacts in worktrees; use git worktree remove |
| Network drive issues — Worktrees on NFS or SMB shares | File locking problems, corruption | Keep worktrees on local disks only |
Trade-off Analysis
| Aspect | Advantage | Disadvantage |
|---|---|---|
| No stashing — Keep changes in place | Requires disk space for each worktree | |
| Parallel work — Multiple branches active | IDE may get confused by multiple instances | |
| Shared database — Efficient storage | Branch locking prevents duplicate checkouts | |
| Fast creation — Instant branch checkout | Network drives can cause issues | |
| Independent state — Each worktree has own HEAD | Cleanup requires manual removal | |
| Testing — Run tests in one, code in another | Build artifacts may duplicate across worktrees |
Implementation Snippets
Worktree Aliases
# Add to ~/.gitconfig
[alias]
wt-add = "!f() { git worktree add ../wt-\"$1\" \"$1\"; }; f"
wt-list = git worktree list
wt-rm = "!f() { git worktree remove ../wt-\"$1\"; }; f"
wt-prune = git worktree prune
wt-clean = "!f() { git worktree list --porcelain | grep '^worktree' | awk '{print $2}' | while read wt; do if [ ! -d \"$wt\" ]; then git worktree prune; fi; done; }; f"
Worktree Setup Script
#!/bin/bash
# scripts/setup-worktrees.sh
# Create standard worktree structure
REPO_ROOT=$(git rev-parse --show-toplevel)
BASE_NAME=$(basename "$REPO_ROOT")
echo "Setting up worktrees for $BASE_NAME..."
# Create parent directory for worktrees if it doesn't exist
PARENT_DIR=$(dirname "$REPO_ROOT")
WT_DIR="$PARENT_DIR/${BASE_NAME}-worktrees"
mkdir -p "$WT_DIR"
# Create worktree for main branch
git worktree add "$WT_DIR/main" main
# Create worktree for develop branch
git worktree add "$WT_DIR/develop" develop
echo "Worktrees created in $WT_DIR"
echo " - main: $WT_DIR/main"
echo " - develop: $WT_DIR/develop"
IDE Exclusion Configuration
// .vscode/settings.json
{
"files.watcherExclude": {
"**/../project-worktrees/**": true
},
"search.exclude": {
"**/../project-worktrees/**": true
},
"files.exclude": {
"**/../project-worktrees/**": true
}
}
Worktree Cleanup Cron Job
#!/bin/bash
# scripts/cleanup-worktrees.sh
# Remove worktrees older than 7 days
find ../ -maxdepth 1 -type d -name "wt-*" -mtime +7 | while read -r wt; do
if [ -d "$wt/.git" ]; then
echo "Removing stale worktree: $wt"
git worktree remove "$wt" --force 2>/dev/null || rm -rf "$wt"
fi
done
git worktree prune
Observability Checklist
- Logs: Not typically applicable — worktrees are local
- Metrics: Track worktree creation frequency and average lifetime
- Alerts: Alert when disk usage from worktrees exceeds threshold
- Dashboards: Display active worktrees per developer and disk space usage
- Cleanup: Automate worktree pruning to prevent stale references
Security and Compliance Notes
- Local only: Worktrees don’t push data; they’re local directories
- Access control: Worktrees inherit repository permissions
- Sensitive data: Worktrees contain the same code as the main repo — protect them equally
- Audit trail: Commits from worktrees appear normally in history
- Compliance: Worktrees don’t change compliance requirements; treat them as part of the repo
Common Pitfalls / Anti-Patterns
- Branch Collision — Trying to checkout
mainin a worktree when it’s already checked out elsewhere. Usegit worktree listto check. - Stale References — Deleting directories without
git worktree remove. Rungit worktree pruneregularly. - IDE Indexing — IDEs indexing multiple worktrees causes performance issues. Exclude linked worktrees.
- Build Artifact Duplication — Each worktree has its own
node_modulesortarget. Share artifacts or clean regularly. - Network Drive Usage — Worktrees on network drives can corrupt. Keep them local.
- Forgetting Worktrees — Creating worktrees and forgetting them wastes disk space. Name them clearly and clean up.
- Force Remove on Dirty Worktrees —
--forcedeletes uncommitted work. Commit or stash first.
Quick Recap Checklist
- Use
git worktree add <path> <branch>to create linked worktrees - Each worktree has independent HEAD and index
- Branches cannot be checked out in multiple worktrees simultaneously
- Use
git worktree listto view active worktrees - Use
git worktree remove <path>to clean up - Run
git worktree pruneto remove stale references - Exclude worktrees from IDE indexing to prevent performance issues
- Keep worktrees on local disks, not network drives
- Clean build artifacts regularly to save disk space
- Use worktrees for hotfixes, reviews, and parallel development
Worktree Management Checklist
- List active worktrees —
git worktree listbefore creating new ones - Name clearly — Use descriptive directory names (e.g.,
../wt-hotfix-payment) - Prune regularly —
git worktree pruneafter deleting directories manually - Clean on branch delete — Remove worktrees when their branches are merged and deleted
- Exclude from IDE — Configure VS Code/IntelliJ to ignore linked worktree directories
- Keep local — Never place worktrees on network drives (NFS, SMB)
- Monitor disk usage — Each worktree duplicates
node_modulesand build artifacts - Lock long-running —
git worktree lockfor worktrees you don’t want auto-pruned - Force remove carefully —
--forcedeletes uncommitted changes; commit first - Cleanup stale refs — Run
git worktree pruneweekly to remove ghost entries
Interview Questions
Git worktree creates linked directories that share the same .git database. All worktrees share objects, refs, and configuration. Creating a worktree is instant and uses minimal additional disk space.
Cloning creates a completely independent repository with its own .git database. It duplicates all objects and requires a full network fetch. Clones are independent; worktrees are connected.
Use worktrees when you need multiple branches of the same repo. Use clones when you need independent repositories or want to test on a different machine.
No. Git prevents checking out the same branch in multiple worktrees to avoid corruption. If you try, you'll get an error: 'main' is already checked out at '/path/to/main'.
If you need to work on the same code in two places, use --detach to create a detached HEAD worktree, or create a temporary branch from the same commit.
If you deleted the directory without git worktree remove, the reference still exists in Git. Run git worktree prune to clean up the stale reference.
Your work is safe if you committed it — commits are stored in the shared .git database. If you had uncommitted changes, they're lost unless you stashed them or have IDE local history.
Git worktrees share a single .git directory, meaning all objects (blobs, trees, commits) are stored once across all worktrees. When a new worktree checks out a branch, Git creates hard links to existing objects rather than copying them.
- Objects are reference-counted — when a worktree is removed, Git checks if objects are still referenced
- New commits in any worktree immediately become available to all other worktrees
- This makes worktree creation nearly instant and disk-space efficient
Locking a worktree prevents Git from pruning it automatically and protects against accidental removal. Use git worktree lock <path> to lock and git worktree unlock <path> to release.
- Locked worktrees are skipped during
git worktree prune - Prevents
git worktree removewithout the--forceflag - Useful for long-running experiments, temporary test environments, or worktrees you want to keep for days
Moving a worktree requires locking it first, then using the move command. The sequence is: git worktree lock <old-path> → mv <old-path> <new-path> → git worktree move <old-path> <new-path> → git worktree unlock <new-path>.
- The lock prevents Git from marking it as stale during the move
- The
git worktree movecommand updates the internal Git reference - Always lock before moving to prevent reference corruption
Worktrees handle submodules normally — each worktree has its own submodule working directories. Submodule state is independent per worktree, but they share the same submodule database in the main repository's .git.
- Checking out a branch in one worktree doesn't affect submodule state in another
- You can have different submodule commits checked out in different worktrees simultaneously
- Submodule updates in one worktree don't automatically propagate to others
git worktree remove is for explicitly deleting a specific worktree you no longer need. git worktree prune cleans up stale references left behind when directories are deleted without using the remove command.
removerequires the worktree to be clean (no uncommitted changes) unless you use--forcepruneruns automatically and removes entries for directories that no longer exist on disk- Best practice: always use
removewhen done; usepruneas a fallback for cleanup
No, worktrees require a working directory, which a bare repository doesn't have. Bare repositories (created with git clone --bare) have no checkout — they only store the Git database. You'd need a regular repository to create worktrees.
- Bare repositories are typically used as central/shared repositories on servers
- To use worktrees with a shared repository, clone it normally first, then create worktrees from that clone
Worktrees don't increase repository size because they share the same .git database. However, each worktree has its own working directory with checked-out files, which consumes disk space independently.
- Git's garbage collection runs across all worktrees simultaneously
- Objects referenced by any worktree's branches or commits are preserved
- Build artifacts (
node_modules,target,dist) in worktrees are not tracked by Git and must be cleaned manually
Use descriptive, consistent naming that indicates the branch purpose. Common patterns include: ../wt-<branch-name> (e.g., ../wt-hotfix-payment), ../<purpose>-<branch> (e.g., ../review-pr-123), or ../<date>-<purpose> for temporary worktrees.
- Keep names short but descriptive — you'll type them in terminal
- Avoid special characters that might break shell commands
- Consider grouping worktrees in a dedicated parent directory (e.g.,
~/project-worktrees/)
Each worktree has its own separate stash stack. Stashes created in one worktree are not accessible from another worktree — they're stored in that specific worktree's .git refs. However, committed work is shared across all worktrees.
- Use
git stash pushwithin a worktree to save uncommitted changes - Commits are shared — you don't need to stash to switch worktrees
- Stashes are local to each worktree's index, not the shared database
Yes, you can run git bisect in one worktree while continuing development in another. This is one of worktree's strongest use cases — when bisecting a regression, you can keep your current feature work untouched in a separate worktree.
- Start bisect in one worktree:
git bisect start - Continue feature work in another worktree without interference
- When bisect completes, just delete the bisect worktree and switch back to your feature worktree
Worktrees continue to work normally when a branch is deleted — the worktree simply becomes orphaned. The directory remains on disk, HEAD stays at the last commit, and you can continue working. However, Git will warn you when entering such a worktree.
- Orphaned worktrees cannot be pushed anywhere (no upstream branch)
- You can create a new branch from the orphaned HEAD to recover
- Consider removing or repurposing orphaned worktrees to avoid confusion
Use git worktree list --verbose or git worktree list --porcelain for detailed output. The verbose flag shows the branch and commit hash for each worktree, while porcelain outputs machine-readable format.
git worktree listshows: path, commit hash, current branch--porcelainoutputs one entry per line with key-value pairs- Locked worktrees show a
lockedmarker in verbose output
Yes, use git worktree add <path> <commit-hash> with a commit hash instead of a branch name. This creates a worktree with a detached HEAD at that specific commit. You can then create a new branch from there if needed.
- Useful for testing old releases or inspecting historical states
- Add
-b <new-branch-name>to create a branch from that commit - Detached HEAD worktrees are useful for experimentation without branch commitments
Worktrees inherit the repository's access control and contain the same code as the main repository. Treat them with the same security hygiene: same access controls, same protection for sensitive data, same compliance requirements.
- Worktrees don't introduce new attack surfaces beyond standard Git repositories
- Each worktree has independent file system permissions
- Audit trails from worktree commits are identical to normal commits
Worktrees are primarily for local development, not CI/CD. In CI, you typically want fresh, isolated environments per job. However, worktrees can be useful for developer-owned build agents where you want to reuse checkouts across jobs.
- CI systems like Jenkins or Buildkite can use worktrees for shared build agents
- Each CI job should use a clean worktree or container, not share with other jobs
- Worktree creation is nearly instant, making it viable for ephemeral CI runners
Each worktree has its own Git configuration but shares the same hooks directory from the main repository's .git/hooks. Hooks run independently in each worktree — committing in one worktree triggers hooks just as it would in the main repository.
- Hooks are shared, not copied — changes to hooks affect all worktrees immediately
- Each worktree runs hooks with its own environment variables and gitdir
- Pre-commit and post-commit hooks fire identically regardless of which worktree you're in
Git will not prevent you from creating a worktree for a branch that has uncommitted changes in another worktree. However, if those uncommitted changes are staged in the index, the new worktree will share that index state. It's recommended to commit or stash changes before creating worktrees to avoid unintended state sharing.
- Worktrees share the object database but have independent working directories
- Uncommitted changes remain in the original worktree only
- Always commit or stash before creating worktrees to ensure clean state
Further Reading
- Git worktree documentation — Official Git documentation
- Git worktree best practices — Usage patterns
- Worktree and IDE configuration — VS Code multi-root setup
- Disk space management — Atlassian guide
- Parallel development workflows — Trunk-based development patterns
Conclusion
Worktrees solve the fundamental tension of needing two branches checked out at once. Instead of stashing, cloning, or juggling directories, worktrees give you parallel workspaces from a single repo — ideal for review, hotfixes, and context switching.
Category
Related Posts
Git Stash and Stash Management: Save Work Without Committing
Master git stash for saving uncommitted changes, named stashes, stash list management, and when to use stash vs commit in production workflows.
Master git add: Selective Staging, Patch Mode, and Staging Strategies
Master git add including selective staging, interactive mode, patch mode, and staging strategies for clean atomic commits in version control.
Git Aliases and Custom Commands: Productivity Through Automation
Create powerful Git aliases, custom scripts, and command extensions. Learn git extras, shell function integration, and team-wide alias standardization for faster workflows.