Git Worktrees vs Discrete Checkouts: A Practical Guide

Last updated Oct 15, 2026 Published Mar 9, 2026

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/objects directory.
  • Each has an independent HEAD. Checking out branch hotfix/login in 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:

  1. Creates the directory ../hotfix (relative to the repository root).
  2. Checks out the branch hotfix/login in that directory.
  3. 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

  1. Chacon, S., & Straub, B. (2014). Pro Git (2nd ed.). Apress. https://git-scm.com/book/en/v2
  2. Git Project. (2024). Git Internals - Git Objects. Git SCM. https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
  3. Git Project. (2024). git-worktree - Manage multiple working trees. Git SCM. https://git-scm.com/docs/git-worktree
  4. 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.

You also might like