The subtree model
A subtree merges another repository's history into a subdirectory of yours. Unlike submodules, there is no separate gitlink and no extra commands required for clone — anyone who clones your repo gets the subtree contents automatically. The cost is a fatter history; the benefit is operational simplicity.
Adding a subtree
git subtree add --prefix=third_party/lib \
https://github.com/example/lib main --squash
The --squash flag collapses the imported history into a single commit, keeping your log clean. Omit it if you want full upstream history.
Pulling updates
git subtree pull --prefix=third_party/lib \
https://github.com/example/lib main --squash
Add a remote alias to avoid retyping the URL:
git remote add lib https://github.com/example/lib
git subtree pull --prefix=third_party/lib lib main --squash
Pushing back upstream
You can extract changes you made under the prefix and send them upstream:
git subtree push --prefix=third_party/lib lib feature-branch
Subtree vs submodule
Choose subtrees when you want a frictionless clone for newcomers, when you rarely modify the vendored code, or when CI complexity around submodules has bitten you. Choose submodules when you need to pin to an exact upstream SHA, want to keep histories strictly separate, or have many projects that share the same dependency at different versions.
Splitting a subdirectory
If you later want to extract the subtree as its own repo:
git subtree split --prefix=third_party/lib -b lib-only
cd ..
git clone /path/to/parent --branch lib-only lib-extracted
Common mistakes
Forgetting --squash imports tens of thousands of upstream commits — annoying for git log readers. Mixing edits inside the prefix with edits outside in the same commit makes subtree push noisy. Keep prefix-touching commits separate. Remember that git subtree is a contributed porcelain script; some distros omit it. Verify with git subtree --help.
Alternative: contrib/subtree merge strategy
For full control you can do subtree merges manually:
git remote add -f lib https://github.com/example/lib
git merge -s ours --no-commit --allow-unrelated-histories lib/main
git read-tree --prefix=third_party/lib/ -u lib/main
git commit -m "Subtree merge of lib"
Related
See "Submodules: adding and updating" and "filter-repo: rewriting history safely" for repository surgery techniques.