Free lesson · Git Mastery
Worktrees
Worktrees
A note from Brandon Carter, Principal SDE at Amazon Web Services.
Multiple Working Directories From One Repository
Module 22 covers advanced operations that don't fit neatly into earlier modules - worktrees, sparse checkout, LFS, custom commands, scripting, performance, alternatives to Git, and a course-closing capstone. These are tools that don't appear in every workflow but become essential when the right situations arise.
This topic covers worktrees. A worktree is a working directory associated with a Git repository. By default, a repository has one worktree - the directory containing the .git/ folder. But Git supports multiple worktrees from a single repository: parallel working directories, each on different branches, all sharing the same object database.
I work as a Bar Raiser at AWS, conducting many interviews per year. I also lead reviews and respond to incidents across the S3 codebase. Context switching is part of my work - between reviewing one PR and writing code on another and responding to an incident on a third. Worktrees are the feature I reach for when stashing and switching branches gets in the way of parallel work.
The Problem Worktrees Solve
Without worktrees, working on multiple things means context switching:
# I'm working on feature-x; uncommitted changes in progress
git status
# (lots of local modifications)
# Urgent bug needs hotfix
git stash # save current state
git checkout main
git checkout -b hotfix/issue-1234
# ... fix the bug, commit, push
# Back to feature work
git checkout feature-x
git stash pop # restore previous state
# (potential merge conflicts on the stashed changes)
The stash-and-switch pattern works but has friction. The local state goes into the stash, which is opaque storage. Switching back may produce merge conflicts on the stashed changes. If multiple hotfixes intervene, the stash stack accumulates and gets confusing. The whole pattern is overhead that has nothing to do with the actual work.
Worktrees eliminate this friction. The original working directory stays on feature-x with its in-progress changes; a second working directory is created for the hotfix work. Both have access to the same commit history, branches, and remotes. Context switching becomes changing terminal windows, not changing repository state.
Creating a Worktree
The command:
# Add a worktree at a specific path, checked out to a specific branch
git worktree add ../hotfix-work hotfix/issue-1234
# Or create a new branch in the new worktree
git worktree add ../hotfix-work -b hotfix/issue-1234
# Or detached
git worktree add ../hotfix-work HEAD~5
The worktree add command creates a new directory at the specified path. The directory has its own working files matching the specified branch or commit. The .git folder isn't duplicated - the new worktree references the original repository's .git folder. Disk usage stays modest.
After creation:
cd ../hotfix-work
# This is now a working directory on hotfix/issue-1234
# git operations here work like a normal repository
# But the .git/ data is shared with the original repository
You can have many worktrees coexist. Each has its own working files and HEAD; all share branches, tags, and the object database.
Listing and Managing Worktrees
# List all worktrees
git worktree list
# /path/to/main-repo abc1234 [main]
# /path/to/hotfix-work def4567 [hotfix/issue-1234]
# /path/to/review-pr-123 ghi7890 [review/pr-123]
# Remove a worktree
git worktree remove ../hotfix-work
# Lock a worktree (prevent removal even if path is unavailable)
git worktree lock ../external-disk-worktree
# Prune stale worktree information
git worktree prune
Management commands handle the lifecycle. The list command shows current worktrees with their branches. Remove deletes a worktree's directory and removes Git's reference to it. Lock prevents accidental removal when a worktree is on external storage. Prune cleans up references to worktrees whose directories have been manually deleted.
When Worktrees Are the Right Tool
The cases where worktrees fit:
Parallel work on different branches. Working on feature A while reviewing PR for feature B.
Hotfixes during in-progress work. Urgent bug fix without disrupting current changes.
Long-running CI builds. Building one branch while editing another.
Cross-branch comparisons. Easy visual comparison via file system tools.
Backporting. Working on the same fix across multiple release branches simultaneously.
Code review. Checking out a PR's branch in a separate directory while keeping your own work intact.
The common pattern in each case is parallel work - situations where the friction of switching between branches outweighs the cost of multiple working directories. For my Bar Raiser work, this is daily: reviewing one PR while my own work continues elsewhere.
Restrictions and Limitations
Worktrees have some constraints:
One worktree per branch (usually). You can't have two worktrees on the same branch by default. (There are workarounds for special cases.)
Shared .git/. All worktrees share refs, hooks, and the object database. Changes in one affect all.
Submodules require care. Submodule state can be complex across worktrees.
Bare repository origin. A worktree of a bare repository works but has slight differences.
The restrictions exist because the object database is shared. Two worktrees on the same branch would create ambiguity about which is current. The restrictions don't impair normal use; they're noticeable only in edge cases.
A Specific Pattern at AWS
For my work, the typical worktree setup:
# Main repository in ~/work/s3-storage/
# This is where I do feature work
cd ~/work/s3-storage
git checkout my-feature-branch
# Hotfix worktree in ~/work/s3-storage-hotfix/
# This is where I respond to incidents
git worktree add ~/work/s3-storage-hotfix hotfix-base
# Review worktree in ~/work/s3-storage-review/
# This is where I do code reviews
git worktree add ~/work/s3-storage-review main
# Three terminal windows; one per worktree
# Context switching = window switching, not branch switching
The pattern reflects how I work. Each worktree has a specific role. Feature work happens in one; reviews happen in another; incidents happen in a third. The directories are stable; only the branch in each may change over time.
The pattern took some adjustment when I first adopted it. The reflex to use stash-and-switch was strong from years of practice. After a few months of worktree usage, the new pattern felt natural and the old one felt clunky.
Worktrees and Branches
A subtle interaction: each worktree has its own HEAD. The main repository's HEAD is in .git/HEAD; worktree HEADs are in .git/worktrees/<name>/HEAD.
# In the main directory
cat .git/HEAD
# ref: refs/heads/main
# In a worktree directory
cat .git/worktrees/hotfix-work/HEAD
# ref: refs/heads/hotfix/issue-1234
The separate HEAD files allow each worktree to be on a different branch independently. Operations in one worktree don't affect the other's HEAD. The refs (branches, tags) are shared, so updating a branch in one worktree shows in the other's branch list, but each worktree's notion of "what I'm checked out to" is independent.
A Specific Story From a Multi-PR Day
About a year ago, I had a day where I was actively engaged with three PRs simultaneously. As Bar Raiser for two of them and code owner for the third, all three needed my attention.
The worktree setup made it manageable:
# Window 1: Reviewing PR-1234 (in review worktree)
cd ~/work/s3-storage-review
git fetch origin pull/1234/head:review-1234
git checkout review-1234
# Window 2: Reviewing PR-1567 (different review worktree)
cd ~/work/s3-storage-review-2
git fetch origin pull/1567/head:review-1567
git checkout review-1567
# Window 3: My own PR-1789, in progress (main worktree)
cd ~/work/s3-storage
# Working on my changes
The three windows each had a stable working directory checked out to a different branch. Switching between PRs was switching between windows - instant. The Git operations in each window were independent; the underlying object database was shared, so commits in one were visible from the others' logs.
Without worktrees, I'd have been switching branches every few minutes, with stash-and-restore cycles in between. With worktrees, each PR had its own visible context. Mental switching was instant.
The lesson: worktrees solve a real friction problem. The friction is invisible until you stop having it; then you wonder how you tolerated it.
What Comes Next
Topic 22.2 covers sparse checkout and partial clone - techniques for working with large repositories efficiently. Topic 22.3 covers Git LFS. Topic 22.4 covers custom Git commands.
What Goes Wrong
Warning: Common worktree mistakes:
Forgetting to clean up worktrees. They accumulate; disk space and confusion grow.
Trying to check out the same branch in two worktrees. Not allowed by default.
Modifying .git in one worktree expecting isolation. They share .git; changes affect all.
Treating worktrees as completely separate repositories. They're not; they share state.
Submodule confusion. Submodules in multiple worktrees need care.
Removing worktree directories manually without pruning. Leaves stale references in .git.
Try It
Exercise: Adopt worktrees:
- Create a worktree at a different path checked out to a different branch.
- Verify both worktrees can have different files modified simultaneously.
- Make a commit in one worktree; observe it appearing in the other's log.
- Try
git worktree listto see the inventory.- Remove a worktree with
git worktree remove; verify it's cleaned up.- Identify one workflow you currently use stash-and-switch for; try worktrees instead.