If you’ve ever worked on multiple branches in a single Git repo, you know the pain of switching back and forth. You have to stash unfinished changes or commit early, just to check out another branch. On top of that, when branches use different dependencies, like Python venvs or node_modules, you often end up reinstalling or adjusting things every time you switch.
That friction shouldn’t be part of your daily workflow. Thankfully, Git has a feature that solves exactly this problem: worktrees.
# What are Git worktrees
A Git worktree is an additional working directory attached to the same repository. Unlike cloning the repo multiple times, worktrees share the same Git database, history, and configuration. Each worktree can have its own branch checked out and its own working directory state. This means:
- - You can work on multiple branches simultaneously
- - Your dependency folders stay isolated
- - You don’t waste disk space duplicating Git history.
Think of a worktree as multiple checkouts of the same repository
sharing a single .git directory.
Important limitation
A branch can only be checked out in one worktree at a time. Git will prevent you from accidentally using the same branch twice.
# Why not just git clone?
Cloning the same repository multiple times works, but it introduces unnecessary overhead:
- - Git history is duplicated for every clone
- - Fetching and staging happens per clone
- - Configuration, hooks, and cleanup must be managed repeatedly
Worktrees avoid these issues, offering a lighter, more manageable workflow.
# Getting started
Creating a worktree is straightforward. You can either create a new branch or use an existing one:
# Create a new branch and worktree
git worktree add -b feat/123-new-button ../feat-123-new-button
# Use an existing branch in a new worktree
git worktree add ../feat-123-new-button feat/123-new-button Here is what my workspace usually looks like:
../
├─ ui-monorepo # original project, always on the development branch
├─ feat-ad-1234-node-deps-in-playground # worktree for a specific feature
├─ fix-ad-1235-stale-state # worktree for a specific bug fix
├─ fix-ad-1236-another-bug-state # worktree for another bug fix The original repo stays on development, while each feature or fix gets its own worktree directory. This makes it easy to open a specific branch in your editor and continue where you left off, without context switching or dependency headaches.
Some other useful worktree commands:
git worktree list # list all active worktrees
git worktree delete <directory> # delete a worktree
git worktree move <worktree> <new-path> # move a worktree to a new location
git worktree prune # remove stale worktree references when directories were deleted manually Now, when a pull request needs rebasing or a quick fix, I can simply open the corresponding directory and make the changes immediately.
Tip
To make it even faster, you can use the project specific aliases from my previous post to create a initialization command:
alias init-env="uv sync --all-groups \
&& cp --update=none .env.dist .env \
&& (cd frontend && npm install)"