Use git subtree when you want vendored code to keep a link to its upstream, so you can pull updates and see changes. Use a plain copy only for tiny, rarely changing files where you record the origin by hand. A plain copy has no built in way to tell you it is out of date.
What each approach is
Plain copy. You download a release or copy files into a directory and commit them. From Git's view they are your files.
Git subtree. The Git subtree documentation says subtrees allow subprojects to be included within a subdirectory of the main project, optionally including the subproject's history. The add command imports the subproject, and the pull and merge commands bring in later changes. By default the subproject's history joins yours. With the squash option you import only a single commit from the subproject rather than its whole history.
git subtree add --prefix=third_party/examplelib https://github.com/example/examplelib.git v1.4.2 --squash
git subtree pull --prefix=third_party/examplelib https://github.com/example/examplelib.git v1.5.0 --squash
What matters for security
| Question | Plain copy | Subtree |
|---|---|---|
| Can you see what version it is | Only if you wrote it down | The import commit records the source and ref |
| Can you diff against upstream | Manual download and compare | Fetch and diff |
| Can you update | Manual replace | One pull command |
| Do scanners see it | Often not, no manifest | Often not, still no manifest |
| Can you carry local patches | Yes, but invisible | Yes, as commits you can list |
| Clone size | Small | Larger without the squash option |
The scanner row is the same for both, and it is the key danger. Neither approach creates a package manifest, so tools that read manifests may never see the code. See vendored code and submodule scanning blind spots.
Making either one auditable
- Put the code in a clearly named directory such as third_party.
- Add a short provenance file beside it. See what to record in a vendored code provenance file.
- Keep your local edits in separate commits with clear messages, so the diff against upstream is readable.
- Add the component to your SBOM by hand or with a tool that scans for copied code. See copied code and your SBOM.
- Include third_party in security scans, not only in license checks. Many setups exclude it by default.
When a plain copy is fine
A single small file with a permissive license, copied once, with a header comment naming the source and version, is reasonable. Anything with a release cycle, a security history or local patches deserves subtree or a proper dependency.
When neither is right
If you copy only to avoid network access at build time, a mirror or a lockfile with a vendor directory may suit you better. See vendoring or using a proxy.
Where Safeguard fits
Safeguard's SBOM and dependency scanning is the layer to check that your vendored directories are visible in your inventory. Try a free scan at gold.safeguard.sh/scan.
Try it yourself
Click through it at your own pace, or open it full screen. Compare what appears in an inventory built from manifests with what a vendored directory would add.
Quick check
Does subtree make vendored code visible to scanners?
Not by itself. Subtree improves tracking in Git, but it creates no package manifest, so record the component in your SBOM separately.
What does the squash option do?
It imports a single commit from the subproject instead of its full history, which keeps your repository smaller.
Sources
Last reviewed: 19 October 2026.