掌握 Git Submodule 的添加、更新、克隆和移除,学会在一个项目中引用和管理其他 Git 仓库。
有时候你的项目需要引用另一个 Git 仓库的代码:公共组件库、配置文件、文档站点……直接复制代码会导致版本同步困难,Submodule 就是解决这个问题的方案。
一、
什么是 Submodule?
Submodule 允许你在一个 Git 仓库中嵌入另一个独立的 Git 仓库。子模块有自己的提交历史、分支和远程地址,父仓库只记录它指向的特定提交。
1 2 3 4 5 6 7 8 9
| my-project/ ← 父仓库 ├── .gitmodules ← 子模块配置文件 ├── src/ ├── libs/ │ └── common-lib/ ← 子模块(独立的 Git 仓库) │ ├── .git/ │ ├── src/ │ └── README.md └── README.md
|
父仓库记录了什么?
1 2
| git ls-tree HEAD libs/common-lib
|
父仓库只保存子模块的一个 commit hash,不保存子模块的文件内容。
二、
添加子模块
1
| git submodule add <仓库地址> <本地路径>
|
示例:
1 2
| git submodule add https://gitee.com/team/common-lib.git libs/common-lib
|
执行后会产生两个变化:
记得提交 .gitmodules:这个文件必须和子模块一起提交到父仓库,否则别人 clone 后不知道子模块的信息。
指定分支
1 2
| git submodule add -b develop https://gitee.com/team/common-lib.git libs/common-lib
|
三、
克隆包含子模块的项目
方式 1:clone 时递归初始化
1 2
| git clone --recurse-submodules https://gitee.com/team/my-project.git
|
方式 2:先 clone 再初始化
1 2 3 4 5 6
| git clone https://gitee.com/team/my-project.git
git submodule init git submodule update
|
推荐
用 --recurse-submodules 一步到位,不容易漏。
嵌套子模块
如果子模块里还有子模块:
1 2
| git submodule update --init --recursive
|
四、
更新子模块
子模块是独立仓库,父仓库不会自动跟踪子模块的新提交。需要手动更新。
拉取子模块的最新代码
1 2 3 4 5 6 7 8
| cd libs/common-lib git pull origin main
cd ../.. git add libs/common-lib git commit -m "chore: 更新 common-lib 到最新版本"
|
批量更新所有子模块
1 2
| git submodule update --remote
|
两种方式的区别:
git pull(进入子模块):更新到子模块远程分支的最新提交git submodule update:切换到父仓库记录的特定提交
前者是”拉最新”,后者是”同步到父仓库指定的版本”。
五、
查看子模块状态
1 2 3 4 5 6 7 8
| git submodule status
|
1 2 3 4
| git submodule summary
|
六、
在子模块中开发
子模块本身就是一个完整的 Git 仓库,你可以在里面正常开发:
1 2 3 4 5 6 7 8 9 10 11 12 13
| cd libs/common-lib
git checkout -b feature/new-api vim src/api.js git add . git commit -m "feat: 添加新接口" git push origin feature/new-api
cd ../.. git add libs/common-lib git commit -m "chore: 更新 common-lib 接口"
|
子模块默认在游离头指针状态:git submodule update 后,子模块处于 detached HEAD。如果要在子模块中开发,先 git checkout main 切到一个分支上。
七、
移除子模块
Git 没有提供一步删除子模块的命令,需要手动操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| git rm -r libs/common-lib
vim .gitmodules
git config --remove-section submodule.libs/common-lib
rm -rf .git/modules/libs/common-lib
git commit -m "chore: 移除 common-lib 子模块"
|
注意
第 1 步 git rm 会同时删除子模块目录和更新索引,但 .git/modules/ 下的缓存需要手动清理。
八、
常见问题
问题 1:clone 后子模块目录为空
1 2 3
| git submodule init git submodule update
|
问题 2:子模块提交后父仓库显示 modified
1 2 3 4 5 6 7
| git submodule status
git add libs/common-lib git commit -m "chore: 更新子模块引用"
|
问题 3:切换父仓库分支后子模块不对
总结:Submodule 速查卡
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| ┌──────────────────────────────────────────────────────────┐ │ Git Submodule 速查 │ ├────────────────┬─────────────────────────────────────────┤ │ 添加子模块 │ git submodule add <url> <path> │ │ 指定分支添加 │ git submodule add -b <branch> <url> │ ├────────────────┼─────────────────────────────────────────┤ │ 克隆含子模块 │ git clone --recurse-submodules <url> │ │ 初始化+拉取 │ git submodule init + update │ │ 递归拉取 │ git submodule update --init --recursive │ ├────────────────┼─────────────────────────────────────────┤ │ 更新到远程最新 │ git submodule update --remote │ │ 查看状态 │ git submodule status │ ├────────────────┼─────────────────────────────────────────┤ │ 移除子模块 │ git rm + 清理 .gitmodules + .git/modules│ └────────────────┴─────────────────────────────────────────┘
|