I came across git worktrees by accident last week. I was working on a blog post with an agent, and rather than disturb the branch I had mid-edit, it put the new work somewhere else. I only noticed when I ran git branch:
dev@laptop project % git branch
* feature-ongoing
+ feature-sidetrack
main
That + was new to me. I’ve used git since roughly 2012 and had never seen any marker but *. The two entries were even coloured differently in my terminal — clearly git was telling me something it assumed I already knew.
So I tried to check out the other branch:
dev@laptop project % git checkout feature-sidetrack
fatal: 'feature-sidetrack' is already used by worktree at '/home/dev/code/project-sidetrack'
Which is where I started reading. It turns out worktrees have been in git since version 2.5, released in 2015. A decade old, entirely unknown to me.
The scenario
The situation is familiar to everyone. Say you’re working on project, deep into feature-ongoing, the working tree is a mess, and something urgent needs a small fix off main.
The standard answer is git stash:
git stash push -m "wip: halfway through feature-ongoing"
git checkout -b feature-sidetrack main
# ... fix, commit, push ...
git checkout feature-ongoing
git stash pop
This works, and for most interruptions it’s still what I’d reach for. But it has friction that compounds the longer the detour lasts. Your work is parked in a stack you have to remember to unpark. Untracked files need -u or they stay behind. If the two branches disagree about which files exist, the checkout can refuse outright. And anything expensive that was warm — a running dev server, a build watcher, a container, a node_modules install — gets torn down and rebuilt on the way there and again on the way back.
The same thing with worktrees
A worktree is a second working directory attached to the same repository. Same .git, same objects, same branches — different folder, different checkout.
The command has a simple shape:
git worktree add -b <new-branch> <path> <start-point>
So the detour above becomes:
git worktree add -b feature-sidetrack ../project-sidetrack main
That’s the whole thing. A new folder ../project-sidetrack appears with feature-sidetrack checked out, branched from main. Your original folder is untouched: same branch, same dirty files, dev server still running. You cd over, fix, commit, and cd back. Nothing to pop, nothing to remember.
The start point can be any branch, not just main, and you can drop -b to check out a branch that already exists:
git worktree add -b feature-experiment ../project-experiment feature-ongoing # new branch off another branch
git worktree add ../project-hotfix hotfix # existing branch, no -b
Scaling up is unremarkable, which is the point:
git worktree add -b feature-sidetrack ../project-sidetrack main
git worktree add -b feature-review ../project-review origin/feature-remote
git worktree list
/home/dev/code/project a1b2c3d [feature-ongoing]
/home/dev/code/project-sidetrack df6a54a [feature-sidetrack]
/home/dev/code/project-review 9e8f7d6 [feature-review]
Three branches checked out at once, three independent working directories, one repository. You can run three servers on three ports and compare them side by side. Switching between them is just cd — there is no “switch worktrees” command, which took me a moment to accept.
Cleaning up:
git worktree remove ../project-sidetrack
git branch -d feature-sidetrack
A few more things to keep in mind when using worktrees:
- Your original folder was always a worktree. It’s the main worktree — it holds the real
.gitdirectory, while the others get a.gitfile pointing back to it. You can’tgit worktree removeit, and deleting it orphans the rest. - The one restriction is per-branch, not per-folder. A branch checked out in any worktree can’t be checked out in another. That’s the error I hit. It exists because two directories moving the same branch pointer independently would corrupt each other.
removedeletes the checkout, not the branch.git branch -dis a separate step. It also refuses if the worktree has uncommitted changes or untracked files, unless you pass--force.- If you delete a worktree folder by hand, run
git worktree pruneto clear the stale metadata.
And git worktree list is the command to remember. When you hit “already used by worktree,” that’s what tells you where it’s held.
So which one?
git stash | git worktree | |
|---|---|---|
| Detour length | Minutes | Hours to days |
| Setup cost | None | A folder, plus a build/install |
| Original branch | Cleaned and restored | Never touched |
| Running processes | Torn down | Keep running |
| Side-by-side comparison | No | Yes |
| Disk | Free | Another full checkout |
| Failure mode | Forgotten stash | Forgotten folder |
My rule after a week: stash for interruptions, worktrees for parallel work. If I’m coming straight back, stash is less ceremony and I shouldn’t overthink it. If the detour has its own life — a review I want to run locally while my branch stays up, a long-lived spike, a build too slow to redo twice — the folder earns its keep.
The cost of good enough
What’s slightly deflating is that none of this is new, obscure, or hard. It’s a built-in feature, four commands wide, that solves a problem I’ve worked around with stashes for years. Then again, that’s the flip side of a tool as deep as git: there’s always something left to find, even after years of daily use. I never went looking because stash was good enough, and good enough is remarkably effective at stopping you from finding better.