Git Worktrees & Parallel Development Jump to heading

The interruption is familiar: you are halfway through a change, something urgent arrives, and the options are all bad. Stash the work and hope it applies cleanly later; commit something half-finished with a message you will have to rewrite; or clone the repository again and wait several minutes to duplicate objects you already have on disk. A worktree is the fourth option β€” a second checked-out directory backed by the same object store, created in a second. This part of Git Workflow Architecture & Branching Strategies covers how they work, where they surprise people, and the housekeeping that keeps them useful.

Prerequisites Jump to heading

What a Worktree Shares and What It Does Not Jump to heading

A worktree is a checked-out directory linked to the repository’s object database. Commits, branches, tags and the reflog are shared instantly β€” a commit made in one worktree is immediately visible from the other. What is not shared is everything outside version control: build output, dependency directories, editor state, and any file matched by .gitignore.

Shared, separate, and the traps in betweenObjects, refs and configuration live in one place and are visible from every worktree. The working files, index and HEAD are per worktree. Ignored files such as dependency directories are per worktree too, which is the detail that surprises people first.Sharedobjects and packsbranches and tagsreflogrepository configPer worktreeworking filesthe indexHEADthe current branchNot shared at allnode_modulesbuild outputeditor stateuntracked filesthe right-hand column is why a new worktree still needs a dependency install

The shared object store is what makes this cheap: creating a worktree writes a few files and checks out a tree, rather than transferring a decade of history a second time.

Step 1 β€” Create One and See the Cost Jump to heading

# A worktree for an existing branch
git worktree add ../app-review origin/feature/refund-window

# A worktree with a new branch created at the same time
git worktree add -b fix/urgent-timeout ../app-hotfix origin/main
# Verification: both are listed, and the disk cost is the working files only
git worktree list
du -sh .git ../app-hotfix

The second command is the interruption-handling case: a new branch from the current default branch, in its own directory, without touching the work in progress you already have checked out.

Step 2 β€” Understand the One-Branch-Per-Worktree Rule Jump to heading

Git refuses to check out the same branch in two worktrees, because two working copies advancing one ref would produce results nobody wants.

git worktree add ../another main
# fatal: 'main' is already checked out at '/home/dev/app'
# Inspect a branch without checking it out
git worktree add --detach ../app-inspect origin/main
# Verification: a detached worktree holds no branch and blocks nothing
git -C ../app-inspect status --short --branch | head -1

Detached worktrees are the right tool for anything read-only: running a build against a specific commit, comparing behaviour between two revisions, or bisecting β€” the technique used in automating git bisect with a test script.

Step 3 β€” Handle Per-Worktree Configuration Jump to heading

Most configuration is shared, which is usually what you want and occasionally exactly wrong β€” a worktree used for a client project may need a different committer email.

# Enable per-worktree configuration, then set a value in one worktree only
git config extensions.worktreeConfig true
git config --worktree user.email '[email protected]'
# Verification: the two worktrees now report different values
git config --get user.email
git -C ../app-client config --get user.email

The conditional-include mechanism described in shipping a team gitconfig with includeIf is usually a better fit when the distinction is by directory rather than by worktree, and it survives the worktree being recreated.

Step 4 β€” Deal With What Is Not Version Controlled Jump to heading

A fresh worktree has no dependency directory, no build cache and no local environment file. The build fails, and the failure is confusing because the source is identical.

# Symlink an expensive shared directory rather than reinstalling it
ln -s ../app/node_modules ../app-review/node_modules   # only if versions match

# Or install properly, which is correct but slower
(cd ../app-review && npm ci)
# Copy local environment files that are ignored by design
cp .env.local ../app-review/.env.local 2>/dev/null || true

SAFETY WARNING β€” symlinking a dependency directory between worktrees is safe only while both branches resolve to the same dependency versions. The moment one branch changes the lockfile, the shared directory serves the wrong tree to one of them, and the resulting failures point at application code rather than at the symlink. Prefer a real install for anything long-lived, and reserve the symlink for short-lived review worktrees.

Creating a usable worktree, end to endAdding the worktree is instant because objects are shared. Making it usable takes the extra steps that version control does not cover: dependencies, ignored configuration files, and a build directory that does not collide with the original.worktree addsecondsobjects sharedDependenciesinstall or symlinkIgnored files.env, local configBuildseparate output dironly the first box is free β€” budget for the other three One object store, three checked-out treesThe repository holds a single object database and one set of refs. Each worktree checks out a different commit from it, and Git refuses to let two worktrees claim the same branch so that no ref can be advanced from two places at once.three worktrees over one shared historymain (app/)ABCfix/timeout (app-hotfix/)CH1detached (app-review/)Robjects are stored once; only the checked-out files are duplicated

Step 5 β€” Remove Them Deliberately Jump to heading

Worktrees accumulate. Deleting the directory without telling Git leaves a stale administrative entry, and the branch stays locked to a directory that no longer exists.

# The correct removal
git worktree remove ../app-review

# After deleting a directory by hand, clean up the bookkeeping
git worktree prune --verbose
# Verification: no stale entries remain
git worktree list --porcelain | grep -c '^worktree'

A worktree containing uncommitted changes will refuse to be removed, which is the behaviour you want. Forcing it discards the work permanently β€” the recovery options in recovering lost commits with git reflog do not help with files that were never committed.

Operating Worktrees Across a Team Jump to heading

Worktrees are a local technique, so there is nothing to roll out β€” but a few shared conventions prevent the common frustrations. The first is naming. A directory called ../app2 tells nobody anything a week later; ../app-PAY-812 or ../app-review does, and it makes the cleanup decision obvious. Teams that adopt worktrees seriously usually settle on a sibling directory pattern β€” the main clone in ~/work/app, everything else in ~/work/app-<purpose> β€” so that shell completion, editor workspaces and build scripts can all assume the layout.

The second is build output. Build systems that write into a fixed path relative to the repository root are fine; those that write to an absolute path, or that cache by project name rather than by path, will have two worktrees fighting over one directory. The symptom is a build that succeeds in one worktree and then mysteriously rebuilds everything in the other. Check this before recommending worktrees to the team, because it is the single most common reason people try them once and go back to cloning.

The third is editor and tooling state. Language servers, test runners and container setups frequently keep per-project state keyed on the directory, which works correctly, and occasionally keyed on the repository, which does not. Where a tool misbehaves, a second clone is a legitimate fallback β€” the comparison is laid out in worktrees vs multiple clones.

Finally, agree on a cleanup habit. A git worktree list that returns eleven entries, six of which point at branches merged months ago, is the state every team reaches without one. A weekly prune, or a shell alias that lists worktrees whose branches are already merged, keeps the list short enough to be useful. The mechanics of deciding what is safe to remove are the same as for remote branches, and are covered in cleaning up stale worktrees safely.

Configuration Reference Jump to heading

Command or settingEffectWhen to use
git worktree add <path> <ref>Creates a linked working copyThe everyday case
git worktree add -b <branch> <path>Creates a branch and a worktree togetherHandling an interruption
git worktree add --detach <path> <ref>Checks out without claiming a branchRead-only inspection, bisecting
git worktree list --porcelainMachine-readable inventoryScripts and cleanup checks
git worktree remove <path>Removes directory and bookkeepingAlways prefer this to rm -rf
git worktree pruneClears entries for deleted directoriesAfter a manual deletion
extensions.worktreeConfigEnables per-worktree config valuesDifferent identity per worktree
git worktree lockPrevents pruning of a removable volumeWorktrees on external media

Troubleshooting Jump to heading

SymptomLikely causeFix
already checked outThe branch is claimed by another worktreeUse --detach, or work on a different branch
Worktree directory gone but branch still lockedDirectory deleted without removegit worktree prune
Build fails only in the new worktreeDependencies or ignored config are missingInstall dependencies; copy local env files
Two worktrees rebuilding each other’s cacheBuild output keyed on project name, not pathSet an explicit per-worktree output directory
remove refuses to runUncommitted changes presentCommit, stash, or review before forcing
Worktree on a removable disk pruned itselfGit could not see the pathgit worktree lock while it is detached

Frequently Asked Questions Jump to heading

Is a worktree just a cheaper clone? Jump to heading

It is cheaper in the way that matters β€” disk and time β€” but it is not isolated. The branches and the object store are shared, so a rewrite performed in one worktree is immediately visible in the others. That sharing is the feature; it is also the reason a genuinely independent experiment sometimes wants a real clone.

Can I have a worktree on a different filesystem? Jump to heading

Yes, and it works well, but a worktree on removable media will be pruned if Git cannot see the path when it runs housekeeping. git worktree lock prevents that, and unlock restores normal behaviour when the volume is back.

Do worktrees help with large repositories? Jump to heading

Substantially, because the expensive part of a second copy is the object transfer and worktrees skip it entirely. Combined with the sparse checkout technique in sparse-checkout for large monorepos, a worktree over a large repository can be both quick to create and small on disk.

What happens to a worktree when I rewrite history? Jump to heading

The rewrite applies to the shared refs, so the other worktree’s HEAD may end up pointing at a commit that is no longer on its branch. Git will tell you the branch has diverged rather than silently losing anything, but it is a good reason to avoid rewriting a branch that another worktree has checked out.

Do worktrees work well with containerised development? Jump to heading

They work, with one adjustment: container mounts are per directory, so each worktree needs its own container or its own mount configuration. Development setups that assume a single project directory β€” a fixed volume path, a container name derived from the project rather than the path β€” will have two worktrees fighting over one container, and the symptom is a build that succeeds once and then behaves as though files are missing.

The fix is to derive container names and volume paths from the worktree directory rather than from the repository name, which most tooling supports through an environment variable. Where it does not, running the second worktree’s container with an explicit project name is usually a one-line override. It is worth testing this before adopting worktrees on a containerised project, because discovering it midway through an urgent fix is the situation the technique was supposed to prevent.