Skip to content

子树(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 --squash

git 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 的对比选择

对比项SubtreeSubmodule
对团队透明度高(普通目录)低(需要了解 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 更好的选择。