8 min read

Stop Stashing, Start Using Git Worktrees

Last Tuesday I stashed a half-finished refactor to go fix a production bug. I found the bug, fixed it, and then spent twenty minutes trying to remember what the refactor was even doing. The stash pop worked. My context didn't.

That was the fifth time that month I'd done the stash-switch-restore dance, and I was done with it. So I finally learned git worktrees — and honestly, it's one of those features that makes you wonder why nobody made you learn it on day one.

Worktrees aren't new. They've been in git since 2.5, which shipped in 2015. But most tutorials treat them like a niche feature for people with weird CI setups. They're not. If you've ever stashed work, switched branches, and stashed it back, worktrees are for you.

What a worktree actually is

A worktree is a second working directory attached to the same repository. Same commit history, same branches, same remote — but a different folder on disk, checked out to a different branch.

That's it. No new commands to learn for committing, pushing, or pulling. You run git inside the new folder and it just works, because the folder is a fully functional checkout. The only thing that's shared is the .git directory itself.

Concretely, instead of one folder looking like this:

~/my-project/
  .git/
  src/
  package.json

You get this:

~/my-project/          # on main
  .git/
  src/
  package.json

~/my-project-hotfix/   # on fix/login-timeout
  src/
  package.json

Both folders can be open at the same time. Both can run npm run dev at the same time. You can commit in one while the other sits there untouched. The branches don't collide because each worktree owns its checkout.

The two commands that cover 90% of it

You need exactly two commands to get the useful 90% of worktrees:

# Add a new worktree, create the branch, check it out
git worktree add ../my-project-feature -b feature/new-thing

# List what you've got
git worktree list

The first one creates the folder, creates the branch, and checks it out. It's a single atomic operation, so there's no "forgot to create the branch" failure mode. The second one shows every worktree, which branch it's on, and whether it's dirty — which becomes your mental map.

When you're done with a worktree, you don't delete it from inside. You remove it from the git side, then delete the folder:

git worktree remove ../my-project-feature
rm -rf ../my-project-feature

That's the whole feature. Everything else is workflow.

How I actually use them

I've been using worktrees daily for a few months now, and three patterns keep coming up.

1. Hotfix while mid-feature

This is the one that sold me. I'm three hours into a feature, everything's half-committed in my head, and someone pings: production is down. Before worktrees, that meant stash, checkout main, fix, commit, push, checkout feature, stash pop — and praying the stash applied cleanly.

Now it's one command:

git worktree add ../app-hotfix -b fix/prod-outage main

I open the new folder in a second editor window, fix the bug, push, and merge. My feature branch never moved. Nothing was stashed. The context switch cost me about four seconds of typing instead of four minutes of dread.

2. Reviewing PRs without breaking your flow

Pull request review is the other big one. When someone asks "can you look at my PR?", the old flow was: stash or commit, switch to their branch, run the code, then switch back and restore everything.

Now I keep a dedicated review worktree:

git worktree add ../app-review

It just tracks whatever branch I point it at. I can check out any PR branch there, run the app, poke at it, and leave — my main workspace stays exactly where I left it. If the review needs a code change, the branch is already checked out there, so committing is normal.

3. Experiments you're scared to run on main

Want to try a risky refactor or a new library without touching your real checkout? Worktree it. Worst case, you delete the folder and the branch, and your main checkout never knew. That psychological safety is real: I attempt way more experiments now, because the blast radius is one disposable folder.

The gotchas nobody warns you about

Worktrees are great, but they have sharp edges. Here are the ones I hit, so you don't have to.

Build artifacts are per-folder, not shared. node_modules, .venv, build caches — each worktree has its own, and they're not shared with the main checkout. My project's node_modules is about 400 MB, so two worktrees means 800 MB, and three means over a gigabyte. Disk is cheap; just don't be surprised when du starts looking dramatic.

Untracked files are per-branch state, and they don't travel. A file you created in one worktree doesn't exist in the others. This is usually what you want, but it trips people up the first time they look for a config file they know they made. It's in the other folder.

Branch names must be unique across the whole repo. You can't check out feature/foo in two worktrees at once. Git will refuse, and it's correct to refuse — but the error message ("already used by worktree") confuses people the first time.

Some tools don't love it. If you use a language server or a build watcher that's path-sensitive, a second checkout can occasionally double-compile. In practice, I've only seen this bite with projects that write to absolute paths. Your mileage may vary.

Make it painless: aliases and cleanup

The two commands above are short, but I still alias them, because muscle memory beats documentation:

# .gitconfig
[alias]
  wt = worktree add
  wtl = worktree list
  wtr = worktree remove

So adding a worktree becomes git wt ../app-fix -b fix/thing main. Three characters shorter and way easier to remember.

I also keep my most-used git recipes — including these aliases and the exact hotfix sequence — in Snippet Ark, because I got tired of rewriting the same commands into my terminal history every few months. It's local-first, so the snippets live on my machine, which feels right for stuff this personal.

One more habit worth stealing: git worktree prune. If you delete a worktree folder with rm -rf instead of git worktree remove, git keeps stale metadata around. Running prune clears it. I run it every few weeks and it takes two seconds.

When to just use stash anyway

I'm not here to tell you stash is evil. It's still the right tool when the change is genuinely tiny — a one-line tweak you'll restore in the next five minutes. Stashing a single console.log removal to check something quickly is fine. Stashing a three-hour refactor is not.

A decent rule of thumb: if you wouldn't be comfortable committing the change with a message, you're probably about to stash something too big. That's the moment worktrees earn their keep.

Start with one worktree tomorrow

You don't need to reorganize your whole workflow. Just do this: next time you need to switch branches while you have uncommitted work, run git worktree add instead of git stash. One time. Feel the difference.

My stash count has gone from multiple times a week to roughly zero, and my context-switching sanity went up with it. It took me eleven years of using git to finally learn this — I'd rather you beat that record.

What's the one git feature you slept on for years before it clicked? I'm always looking for the next one.