Appearance
子树(Subtree)
Git Subtree 是将外部仓库代码整合到当前仓库的另一种方式,比 Submodule 更简单,对使用者更透明。
什么是子树及适用场景
Subtree 通过将外部仓库的历史合并到主仓库,而不是作为独立的嵌套仓库。对于不了解 subtree 的团队成员,它看起来就是普通的目录。
适用场景:
- 想引入外部代码但不想让团队感知复杂性
- 需要修改引入的外部代码(比 submodule 更方便)
- 偶尔需要同步外部代码更新
- 希望克隆仓库时不需要额外操作
git subtree add 添加子树
bash
# 添加外部仓库作为子树
# 语法:git subtree add --prefix=<目录> <仓库URL> <分支> --squash
git subtree add --prefix=libs/utils https://github.com/user/utils.git main --squash
# --squash:将外部仓库的所有历史压缩为一个提交(推荐,避免历史污染)
# 不加 --squash 则引入完整的外部仓库历史添加后的效果:
libs/utils/目录包含外部仓库的文件- 主仓库包含一个合并提交(引入 utils 的历史)
- 对不了解 subtree 的人,看起来就是普通目录
git subtree pull 同步更新
当外部仓库有更新时,拉取最新代码:
bash
# 同步外部仓库的更新
git subtree pull --prefix=libs/utils https://github.com/user/utils.git main --squashgit subtree push 推回修改
如果在主仓库修改了 subtree 目录的代码,可以推回外部仓库:
bash
# 将 libs/utils 目录的改动推送到外部仓库
git subtree push --prefix=libs/utils https://github.com/user/utils.git main
# 或推送到新分支
git subtree push --prefix=libs/utils https://github.com/user/utils.git feature/my-changes使用远程引用简化命令
每次都写完整 URL 很麻烦,可以先添加远程引用:
bash
# 添加远程引用
git remote add utils https://github.com/user/utils.git
# 添加子树
git subtree add --prefix=libs/utils utils main --squash
# 更新子树
git subtree pull --prefix=libs/utils utils main --squash
# 推回修改
git subtree push --prefix=libs/utils utils main与 Submodule 的对比选择
| 对比项 | Subtree | Submodule |
|---|---|---|
| 对团队透明度 | 高(普通目录) | 低(需要了解 submodule) |
| 克隆复杂度 | 低(普通克隆即可) | 高(需要 --recurse-submodules) |
| 历史追踪 | 混合在一起 | 完全独立 |
| 更新同步 | 手动执行 pull | 手动执行 update |
| 向上游贡献 | 通过 subtree push | 直接在子模块目录操作 |
| 磁盘空间 | 与主仓库共享 | 独立 .git 目录 |
| 学习成本 | 低 | 高 |
| 适合场景 | 偶尔同步的外部代码 | 频繁更新的依赖 |
总结
| 操作 | 命令 |
|---|---|
| 添加子树 | git subtree add --prefix=<path> <remote> <branch> --squash |
| 拉取更新 | git subtree pull --prefix=<path> <remote> <branch> --squash |
| 推回修改 | git subtree push --prefix=<path> <remote> <branch> |
Subtree 更适合希望保持工作流简单的团队。如果外部代码变化不频繁,或者团队 Git 经验有限,Subtree 是比 Submodule 更好的选择。