子仓库怎么塞进父仓库:Submodule 与 Subtree 对比

发表于 2025-07-01 787 字 4 min read

文章目录
对比 Git Submodule 与 Subtree 两种在仓库内嵌子仓库的方案与用法。

我第一次遇到”把仓库塞进仓库”的需求,是想把一个公共组件仓库合进自己的项目。当时图省事直接复制代码,结果人家一更新我就得手动同步,烦得不行。后来才搞明白,Git 里正儿八经有两条路子:Git Submodule 和 Git Subtree。这俩我都试过,各有各的脾气,下面按我实际用的顺序分别说说。


一、Git Submodule

Submodule 会在你的主仓库中保留一个对子仓库的引用(commit ID),子仓库的源码存放在一个独立目录下。

1. 添加子模块

# 进入主仓库目录
cd your-main-repo

# 添加子模块:将子仓库克隆到 subdir 目录
git submodule add https://github.com/username/other-repo.git subdir

这时,主仓库会新增一个 .gitmodules 文件,记录子模块的 URL 和路径,并在 subdir 目录下生成一个指向子仓库特定提交的指针。

2. 提交变更

git add .gitmodules subdir
git commit -m "Add other-repo as submodule in subdir"
git push

3. 克隆包含子模块的仓库

其他人克隆时,需要加上 --recurse-submodules,或者在克隆后执行初始化:

# 克隆并自动初始化子模块
git clone --recurse-submodules https://github.com/yourname/your-main-repo.git

# 如果已经克隆过,执行:
git submodule init
git submodule update

4. 更新子模块

如果子仓库有新提交,需要在主仓库里更新引用:

# 进入子模块目录,拉取最新
cd subdir
git pull origin main   # 或者其他分支

# 返回主仓库,提交更新的子模块引用
cd ..
git add subdir
git commit -m "Update submodule to latest"
git push

二、Git Subtree

Subtree 会把子仓库的代码合并到主仓库的某个子目录下,历史记录也被合并到你的主仓库中,不需要 .gitmodules。

1. 添加子树

# 在主仓库中将 other-repo 的 main 分支作为子树引入到 subdir
git remote add other-repo https://github.com/username/other-repo.git
git fetch other-repo
git subtree add --prefix=subdir other-repo main --squash
  • --prefix=subdir:要将子仓库放在哪个子目录
  • other-repo main:指定远端名和分支
  • --squash:将子仓库历史压缩为一个合并提交(可选)

2. 更新子树

当子仓库有新提交时,拉取并合并到子目录:

git fetch other-repo
git subtree pull --prefix=subdir other-repo main --squash

如果想保留完整历史,可去掉 --squash。


小结

特性SubmoduleSubtree
仓库历史独立管理,主仓库只记录引用合并到主仓库历史
克隆依赖需额外初始化 (--recurse-submodules)像普通代码一样直接克隆
灵活性可以切换子模块到任意提交更新操作稍重,需要合并
适用场景子仓库独立发展,修改频率低需要把子项目完全打包进主仓库,或不想管理多个仓库依赖
  • 子模块(Submodule):适合子项目独立开发、并且需要在不同主仓库间共享同一份代码的场景。
  • 子树(Subtree):适合你希望把子项目的代码完全纳入主仓库,并且减少操作步骤的场景。