Git Worktrees: Shared Caches, Faster Branch Switching

Why `git worktree` is underused, how to set up two or three worktrees for parallel feature work, and what gity does to keep status caches warm across them.

  • #worktree
  • #git
  • #workflow
  • #performance

git worktree is one of Git’s most powerful and least-used features. It lets you have multiple branches of the same repository checked out at the same time, in different directories, sharing the same object store. For anyone working in a large repo, it’s transformative — and the bigger the repo, the better worktrees get.

The problem worktrees solve

Without worktrees, switching branches looks like this:

# You're working on feature-x.
git status                 # 800ms in a 200k-file monorepo
# An urgent bug lands on main. Save your work.
git stash
git checkout main          # tear down feature-x's working tree, set up main's — 6 seconds
# Fix the bug.
git commit -am "fix: ..."
git push
git checkout feature-x     # tear down again — another 6 seconds
git stash pop
# Where was I?

Every context switch costs you 12 seconds of Git plus the cognitive cost of remembering where you were. Plus your IDE has to re-index. Plus your build cache is invalidated.

With worktrees:

# You're working on feature-x in ~/work/monorepo-feature-x.
# An urgent bug lands on main.
cd ~/work/monorepo-main    # already checked out, instant
git pull
# Fix the bug.
git commit -am "fix: ..."
git push
cd ~/work/monorepo-feature-x   # back to where you were, instant
# IDE state, builds, terminal history — all intact.

The branch switch becomes a cd. No git checkout, no working-tree rebuild, no re-indexing.

How to set up worktrees

# Inside an existing clone:
cd ~/work/monorepo
git worktree add ../monorepo-feature-x feature/x
git worktree add ../monorepo-hotfix    hotfix/login

You now have three worktrees:

  • ~/work/monorepo — main
  • ~/work/monorepo-feature-x — feature/x
  • ~/work/monorepo-hotfix — hotfix/login

All three share the same .git/ object store (the second and third have a .git file pointing to the primary’s .git directory). New commits in any worktree are immediately visible to the others.

To remove a worktree:

git worktree remove ../monorepo-hotfix

Disk usage math

The object store is shared, so duplication is only the materialized working tree. For a 5GB repo with 250k files and a checkout size of 1GB:

  • 1 worktree: ~6GB total
  • 3 worktrees: ~5GB shared + 3GB materialized = ~8GB total

Not free, but much cheaper than three full clones. And the deduplication wins compound as the object store grows (it’s almost always larger than any single working tree).

Where worktrees get awkward

A few things to know:

  • Submodules: each worktree has its own submodule checkout. Worktrees + submodules works but is more setup.
  • Hooks: hooks in .git/hooks/ are shared across worktrees. If you need worktree-specific hooks, set core.hooksPath.
  • Branches: you can’t have the same branch checked out in two worktrees. Git will refuse with “fatal: ‘main’ is already checked out at …” This is a feature — prevents conflicting commits.
  • IDE indexing: most IDEs treat each worktree as an independent project. Three open IDEs, three indexing jobs. Tolerable, but be aware.

What gity does for worktrees

When you gity register multiple worktrees of the same repo, gity recognizes that they share an object store. The benefits:

Shared status cache

The hottest cache entries (commit objects, tree objects, recent blob references) are replicated across the worktrees’ fsmonitor caches. After switching from monorepo-main to monorepo-feature-x via cd, the first git status in the new worktree hits the warm cache instead of cold.

This matters because the obvious win of worktrees — “no git checkout cost” — comes with a subtler loss: each worktree’s fsmonitor cache is naive. Without coordination, the first status call in a worktree you haven’t visited in an hour walks the tree from scratch.

gity’s replication layer (built on the rykv crate) fixes this. The cache warms as soon as the first worktree warms, and stays warm for the others.

Shared maintenance

git maintenance runs against the shared object store, not the working tree. So one maintenance run benefits all worktrees. gity schedules it once per object store, not once per registered worktree — saving CPU and battery for multi-worktree power users.

Independent watcher state

Each worktree still has its own file watcher (changes in one worktree don’t trigger events in another). Only the cache layer is shared. So no false positives, no spooky action at a distance.

A common pattern: trunk-based dev + hotfix worktree

If your team does trunk-based development, the most useful setup is:

git clone <repo>                 # ~/work/monorepo, checked out on main
git worktree add ../monorepo-feature feature/$(date +%Y-%m-%d)

The primary worktree stays on main, ready for an emergency hotfix at any moment. Your day-to-day work happens in the second worktree. When a hotfix lands, you cd to the primary, do the fix, push, and cd back.

Add a third one if you frequently context-switch between two features. Most engineers don’t need more than three concurrent worktrees.

Try it

cargo install gity
git worktree add ../monorepo-feature feature/x
gity register ~/work/monorepo
gity register ~/work/monorepo-feature
cd ~/work/monorepo-feature
git status   # cache primed from the main worktree

If you’ve been living in a single worktree because branch-switching was a pain, this combination is liberating. The mental tax of “should I switch branches now or finish this thought?” disappears.

Frequently asked questions

What's the difference between a worktree and a branch?

A branch is a pointer to a commit; multiple branches can exist in a single working tree. A worktree is an *additional* directory that checks out a different branch of the same repository — so you can have main checked out in one folder and feature-x in another, sharing the same object store.

Why use worktrees instead of just switching branches?

Switching branches with `git checkout` re-materializes the working tree, runs status against the new HEAD, and invalidates any in-progress build. Worktrees let you have multiple branches concurrently materialized — switch by `cd`, not `git checkout`.

Don't worktrees duplicate disk usage?

The working tree contents are duplicated (one tree per worktree), but the object store is shared. So three worktrees of a 5GB repo use roughly 5GB + (3 × working-tree-size), not 15GB.