Free — no signup required

Git Subtrees: The Alternative

3 min read

A Different Philosophy

Where submodules store a pointer to another repository, Git Subtree physically copies the source code and commit history of the external project into your repository. From that point on, the external code lives inside your repo as ordinary files — no special commands needed to access it.

Adding a subtree means pointing at the external repository and merging its history into a chosen subdirectory of the local repo. A squash option collapses the external repo's entire history into a single commit, keeping the local repo's log clean; without it, every commit from the external repo gets imported too, which can add thousands of entries to the log.

Submodule vs. Subtree: A Direct Comparison

Dimension Submodules Subtrees
Storage Pointer (SHA only) Full file copy + history
Clone experience Empty directory; requires --recurse-submodules Files present immediately
Repo size Small Grows with each external project
Pinning to a version Explicit and strict Possible but less obvious
Modifying upstream Requires branch + push to submodule remote git subtree push (slow on large repos)
Team friction High (easy to forget submodule update) Low (feels like a normal folder)
CI complexity Must add submodule update step No special steps

Pushing Changes Back Upstream

The hardest part of subtrees is contributing changes back to the original repository. Pushing a subtree's changes upstream requires Git to scan the entire commit history to extract only the commits that touched the subtree's prefix — on a large repository with years of history, that scan can take minutes. This is the primary operational cost of subtrees over submodules.

When to Use Each

  • Use Submodules when you need strict version pinning of a dependency you rarely modify (e.g., a vendored binary tool or a frozen third-party library).
  • Use Subtrees when you want to merge an external project and treat it as your own code, modifying it frequently and accepting the larger repo size.
  • Use a Package Manager (npm, pip, Go modules, Maven) in almost every other case. Package managers were invented precisely to solve the dependency management problem that submodules and subtrees address awkwardly. If your language ecosystem has a package manager, prefer it.
Interview Tip

A common platform engineering interview question is: "How would you share code between multiple microservice repositories?" The answer has three tiers: (1) package manager with a private registry — best for most cases; (2) git subtree — good when you want the code embedded and modified locally; (3) git submodule — good for strict version pinning of rarely-modified dependencies. Knowing all three and their trade-offs, rather than defaulting to one, signals senior-level thinking.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free