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 没有一键删除命令,需要手动四步:

  1. .gitmodules 文件中删除对应配置段
  2. .git/config 文件中删除对应配置段
  3. 删除子模块目录并取消追踪:git rm --cached
  4. 删除 .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