Introduction
Git has become the de‑facto standard for source‑code version control, and most developers are familiar with its core commands: clone, checkout, branch, merge, and the like. Yet, as projects grow and teams adopt more sophisticated workflows, the limitations of a single working directory become apparent. Switching branches repeatedly, juggling multiple feature branches, or maintaining parallel builds can be cumbersome, error‑prone, and time‑consuming.
Enter Git worktrees—a powerful, built‑in mechanism that lets you check out multiple branches (or commits) simultaneously, each in its own separate working directory, while sharing a single .git repository. In this article we will:
- Explain what a worktree is and how it differs from traditional clones.
- Walk through the full set of commands (
git worktree add,list,remove, etc.). - Demonstrate real‑world scenarios where worktrees shine: feature‑branch development, CI pipelines, code‑review environments, and more.
- Explore advanced topics such as nested worktrees, detached HEADs, and sharing objects across worktrees.
- Compare worktrees with alternatives like multiple clones, submodules, and sparse‑checkout.
- Provide best‑practice guidelines, troubleshooting tips, and a curated list of resources.
By the end of this guide, you should feel confident adding Git worktrees to your daily workflow, reducing context‑switching overhead, and keeping your repository tidy.
Table of Contents
- What Is a Git Worktree?
- Prerequisites & Setup
- Core Commands
- Practical Use Cases
- Advanced Scenarios
- Worktrees vs. Alternatives
- Best Practices & Gotchas
- Troubleshooting Common Issues
- Conclusion
- Resources
What Is a Git Worktree?
A worktree is an additional working directory that is linked to a single Git repository. The repository’s object database (.git/objects) and metadata (refs, config, etc.) live in one place, while each worktree contains its own checked‑out files, a .git file that points back to the shared repository, and a HEAD reference that tells Git which branch (or commit) that worktree is on.
Key points:
| Feature | Traditional Clone | Git Worktree |
|---|---|---|
Separate .git directory | Yes (full copy) | No (shared) |
| Disk usage | O(N) per clone | O(1) for objects; only checked‑out files duplicate |
| Branch switching | git checkout (affects whole repo) | Each worktree can be on its own branch simultaneously |
| Setup time | Full clone (network) | Instant (git worktree add) once repo exists |
| Isolation | Complete isolation (good for CI) | Shared history, but isolated working files |
Because all worktrees reference the same underlying repository, creating a new worktree is essentially a cheap pointer operation—no network traffic, no duplicate objects, and minimal filesystem overhead.
Prerequisites & Setup
Before diving into commands, ensure you meet the following requirements:
- Git version ≥ 2.5 – Worktree support was introduced in Git 2.5, but many enhancements arrived in later releases. Verify with:
git --version - A central repository – Either a local bare repository (
git init --bare) or a regular non‑bare repository you already have cloned. - Filesystem permissions – The user running Git must have read/write access to the parent directory where you intend to create worktrees.
Note: Worktrees cannot be created inside the main working directory (
$GIT_DIR/worktrees) of a non‑bare repository. They must reside outside the primary working tree.
Core Commands
Git ships a dedicated sub‑command namespace for worktrees: git worktree. Below we cover the three most common operations.
Creating a Worktree
git worktree add [options] <path> [<branch>]
<path>– Destination directory for the new worktree.<branch>– Branch name, tag, or commit SHA to check out. If omitted, Git creates a new branch namedworktree/<path>based on the current HEAD.- Options of interest:
-b <new-branch>– Create and switch to a new branch.-f– Force creation even if<path>already exists (overwrites).--detach– Checkout a detached HEAD (useful for reviewing a specific commit).
Example: Simple Feature Branch
# From the main repo root
git checkout -b feature/login
git worktree add ../feature-login worktree-login
Result:
$ tree -L 2 .
.
├── .git
├── src
└── worktree-login
├── .git -> ../.git/worktrees/worktree-login
└── src
The new directory worktree-login now contains the same src files, but you can work on feature/login without affecting the primary working tree.
Listing Existing Worktrees
git worktree list
The output shows each worktree’s path, its HEAD reference, and whether it’s locked (e.g., during a rebase).
$ git worktree list
/absolute/path/to/repo (HEAD)
../feature-login (feature/login)
/tmp/ci-build-12345 (detached HEAD)
Removing a Worktree
git worktree remove <path>
This operation:
- Unlinks the worktree from the repository (removes the entry in
.git/worktrees). - Optionally deletes the directory (use
-fto force removal of uncommitted changes).
git worktree remove ../feature-login
Caution: If the worktree contains uncommitted changes, Git will refuse to remove it unless you pass
-f. Always verify you have no valuable work before forcing deletion.
Practical Use Cases
Parallel Feature Development
Imagine a team working on three independent features: login, payments, and notifications. With a single repository, developers would constantly git checkout each branch, risking merge conflicts and losing context. Using worktrees:
# From the main repo root
git worktree add ../login -b feature/login
git worktree add ../payments -b feature/payments
git worktree add ../notify -b feature/notifications
Each developer can open three terminals, each pointed at a different directory, and run npm start, docker compose up, or any build command without interfering with the others.
Benefits
- Zero checkout time – Switching branches is simply a
cdinto the appropriate directory. - Independent build artifacts – Build caches (e.g.,
node_modules,target/) stay isolated, preventing cross‑contamination. - Simplified CI – CI pipelines can spin up a fresh worktree for each job without cloning the repo again.
CI / Automated Builds
Many CI systems (GitHub Actions, GitLab CI, Azure Pipelines) start each job with a clean checkout. However, some workflows need multiple branches simultaneously—for example, building a feature branch against the latest main to run integration tests.
A typical CI script using worktrees:
# .github/workflows/feature-tests.yml
name: Feature Tests
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
# Fetch all branches
fetch-depth: 0
- name: Create worktree for main
run: |
git worktree add ../main main
- name: Run integration tests
run: |
# Build main
cd ../main && ./gradlew assemble
# Build feature
cd $GITHUB_WORKSPACE && ./gradlew test
Because the worktree shares the same object database, the job finishes faster than a second git clone. Disk usage stays low, even with large monorepos.
Code Review & Hotfixes
When reviewing a pull request, you might want to checkout the exact commit the PR is based on, while still having a clean main environment for comparison.
# Assume PR #42 targets main
git fetch origin pull/42/head:pr-42
git worktree add /tmp/pr-42 pr-42
git worktree add /tmp/main main
Now you can run diff tools, static analysis, or even launch a local web server to compare UI changes side‑by‑side.
Advanced Scenarios
Nested Worktrees & Path Constraints
Git forbids creating a worktree inside another worktree’s directory because it would lead to ambiguous repository references. However, you can nest worktrees under a common parent folder outside the main repository:
/repos/project # original repo
/repos/wt-main # primary worktree (same as repo root)
/repos/wt-feature-a # separate worktree
/repos/wt-feature-b # another worktree
Attempting the following will raise an error:
git worktree add repos/project/inner worktree-inner
# error: 'repos/project/inner' is a subdirectory of the repository
Solution: Keep all worktrees at the same hierarchical level, or use a dedicated worktrees/ directory sibling to the repo.
Detached HEAD Worktrees
A detached HEAD points directly at a commit rather than a branch. This is useful for:
- Testing a release tag without creating a temporary branch.
- Bisecting a regression while preserving the current branch state.
git worktree add --detach /tmp/release-1.2 v1.2.0
The new worktree will show:
/tmp/release-1.2 (detached HEAD)
You can still make commits; they will be orphan commits reachable only through the worktree’s reflog. To preserve them, create a branch after the fact:
cd /tmp/release-1.2
git checkout -b hotfix/1.2.1
Sharing Objects & Disk Savings
All worktrees share the same object store (.git/objects). This means:
- No duplicate blobs – Even if you have dozens of worktrees, the on‑disk size remains roughly the size of a single repository plus the checked‑out files.
- Garbage collection (
git gc) works across all worktrees. Objects referenced only by a deleted worktree will be pruned on the next GC run.
If you need to prune unused worktrees automatically, you can use:
git worktree prune
It removes stale lock files and entries for worktrees that no longer exist on disk.
Worktrees vs. Alternatives
| Scenario | Worktrees | Multiple Clones | Submodules | Sparse Checkout |
|---|---|---|---|---|
| Parallel branch editing | ✅ (single repo, cheap) | ❌ (full clone each) | ❌ (submodule points to another repo) | ❌ (still one working tree) |
| CI with many branches | ✅ (fast, low disk) | ❌ (network + storage) | ❌ (complex) | ❌ |
| Isolated environment (e.g., different build tools) | ✅ (different dirs) | ✅ (full isolation) | ❌ | ❌ |
| Cross‑repo dependencies | ❌ (only one repo) | ✅ (multiple repos) | ✅ (explicit dependency) | ❌ |
| Disk usage | Low (shared objects) | High (duplicate objects) | Moderate (each submodule its own repo) | Low (same repo) |
Takeaway: Use worktrees when you need multiple checked‑out states of the same repository. If you need truly separate repositories (different remotes, distinct histories), clones or submodules remain appropriate.
Best Practices & Gotchas
Keep worktrees outside the main repo directory
Avoid path collisions and the “cannot be a subdirectory of the repository” error.Name worktree directories clearly
Prefix withwt-or include the branch name to prevent confusion (wt-feature-login).Regularly prune stale worktrees
git worktree pruneAvoid committing from a detached HEAD unless you intend an orphan commit
Immediately create a branch if you plan to keep those changes.Lock a worktree during long‑running operations
Usegit worktree lock <path>to prevent accidental removal or rebasing while a CI job runs.Be mindful of pre‑commit hooks
Hooks are stored in the shared repository, so they run in every worktree. If you need per‑worktree hook variations, consider a custom script wrapper.Use
git gccarefully
Runninggit gcwhile many worktrees are active can be slower because Git must verify references across all worktrees.Combine with
git stash
If you need to temporarily switch worktrees but keep uncommitted changes,git stashworks as usual inside each worktree.
Troubleshooting Common Issues
| Symptom | Likely Cause | Fix |
|---|---|---|
fatal: cannot create worktree at '<path>': file exists | Target directory already contains files or a previous worktree. | Delete the directory (or move it) and retry, or use -f to force. |
error: worktree '<path>' already registered | Same worktree path already recorded in .git/worktrees. | Run git worktree prune or manually remove the entry from .git/worktrees. |
git worktree add --detach yields a detached HEAD but you cannot push. | Detached HEAD has no upstream branch. | Create a branch (git checkout -b new-branch) before pushing. |
git worktree list shows (locked) and you cannot remove it. | A lock file (locked) exists, often from an incomplete rebase or merge. | Resolve the underlying operation (e.g., git rebase --abort) or manually delete the lock file (rm .git/worktrees/<name>/locked). |
| Build artifacts from one worktree appear in another. | Shared build/cache directories (e.g., node_modules) placed at repo root. | Move caches into each worktree’s own directory or configure the build tool to use a relative path. |
Conclusion
Git worktrees are a game‑changer for developers who need to juggle multiple branches, run parallel builds, or provide isolated environments without the overhead of cloning the same repository repeatedly. By sharing the underlying object database, worktrees keep disk usage low, speed up setup, and simplify many common workflows—from feature development to continuous integration.
Key takeaways:
- Create worktrees with
git worktree add; they are cheap and fast. - List and prune regularly to keep the repository tidy.
- Leverage detached HEADs for tag testing or bisecting.
- Integrate worktrees into CI pipelines to avoid redundant clones.
- Follow best practices—keep worktrees outside the main repo, name them clearly, and lock them when necessary.
Adopting worktrees can reduce context‑switching friction, improve developer productivity, and make your Git‑centric processes more robust. Give them a try on your next project, and you’ll quickly see why they’re now a staple in many modern development toolchains.
Resources
- Git Worktree Documentation (official) – Comprehensive reference for all commands and options.
- Atlassian Git Tutorials – Worktrees – A beginner‑friendly guide with visual examples.
- GitHub Docs – Using Multiple Worktrees – Practical advice for integrating worktrees with GitHub workflows.
- Git Internals – Object Database Sharing – Deep dive into how worktrees share objects under the hood.
- GitHub Actions – Checkout with Worktrees Example – Sample workflow showing worktree usage in CI.