Table of contents
- The problem with switching branches
- What is a Git worktree?
- A first example: adding a worktree
- Comparing worktrees with discrete checkouts side by side
- Creating a worktree for a new branch
- Listing and inspecting worktrees
- Detached HEAD worktrees
- Handling the one-branch-one-worktree constraint
- Worktrees and continuous integration
- Moving and locking worktrees
- Worktrees in scripts and tooling
- When to prefer a separate clone
- Summary
- References
Git Worktrees vs Discrete Checkouts: A Practical Guide
The content here is under the Attribution 4.0 International (CC BY 4.0) license
Working on more than one branch at the same time is a common need. A developer may be fixing a production bug while also writing a new feature, or reviewing a colleague’s pull request while their own work-in-progress is not yet committed. The classic solution is to either commit (or stash) uncommitted work, switch branches, then switch back, or to clone the repository a second time into a separate directory. Both approaches work, but both carry costs that accumulate quickly in real projects (Chacon & Straub, 2014).
Git introduced the git worktree command to provide a first-class answer to this problem. A worktree is an
additional working directory that is linked to the same repository, sharing the same object database and
history, but pointing to a different branch or commit. This article walks through the concept from first
principles, compares it with discrete checkouts, and progresses toward more advanced usage patterns.
Prerequisites and Related Reading
This article assumes you are comfortable with the basic Git commands: git init,
git commit, git branch, and git checkout (or git switch).
If you need a refresher, the friendly introduction to Git in this series
covers those foundations. Understanding how Git stores data as objects
(Git Project, 2024) is helpful for the later sections but is not required
to follow the practical examples.
The problem with switching branches
Imagine you are midway through a feature. Your working directory contains uncommitted changes across several
files. Then an urgent bug report arrives and you need to reproduce it on main. The three options most
developers reach for are listed below, along with their drawbacks.
Stash and switch: git stash saves the current state and lets you switch branches, but stash is a stack that
is easy to forget or misapply, especially when multiple stashes accumulate.
Commit a work-in-progress: creating a “WIP” commit preserves the state but pollutes the branch history and requires an interactive rebase to clean up later.
Clone again: running git clone into a second directory gives a completely independent working tree. The
.git directory is duplicated in full, which means all objects, pack files, and configuration are copied.
For a repository with several gigabytes of history, this doubles storage and download time
(Chacon & Straub, 2014).
Git worktrees solve the same problem without any of those trade-offs.
What is a Git worktree?
Every repository starts with one worktree: the directory created by git init or git clone. Git calls this
the main worktree. The git worktree add command creates additional linked worktrees, each with their own
working directory and HEAD pointer, while sharing the repository’s single object database
(Git Project, 2024).
The key properties of linked worktrees are:
- They share history. A commit pushed from one worktree is immediately visible in all others because they read
from the same
.git/objectsdirectory. - Each has an independent
HEAD. Checking out branchhotfix/loginin a linked worktree does not affect the branch checked out in the main worktree. - A given branch can only be checked out in one worktree at a time. Git enforces this to prevent conflicting index states.
A first example: adding a worktree
Start with any existing repository and run:
git worktree add ../hotfix hotfix/login
This command:
- Creates the directory
../hotfix(relative to the repository root). - Checks out the branch
hotfix/loginin that directory. - Registers the new worktree in
.git/worktrees/.
You can now open a terminal in ../hotfix, make changes, commit, and push, all without disturbing the work
in your main worktree. When you are done, remove the worktree with:
git worktree remove ../hotfix
Git also prunes stale worktree references automatically when you run git worktree prune.
Comparing worktrees with discrete checkouts side by side
The table below summarises the practical differences between the two approaches.
| Criterion | git worktree | Separate clone |
|---|---|---|
| Disk usage | Single object database shared | Full .git directory duplicated |
| Setup time | Seconds (no network) | Minutes (full download) |
| Branch isolation | Yes, per-worktree HEAD | Yes, independent clone |
| Shared history | Yes, instant visibility | No, requires fetch/pull |
| Remote configuration | Inherited from main clone | Must be reconfigured |
| Risk of divergence | Low (same object store) | Higher (requires manual sync) |
The shared object database is the most important difference. In a large monorepo measured at several
gigabytes, a second clone doubles storage. A worktree adds only the working-directory files and a small
metadata entry under .git/worktrees/.
Creating a worktree for a new branch
If the branch does not exist yet, pass -b to create it at the same time:
git worktree add -b feature/new-api ../api-work main
This creates feature/new-api starting from main and checks it out in ../api-work. This pattern is
particularly useful when starting work on a new feature while another branch is being reviewed: both
directories are open simultaneously in separate editor windows or terminal tabs.
Listing and inspecting worktrees
At any point, git worktree list shows all worktrees and their current branch:
$ git worktree list
/home/user/project a1b2c3d [main]
/home/user/hotfix e4f5g6h [hotfix/login]
/home/user/api-work 7i8j9k0 [feature/new-api]
The output includes the absolute path, the current commit SHA, and the branch name. A worktree in detached HEAD state shows the commit SHA without a branch name.
Detached HEAD worktrees
Sometimes you need to inspect a specific commit or tag without creating a branch. Passing a commit SHA or tag name instead of a branch name creates a worktree in detached HEAD state:
git worktree add ../v2-review v2.0.0
This is useful for running tests against a released version while continuing development on main, or for
investigating a historical state without the risk of accidentally committing to a branch.
Handling the one-branch-one-worktree constraint
Git prevents the same branch from being checked out in two worktrees simultaneously. If you try, you receive an error like:
fatal: 'main' is already checked out at '/home/user/project'
This constraint exists because each worktree maintains its own index (staging area) for the checked-out branch. Allowing two worktrees to check out the same branch would create two independent indexes for the same branch, making it ambiguous which one represents the true staged state (Git Project, 2024).
The practical implication is that if you want to open a read-only copy of main to reference code while
working on a feature branch, you need to create a detached HEAD worktree pointing at the tip of main:
git worktree add --detach ../main-ref main
Worktrees and continuous integration
Teams that run local CI scripts or build pipelines sometimes need to build multiple branches simultaneously
without context-switching. Worktrees integrate naturally into this pattern. Each worktree has its own
working directory, so build artifacts in ../hotfix/dist do not collide with those in ./dist.
A common shell pattern for running the test suite against two branches in parallel is:
git worktree add ../feature-test feature/experiment
(cd ../feature-test && npm install && npm test) &
npm test &
wait
Both test runs execute simultaneously and write their output independently. The wait command holds the
shell until both finish. Research on distributed development practices suggests that reducing context-switch
overhead allows developers to maintain higher focus during parallel tasks
(Bird et al., 2009).
Moving and locking worktrees
Git 2.17 added git worktree move and git worktree lock. Locking prevents a worktree from being pruned
automatically, which is useful when the worktree directory resides on a removable drive or a network
filesystem that may be temporarily unavailable:
git worktree lock ../hotfix --reason "on external drive"
Moving relocates the worktree directory and updates the internal metadata:
git worktree move ../hotfix ../bugfixes/hotfix-login
After a move, git worktree list reflects the new path immediately.
Worktrees in scripts and tooling
Because worktrees are just directories, they compose naturally with any script that accepts a path. The
GIT_DIR and GIT_WORK_TREE environment variables, which many Git-aware tools read, are correctly set when
running Git commands inside a linked worktree.
Advanced shell scripts can enumerate worktrees programmatically using the --porcelain output format:
git worktree list --porcelain
The output is line-oriented and stable across Git versions, making it suitable for parsing in Bash or Python scripts that automate multi-branch workflows.
When to prefer a separate clone
Worktrees are the right choice in most multi-branch scenarios, but there are situations where a discrete clone remains appropriate:
Different remote configurations: if you need to push to a fork and to upstream independently, each with different credentials or remote URLs, separate clones with distinct configurations avoid confusion.
Experimentation with repository settings: if you want to test different git config values (for example,
different merge strategies or core.autocrlf settings) without affecting the main development environment,
an isolated clone avoids side effects.
Onboarding and demonstrations: when setting up a clean environment to demonstrate a workflow to a colleague, starting from a fresh clone avoids any local state that might affect the demo.
In all other cases where the goal is simply to have two branches available simultaneously, worktrees are the more efficient choice (Chacon & Straub, 2014).
Summary
Git worktrees allow multiple branches to be active simultaneously by creating linked working directories that
share a single object database. Compared with keeping separate clones, worktrees reduce disk usage, eliminate
download time, and keep history automatically in sync. The practical commands covered here, git worktree add,
git worktree list, git worktree remove, git worktree lock, and git worktree move, cover the complete
lifecycle of a worktree from creation to clean-up.
Start with the simple case: git worktree add ../hotfix hotfix/my-branch. Once that pattern becomes
natural, the more advanced uses, detached HEAD worktrees, parallel CI runs, and scripted enumeration, follow
directly from the same mental model.
References
- Chacon, S., & Straub, B. (2014). Pro Git (2nd ed.). Apress. https://git-scm.com/book/en/v2
- Git Project. (2024). Git Internals - Git Objects. Git SCM. https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
- Git Project. (2024). git-worktree - Manage multiple working trees. Git SCM. https://git-scm.com/docs/git-worktree
- Bird, C., Nagappan, N., Devanbu, P., Gall, H., & Murphy, B. (2009). Does distributed development affect software quality? An empirical case study of Windows Vista. Proceedings of the 31st International Conference on Software Engineering, 518–528. https://doi.org/10.1109/ICSE.2009.5070550
About this post
This post was written by a human with AI assistance, which was used for research, outlining, editing and code suggestions. Claims, code samples and references were reviewed before publication, and the sources are cited so you can judge them yourself.