Git 子模块相关功能和实用技巧
背景:AI 辅助开发效率提升
在 AI 辅助开发场景下,前端与后端往往需要同时查看和修改、联动调试。将前后端拆分为独立 Git 仓库,再通过子模块组合成一个父仓库,可以让 AI 工具(如 CodeMaker)在同一工作区同时感知前后端代码上下文,无需频繁切换目录,显著提升 AI 开发效率。
父仓库 = 只包含 .gitmodules 和 openspec 配置,不存业务代码
子模块 = 前端仓库 + 后端仓库,各自独立 CI/CD 和权限管理
AI 工具 打开父仓库根目录,即可同时索引前后端代码,实现跨仓库联动分析和修改
本项目配置参考(xxx-all)
以下是本项目的 .gitmodules 配置,包含前端和后端两个子模块,均追踪 master 分支:
[submodule "xxx-platform"]
path = xxx-platform
url = ssh://git@git.com:10022/xxx/xxx/xxx-platform.git
branch = master
[submodule "xxx-react"]
path = xxx-react
url = ssh://git@git.com:10022/xxx/xxx/xxx-react.git
branch = master
| 子模块 | 类型 | 说明 |
|---|---|---|
| xxx-platform | 后端 | Spring Boot 多模块 Maven 项目 |
| xxx-react | 前端 | React + TypeScript 项目 |
快速上手
# 首次克隆父仓库(同时拉取前后端子模块)
git clone --recurse-submodules ssh://git@git.com:10022/xxx/xxx/xxx-all.git
# 已克隆但子模块为空时
git submodule update --init --recursive
# 日常拉取(前后端一起更新)
git submodule foreach git pull origin master
# 添加子模块
git submodule add ssh://git@git.com:10022/xx/xx/xx-xx.git
用 AI 工具打开 xxx-all 根目录,即可同时获得前端(xxx-react/)和后端(xxx-platform/)的完整代码上下文,实现前后端联动开发。
一、什么是 Git 子模块
Git 子模块允许将一个 Git 仓库嵌套到另一个 Git 仓库中,作为其子目录存在。主仓库只记录子模块的仓库地址和特定 commit SHA,不追踪子模块的具体文件内容。
适用场景:共享公共库、第三方依赖管理、多项目协同开发。
二、基础操作
2.1 添加子模块
git submodule add <repo-url> [本地路径]
# 示例
git submodule add https://github.com/example/lib.git libs/lib
执行后会生成 .gitmodules 文件,记录子模块配置。
2.2 克隆含子模块的仓库
# 一步完成(推荐)
git clone --recurse-submodules <repo-url>
# 已克隆但未拉取子模块时补救
git submodule update --init --recursive
2.3 查看子模块状态
git submodule status
输出格式:[-/+/空格][SHA] 路径 (分支/标签),前缀含义:
空格— 已初始化且与主仓库记录一致-— 尚未初始化+— 子模块 HEAD 与主仓库记录的 commit 不同
三、更新与同步
3.1 拉取子模块最新代码
# 更新所有子模块到主仓库记录的 commit
git submodule update --recursive
# 拉取子模块远程最新(不受主仓库 commit 约束)
git submodule update --remote --recursive
3.2 主仓库拉取后同步子模块
git pull
git submodule update --init --recursive
执行 git pull 后若子模块有变更,必须手动运行 submodule update,否则子模块仍停在旧 commit。
3.3 批量 foreach 操作
# 对所有子模块执行任意 git 命令
git submodule foreach git pull origin main
git submodule foreach git status
四、子模块内开发
4.1 进入子模块提交代码
cd libs/lib
git checkout main # 子模块默认是 detached HEAD,先 checkout 分支
# ... 修改代码 ...
git add .
git commit -m "feat: xxx"
git push
4.2 在主仓库更新子模块引用
cd ../../ # 回到主仓库根目录
git add libs/lib
git commit -m "chore: update submodule lib to latest"
git push
子模块的 commit 必须先推送到远程,主仓库才能让其他人 checkout 到该 commit。先推子模块,再推主仓库。
五、删除子模块
Git 没有一键删除命令,需要手动四步:
- 从
.gitmodules文件中删除对应配置段 - 从
.git/config文件中删除对应配置段 - 删除子模块目录并取消追踪:
git rm --cached - 删除
.git/modules/目录
# 快捷脚本(以 libs/lib 为例)
git submodule deinit -f libs/lib
git rm -f libs/lib
rm -rf .git/modules/libs/lib
git commit -m "chore: remove submodule lib"
六、常用实用技巧
6.1 全局配置自动同步
# 每次 git pull 自动更新子模块(Git 2.15+)
git config --global submodule.recurse true
6.2 浅克隆子模块(加速)
git clone --recurse-submodules --shallow-submodules <repo-url>
只拉取子模块最新 1 个 commit,大幅减少 CI/CD 场景的拉取时间。
6.3 指定子模块跟踪分支
# 在 .gitmodules 中设置追踪分支
git submodule set-branch --branch main libs/lib
git submodule update --remote libs/lib
6.4 diff 显示子模块变更详情
git diff --submodule=diff
默认 git diff 只显示 commit SHA 变化,加 --submodule=diff 可展示子模块内的文件级变更。
6.5 只初始化指定子模块
git submodule update --init libs/lib
七、常见问题排查
| 问题现象 | 原因 | 解决方案 |
|---|---|---|
| 子模块目录为空 | 未执行 init/update | git submodule update --init --recursive |
| 子模块处于 detached HEAD | update 默认不 checkout 分支 | 进入目录执行 git checkout |
| push 后别人拉不到子模块新 commit | 子模块未先 push | 先在子模块目录 push,再 push 主仓库 |
| clone 后子模块内容缺失 | 未加 --recurse-submodules | 执行 git submodule update --init --recursive |
| 子模块 commit 冲突 | 多人同时更新同一子模块引用 | 像解决普通文件冲突一样,手动选择正确的 commit SHA |
八、速查卡片
克隆:git clone --recurse-submodules
补救初始化:git submodule update --init --recursive
更新到主仓库记录版本:git submodule update --recursive
拉取子模块最新:git submodule update --remote --recursive
批量操作:git submodule foreach <命令>
删除子模块:git submodule deinit -f → git rm -f → 删 .git/modules/
自动同步配置:git config --global submodule.recurse true