Git Worktrees Intro
Worktrees let you check out multiple branches at the same time, each in its own directory, all sharing one underlying repository. No more git stash dances when you need to jump between contexts.
Search across all documentation pages
Worktrees let you check out multiple branches at the same time, each in its own directory, all sharing one underlying repository. No more git stash dances when you need to jump between contexts.
๐ค Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles ๐ฅ
Quick-reference recipe card -- copy-paste ready.
# Add a worktree on an existing branch
git worktree add ../feature-branch feature-branch
# Add a worktree and create a new branch in one step
git worktree add ../new-feature -b new-feature
# Add a worktree at a specific commit or tag (detached HEAD)
git worktree add ../hotfix abc1234
# List all worktrees attached to this repo
git worktree list
# Remove a worktree (after committing or stashing changes)
git worktree remove ../feature-branch
# Clean up stale entries after a folder was deleted manually
git worktree pruneWhen to reach for this: Any time you would otherwise stash work, clone the repo a second time, or context-switch between branches multiple times an hour.
# 1. Start a feature in its own directory
git worktree add ../my-feature -b my-feature
# 2. Work and commit inside that worktree
cd ../my-feature
# ...edit files, run tests...
git commit -am "feat: scaffold new feature"
# 3. Walk back to the main worktree to merge
cd ../main-repo
git switch main
git merge my-feature
# 4. Clean up
git worktree remove ../my-feature
git branch -d my-featureWhat this demonstrates:
Think of a Git repository as a writer's house. In the back room sits a steel filing cabinet -- this is the .git directory, holding every commit, every branch, every fragment of history. Out in the study is your writing desk, where one version of the manuscript is laid out so you can read and edit it.
For most of Git's history, you only had one desk. Switching branches meant gathering up the pages on the desk, filing them back, and laying out a different version. If you were halfway through a feature and needed to fix a production bug, you had to stash your half-finished work, clear the desk, fix the bug, then reverse the whole process.
Worktrees give you a second desk. And a third. And a fourth. Each desk holds a different branch, with its own files visible and editable, while the others continue holding whatever you had laid out before. All the desks share the same cabinet -- the same history, the same commits, the same remote configuration. But the files on top, the working trees, are independent.
That's what a worktree is: a second view onto the same repository.
When you add a worktree, Git does something elegant. In the new folder, it creates the working tree files for the branch you asked for, exactly as if you had cloned the project fresh. But it does not clone anything. The new folder contains a tiny .git file (not a directory) that points back to the original repository's .git folder. Every operation in the new worktree -- commit, push, pull, log -- reads from and writes to that single shared cabinet.
| Shared across all worktrees | Separate per worktree |
|---|---|
| Object database (commits, blobs, trees) | Working directory files |
| Branches and tags | Index (staging area) |
| Remote configuration | HEAD reference |
| Stashes | Uncommitted changes |
| Most config | Submodule checkouts (init per worktree) |
| Hooks | Build artifacts (node_modules, etc.) |
The wonderful consequence: there's no duplication of history. If your repo is gigabytes of objects, adding a worktree barely adds anything to disk -- you only pay for the checked-out files, not the history behind them.
Git enforces a single rule that catches everyone once: a given branch can only be checked out in one worktree at a time. Try to check out main in two worktrees, and Git refuses. The reason is simple -- if both desks held the same draft and you wrote different changes on each, which set of changes would the cabinet eventually accept? The constraint protects you from a question that has no good answer.
The workaround is what you'd expect: branch off. If you want a second worktree based on main, create a new branch from main (git worktree add ../scratch -b scratch main) and use that.
Inside your original .git folder, a worktrees/ subdirectory holds a small folder for every linked worktree. Each contains that worktree's own HEAD, index, logs, and a few other files. The original repo always knows where every worktree lives, what branch it has checked out, and whether the folder still exists on disk.
git worktree list prints all of them, including the main worktree -- which is just the first desk among equals.
Linked worktrees feel like equals to the main one in day-to-day use, but the main worktree is where the actual .git directory lives. If you delete the main worktree's folder while linked worktrees still exist, all of them break -- the cabinet they depend on has vanished. Treat the main worktree as the foundation. Move it carefully (use git worktree repair after if needed).
The mental shift is from "branches are places I visit, one at a time" to "branches are places I can occupy simultaneously, each in its own room." Worktrees shine when:
Worktrees are not a substitute for branches. They are not always a substitute for stashing -- a quick 30-second context switch is still faster with stash. They are almost always a substitute for cloning the repo twice, since you avoid the storage cost and the headache of keeping two clones in sync via the remote.
Things that will bite you. Each gotcha includes what goes wrong, why it happens, and the fix.
Trying to check out a branch already in another worktree -- Git refuses with fatal: '<branch>' is already checked out at .... Fix: Either work in the existing worktree, create a new branch off that one (git worktree add ../scratch -b scratch <branch>), or remove the other worktree first.
Deleting a worktree folder manually with rm -rf -- Git's metadata still points to the missing folder, and git worktree list keeps showing it. Fix: Run git worktree prune to clean up dangling references.
Removing the main worktree while linked worktrees exist -- The shared .git directory disappears and every linked worktree is broken. Fix: Never delete the main worktree's folder. If you need to relocate the repo, move the main worktree first, then run git worktree repair from inside it to update the links.
Submodules don't auto-initialize in a new worktree -- The submodule directories appear empty. Fix: Run git submodule update --init --recursive inside each new worktree that needs submodules.
node_modules and build artifacts duplicated per worktree -- Each worktree has its own working tree, so npm install in three worktrees means three node_modules directories. Fix: Use a shared package cache (pnpm's content-addressable store handles this naturally) or accept the disk cost as the price of parallelism.
Hooks fire in every worktree -- Hooks live in the shared .git/hooks directory, so a pre-commit hook runs no matter which worktree you commit from. Fix: Usually what you want, but if a hook needs different behavior per worktree, branch on git rev-parse --git-common-dir vs --git-dir inside the hook.
IDEs open the wrong folder after switching worktrees -- Editor windows tied to the old path keep showing stale files. Fix: Open each worktree as its own project/window in your editor; treat them as separate workspaces.
Forgetting to commit before git worktree remove -- Git refuses to remove a worktree with uncommitted changes (good!), but you may get stuck if the folder has untracked files you forgot about. Fix: Either commit, stash, or use git worktree remove --force if you are certain the changes are disposable.
Other ways to solve the same problem -- and when each is the better choice.
| Alternative | Use When | Don't Use When |
|---|---|---|
git stash | A 30-second context switch -- fix one line on another branch and come back | The interruption is long, or you need both branches running side-by-side |
| Cloning the repo twice | You need fully isolated .git directories (e.g., experimenting with destructive git filter-repo) | You want shared history, branches, and remotes -- worktrees are strictly better here |
| Branch switching in place | Single-branch flow with no in-progress work to preserve | You have uncommitted changes, or want to keep two contexts open |
git worktree | Two or more branches need to coexist -- review + feature, hotfix + WIP, A/B comparison | A simple stash-and-switch would do the same job in less time |
.git directory, history and all -- two independent repos..git directory, so commits and fetches in one are immediately visible in others.git worktree add ../scratch -b scratch main creates a new branch starting from main and checks it out in the new worktree.../my-feature) or a dedicated worktrees parent (~/projects/myrepo-worktrees/feature-x)..git directory, so git stash list shows the same entries from any worktree.git fetch in any worktree updates the shared remote refs, so all worktrees see the new commits immediately.git pull only updates the branch checked out in the worktree where you ran it -- the other worktrees stay on whatever commit they had.git worktree move ../old-path ../new-pathmv, run git worktree repair ../new-path from inside the main worktree..git/hooks and that directory is shared, so a pre-commit hook fires in any worktree where you commit.git submodule update --init --recursive after creating it.git worktree list(detached HEAD)).--porcelain for a stable, machine-readable format.node_modules and build artifacts duplicate.git status only scan the current worktree.Reviewed by Chris St. JohnยทLast updated Jul 19, 2026
๐ค Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles ๐ฅ