A worktree is an additional checkout attached to a repository. Instead of repeatedly switching one checkout, stashing files, or cloning the repository again, you keep a few working trees around and let each one keep its own state.
It is one of Git's best workflow tools when you are moving between a feature branch, a hotfix, a benchmark, a docs edit, or an agent run at the same time. It has been underused for years, but agent workflows make it essential.
Another Checkout, Attached to One Repository
- Linked Worktree
- Additional working tree attached to the same repository, sharing the common object store while keeping its own checkout state.
A linked worktree is another checkout attached to the same repository, sharing one object database, packfiles, and commit-graph rather than carrying its own.
That means multiple worktrees share the common object store and most repository-level data, while each worktree still has its own:
- working-tree files on disk
HEAD- index
FETCH_HEADand other pseudo-refs- local checkout state during merges, rebases, and similar operations
That setup lets one worktree sit on main, another hold a release branch, and a third stay in detached HEAD for a benchmark or bisect. They are separate checkouts over one shared repository.
On first use, worktrees are just convenience: no stashing, no branch switching, no duplicate clone. The bigger gain appears later or in agent flows.
Stable paths mean long-running local state stays attached to the task that created it. Editors, language servers, test runners, benchmark scripts, container mounts, and shell history all behave more predictably. Separate indexes matter too. Each worktree has its own index, so staging and checkout state do not collide across tasks. That is cleaner for humans, and it also avoids a surprising amount of mechanical friction in automation-heavy workflows: fewer accidental staging mistakes, less index rewrite churn, and less contention around one mutable checkout trying to serve several workers at once.
Isolated build state completes the story. Node modules, Python virtual environments, generated files, caches, and benchmark outputs all tend to stick to the worktree that produced them. That keeps a docs task from inheriting a release build's byproducts and keeps a benchmark run from sharing a half-mutated tree with an unrelated fix.
Give each task its own:
- worktree
- branch
- index
- stable path
- test or build state
Git's Branch Safety Rule Helps
One of Git's most useful safeguards is that a branch generally should only be checked out in one worktree at a time. Git refuses the overlapping checkout by default unless you force it. That rule can look annoying until you remember what a branch is: a movable ref. If two active workers both treat that branch as their live checkout, it becomes easy for one to move the ref while the other still assumes an older position. So the safety rule nudges you toward the cleaner pattern of one active branch, one active worktree, and one active worker attached to that branch.
Preserving Expensive Local State
Without worktrees, an interruption often turns into one of these patterns:
- stash or commit in-progress work
- switch branches
- rewrite the index and working tree
- switch back later
- restore the earlier state
Or, if the second task needs to stay around:
- clone the repository again
- fetch the same objects again
- maintain another object store and another checkout
Worktrees avoid both patterns. They let you take the checkout cost once, keep that context around, and move to another context without tearing the first one down.
That matters more as repositories get larger. Rewriting a large working tree is real work. Rebuilding one large index over and over is real work. Recloning a repository with a large history or large packfiles is definitely real work. Restarting a long-running test or losing a carefully prepared build cache because one shared checkout had to switch branches is also real work.
Customizing a Worktree Before Checkout
git worktree add --no-checkout is useful when you want to customize a new worktree before the checkout, including sparse-checkout setup.
A practical pattern is:
- create a worktree for the task
- choose or create the task branch
- apply a sparse-checkout pattern if the task only needs part of the repository
- run the human or agent workflow inside that prepared context
Not every task needs the same local checkout. A test fix may need one directory. A docs change may need another. A release-prep worktree may need a broader slice. Worktrees plus sparse-checkout let you shape each context to the job instead of forcing one dense checkout to serve everything.
Another important operational detail is that you can enable extensions.worktreeConfig and then use git config --worktree for settings that should stay local to one worktree.
To enable this, first set the extension in the shared repository config:
git config extensions.worktreeConfig true
Then, inside a specific worktree, use --worktree to write settings that apply only there:
git config --worktree core.sparseCheckout true
git config --worktree core.sparseCheckoutCone true
git config --worktree core.fsmonitor false
These settings live in a per-worktree config.worktree file (.git/config.worktree for the main worktree, .git/worktrees/<name>/config.worktree for linked worktrees; git rev-parse --git-path config.worktree prints the exact path). Each worktree can then have its own sparse-checkout cone, its own fsmonitor behavior, or its own experimental flags without affecting other worktrees.
Settings like core.sparseCheckout generally should not be shared unless you really mean to use sparse-checkout across all worktrees. Different worktrees may reasonably want different:
- sparse-checkout settings
- fsmonitor behavior (some worktrees may be on different filesystems)
- temporary tool configuration
- hooks behavior
- feature flags for experiments
Lifecycle Commands
The basic add flow gets most of the attention, but the rest of the lifecycle matters if you want a multi-worktree setup to stay healthy over time.
git worktree listshows what is attachedgit worktree addcreates a new checkoutgit worktree removecleans one up deliberatelygit worktree pruneremoves stale administrative records for worktrees that no longer existgit worktree lockprotects a worktree that lives on a removable or temporarily unavailable pathgit worktree moveandgit worktree repairhelp when paths change
A short lifecycle pass looks like:
git worktree add -b fix ../repo-fix main
git worktree list --porcelain
git worktree remove ../repo-fix
git worktree prune
That creates a task checkout, shows the attached worktrees in machine-readable form, removes the checkout, and cleans stale administrative records. This flow was always useful, but is now more important when worktrees are more disposable and more numerous. Some worktrees stay around for weeks; others exist only for a short benchmark, a reproduction, or an automated task. That makes cleanup and visibility part of normal operation rather than occasional housekeeping.
The older human use cases did not go away, either. Worktrees are still excellent for hotfixes without disturbing an ongoing refactor, release branches that need to stay checked out for days, bisect and benchmark sessions, and side-by-side review. The newer parallel-work story did not replace those cases. It broadened them.
Why Worktrees Usually Beat Duplicate Clones
A second clone duplicates repository administration, object transfer, local maintenance, and storage. A second worktree reuses the repository core and adds another checkout context.
Worktrees are usually the better answer when the problem is:
"I need another active context for the same repository."
A true second clone still has its place. Sometimes you really do want a fully separate repository boundary. But for most local parallel work inside the same repository, worktrees are the more economical and more legible choice.
The difference gets larger as repository size grows and as the number of active workers increases. Shared objects, shared packfiles, and shared metadata mean less duplicated transfer, less duplicated disk use, and less duplicated maintenance. Separate checkouts mean less switching churn and less accidental context mixing.
Pairing Worktrees with Sparse-Checkout
Sparse-checkout (Chapter 10) narrows what Git materializes in the working tree. Sparse-index narrows the index representation too. Worktrees pair with both.
One worktree can represent one task. Sparse-checkout lets that task materialize only the directories it actually needs. Sparse-index keeps the index cost closer to that task's real local footprint. That is a much better local model than one huge checkout for every task, or many duplicate clones each carrying dense state.
A practical pattern is: one repository, several worktrees, each worktree sparse to its task, sparse-index enabled where it helps. Each worktree carries only the cost of the work it actually does.
This is where extensions.worktreeConfig earns its keep. With worktree-local config enabled, each worktree gets its own core.sparseCheckout posture and its own cone, so one sparse task cannot accidentally redefine another task's local shape.
git config extensions.worktreeConfig true
git config --worktree core.sparseCheckout true
git config --worktree core.sparseCheckoutCone true
git config --worktree index.sparse true