子模块与子树怎么选
两者都是「把一个仓库引入另一个仓库」的方案,但机制相反:子模块(submodule)在父仓库里保存一个指向对方仓库某次提交的指针,对方仓库与历史保持独立;子树(subtree)把对方的代码以普通目录形式并进本仓库历史,克隆本仓库即可完整构建。选型的关键是「组件是否独立演进、团队是否接受多一步初始化」;拿不准时先看对方是否被多个项目共用。
基本信息
| 子模块本质 | 父仓库存指针(gitlink),不保存对方文件历史 |
|---|---|
| 子树本质 | 对方内容并入指定前缀目录,历史合入本仓库 |
| 关键命令 | git submodule update --init --recursive;git subtree add --prefix=目录 仓库 分支 |
| 上游贡献 | 子模块直接向对方仓库提 PR;子树用 subtree split 拆出提交再推 |
| 地址维护 | 子模块地址记录在 .gitmodules,改地址要同步提交 |
对比
| 维度 | 子模块 | 子树 subtree |
|---|---|---|
| 克隆体验 | 需 --recurse-submodules | 普通克隆即可 |
| 历史 | 独立仓库历史 | 合入本仓库历史 |
| 更新方式 | 更新指针 / --remote | subtree pull/push |
| 贡献回上游 | 直接向子模块仓库提 PR | 用 subtree split 生成提交 |
| 适合 | 共享库、插件 | 少量嵌入式代码、单仓构建 |
快速判断
快速判断
组件独立发版、被多仓复用 → 子模块
只想单仓构建、改动频率低 → 子树
需要严格版本对齐与审计 → 子模块 + 固定指针两种方案都要求团队约定更新节奏;混用时更新责任不清,通常只选一种。审计场景关注「当前指向哪次提交」,这正是子模块指针与子树合并记录都能回答的问题,但查法不同。