Git Worktrees: Stop Stashing, Start Multitasking
TL;DR: Git worktrees let you work on multiple branches at once. Use git worktree add <path> <branch> to create, git worktree list to view, and git worktree remove <path> to delete worktrees.
If you want to understand what git worktrees are and how to work with them, then this is the right place for you.
When working on this blog, I have run into cases where I'm writing a long blog post, want to add a new feature, there is a typo I want to fix and got an idea for a short TIL to share. For you it might be a hot fix in production or an urgent code review in between a feature you are implementing.
I used to use git stash to switch between such tasks until I learned worktrees. The cost of managing the stash was not worth it. Also, if you want a coding agent to work on multiple independent tasks, worktrees come in handy.
Now that you know when you might need to use worktrees, let's find out what they are.
What is a git worktree?
A git worktree is basically a folder which has a copy of your project files while sharing the same git history with the main repository.
You can think of it like having multiple monitors connected to a single computer tower.
- The computer (main repository with .git folder): holds all the data, memory and processing power (the git history, objects, and refs)
- The monitors (worktrees): can show different windows/apps (different branches/files) at the same time but they are all powered by that one computer. This makes it easy for you to switch between applications (features/fixes).
Let's say you have your main repo at ~/projects/my-app and you create a worktree at ~/projects/my-app-hotfix, they share the same git history.
Instead of a .git folder, each worktree contains a .git file (not folder). This file stores a reference to the main repository. For our example above, the contents might be
gitdir: ~/projects/my-app/.git/worktrees/my-app-hotfix
To work with worktrees, you basically need three things, creating them, listing them and removing them.
Creating a worktree
You can create a worktree using the add command, i.e., git worktree add <path> <branch>
pathis the directory where you want to create the worktree.branchis the branch you want to check out in that worktree. If you want to create and check out a new branch in that worktree, you should pass the-bflag before the branch.
For example, if we were to create our ~/projects/my-app-hotfix worktree above, below are the commands we could use:
# in the main repository (~/projects/my-app)
git worktree add ~/projects/my-app-hotfix -b hot-fix/fix-nav-typo main
# the above command creates a new worktree, and a new branch called hot-fix/fix-nav-typo in that worktree and checks it out.
# the main at the end is to tell git to use the main branch as our base branch when creating a new branch in that worktreeAnd since worktrees are just folders, you don't need any more git commands to switch to them. You can do so using the command line or your IDE.
For example:
# if you switch to the worktree in your terminal
cd ../my-app-hotfix
# and run git status
git status
# you will see that you are on the hot-fix/fix-nav-typo branch.Now that you can create worktrees, let's see how you can see existing worktrees.
Listing worktrees
git worktree list
# this shows every active directory linked to your repository (even the main repository, it's considered a worktree)For example, in our case, we would see something like:
| Directory | Commit | Branch |
|---|---|---|
~/projects/my-app | 78edf99 | main |
~/projects/my-app-hotfix | 78ed49 | hot-fix/fix-nav-typo |
78ed49is the commit each branch is on in this example and we can see that our main repo is on the main branch but the worktree is on thehot-fix/fix-nav-typobranch.
Given that worktrees are basically a copy of your entire codebase, creating many of them isn't ideal especially if your codebase is really big. You will need to clean up after yourself if you find yourself creating many of them in a non-reusable fashion.
Removing worktrees
git worktree remove <worktree path>
# If we are to remove the worktree we have created
# while in the main repository folder
git worktree remove ../my-app-hotfixSince worktrees are just folders, you might be tempted to just delete the folder. This deletes the contents but the git database isn't updated, git still thinks the worktree exists. You can tell git to clean worktrees that no longer exist using the prune command.
git worktree pruneSince git has no way of bulk deleting worktrees, you can use the above trick to your advantage by bulk-deleting worktrees in your IDE and then run git prune.
# to delete multiple worktrees, you would have to delete them one at a time
git worktree remove ../feature-A
git worktree remove ../feature-B
git worktree remove ../feature-C
# or you can delete all of them at once in your IDE, File Explorer or the command line
rm -rf ../feature-A ../feature-B ../feature-C
# and then run git prune
git worktree pruneQuick Reference
# Create worktree with new branch
git worktree add <path> -b <new-branch> <base-branch>
# Create worktree with existing branch
git worktree add <path> <existing-branch>
# List all worktrees
git worktree list
# Remove worktree
git worktree remove <path>
# Clean up deleted worktrees
git worktree pruneA few more tips I have found useful:
The Slot Strategy: Instead of creating a worktree for each and every bug fix or feature, you can create a few worktrees that never get deleted, i.e.,
~/projects/my-app-root/
├── main/ <-- Always on 'main' branch, use it for reference
├── feature-slot/ <-- You switch branches inside here for big features
├── hotfix-slot/ <-- You switch branches inside here for quick fixes
└── review-slot/ <-- You use this to test colleagues' PRsThe "Sibling" Directory Strategy: Don't create your worktree inside your main repository folder; this confuses your tools, your gitignore and sometimes yourself. Instead, create a parent directory to hold all worktrees, i.e.,
Bad
~/projects/my-app/ <-- Main Repo
~/projects/my-app/hotfix/ <-- Worktree inside (messy!)Good
~/projects/my-app-root/ <-- Wrapper Folder
├── main/ <-- Main Repo (The "primary" clone)
├── feature-login/ <-- Worktree 1
└── hotfix-bug-123/ <-- Worktree 2Dependencies are not shared: When you switch to a new worktree you have to reinstall dependencies. It doesn't have your node_modules if you are working with node or your .venv folder if you are working with python. So your first step should be running your dependencies command.
One branch, one place: You can only check out a branch in one worktree at a time. Git prevents you from checking out a branch in multiple worktrees to avoid the risk of overriding your work.
Port collision: While git isolates your files, it doesn't isolate your computer resources. If running your server in your main worktree claims a port on your computer, running it in the worktree folder will fail as the port will be in use. While running multiple instances of your app is convenient, it also creates a risk of confusion, you have to remember which worktree is running on which port.
Tool confusion: Your IDE treats a new worktree as a completely new project, so if you have things that are not committed, e.g., your IDE settings (.vscode/settings.json). They won't be carried over.
Where to go from here?
While we have covered the most common commands you will need when working with worktrees, they are not the only commands available. You can see the full list at the git worktree documentation.
If you want to look into advanced use cases, you can learn more about bare repositories, i.e., using git clone --bare and then exclusively use worktrees. The main repo contains git history only and all worktrees are equal and disposable, i.e.:
.git/ (bare repo, holds history only)
main/ (worktree)
hotfix/ (worktree)Related Posts (3)
Git Rebase --onto
1 min readLearned about git rebase --onto for moving branches to a different base.
What started as a multiplayer prototype revealed duplicated state, overloaded slices, and blurred team ownership. This is how we refactored it — and why reading the docs still matters when coding with AI.
We needed to solve concurrent editing in our React Flow workflow builder. Here's how Yjs + WebSockets, Yjs + SSE, and Liveblocks compared — and why we chose locks instead.