Worktrees, submodules and large repositories
Several working trees from one repository, submodules and subtrees for shared code, sparse checkout and partial clones, and what actually makes a clone slow.
Worktrees
# a second working tree for the same repository
git worktree add ../project-hotfix main
git worktree add -b release/2.4 ../project-2.4 main
git worktree list
git worktree remove ../project-hotfix
git worktree prune # clean up metadata for trees deleted by hand
# each worktree has its own files and index, and shares the object store
cd ../project-hotfix && git statusA worktree lets two branches be checked out at the same time in different directories, with no second clone and no stashing. The object database is shared, so disk usage grows only by the checked-out files.
- Ideal for reviewing a pull request while your own work stays untouched, or for running a long build on
mainwhile you edit a branch. - A branch can be checked out in only one worktree at a time, which prevents the confusion of two directories editing the same branch.
- Deleting a worktree directory by hand leaves stale metadata behind; run
git worktree pruneto clean it up. - A worktree is still a full checkout, so for a huge monorepo consider a sparse checkout in addition.
Submodules and subtrees
git submodule add https://github.com/org/shared-lib vendor/shared-lib
git commit -m "Add shared-lib as a submodule"
# cloning a repository that has them
git clone --recurse-submodules <url>
# or initialise them in an existing clone
git submodule update --init --recursive
git submodule update --remote # move to the submodule's latest commit
git submodule status
# the parent records a commit id, so the diff shows a pointer change
git diff --submodule- The superproject records one specific commit per submodule: updating the library is a commit in the parent repository, not an automatic sync.
- Everyone who clones must remember
--recurse-submodules; without it the directories are empty and the build fails in a confusing way. - Submodules are separate repositories with their own remotes and detached checkouts, so commits made inside one are easy to lose if you forget to push them.
- A subtree copies the other project's files and history into yours: no clone step for consumers, but duplicated code and updates that are ordinary merges.
⚠️
A submodule pinned to a commit nobody else has fetched is the classic broken build: the parent repository looks fine and the CI clone fails. Prefer a package registry for anything versioned, and use submodules only for repositories you genuinely must co-develop.
Making a large repository bearable
# sparse checkout: only materialise the directories you need
git sparse-checkout init --cone
git sparse-checkout set apps/web packages/ui
git sparse-checkout list
git sparse-checkout disable
# shallow and partial clones
git clone --depth 1 <url> # one commit, no history
git clone --filter=blob:none <url> # full history, file contents on demand
git clone --filter=blob:none --sparse <url> # both
git fetch --unshallow # get the history later when you need it| Problem | Technique | Trade-off |
|---|---|---|
| Huge repository, CI needs one application | Sparse checkout with --cone | Some tools still walk the whole tree |
| Clone takes minutes | --depth 1 | No history: blame, bisect and most merges need more |
| Large binaries throughout history | --filter=blob:none or Git LFS | The server must support partial clone or LFS |
| Repository size driven by assets | Git LFS | Everyone must install it; existing blobs need a rewrite |
| History itself is enormous | Shallow clone plus a periodic re-clone in CI | Limited archaeology on older commits |
- Measure before optimising:
git count-objects -vHtells you whether size comes from history, packs or loose objects. - A deep clone in CI is usually unnecessary — most jobs need the checkout, not ten years of commits.
- Sparse checkout reduces the working tree, not the object store; a partial clone reduces the download.
FAQ
Worktree or a second clone?
A worktree when you are working in the same repository and want shared objects with no extra fetching. A second clone when you want isolation: separate configuration, separate hooks, and no chance of one checkout disturbing the other.
Submodule or subtree?
Submodules keep a clear boundary and pin an exact commit, but add a step to every clone. Subtrees are simpler for consumers and messier for contributors. If the shared code can be published as a package, that is usually better than either.
Related
Repository health, LFS and recovery Installing Git and configuring your environment
Last refreshed 2026-09-18.