首页/开发/Git 与协作速查/git submodule add 引入子模块
进阶与大仓实践69 条

git submodule add 引入子模块

把外部仓库以固定提交的形式嵌入当前仓库:父仓库只记录子模块的 URL、路径与提交指针,子模块内容单独克隆。

Git 与协作速查开发更新于 2026-10-13

子模块解决什么问题

子模块(submodule)把另一个 Git 仓库以固定提交的形式嵌入当前仓库:父仓库只保存子模块的 URL、检出路径,以及一个指向具体提交的 gitlink 记录,文件内容仍存放在子模块自己的仓库里。它适合「依赖的代码由其他团队或上游维护、但必须锁定到具体版本」的场景,代价是父子仓库要分别提交、分别推送。

基本信息

适用版本Git 1.6.5 起支持,Windows/Linux/macOS 行为一致
引入命令git submodule add <仓库URL> [<路径>]
父仓库记录什么.gitmodules(URL 与路径)+ gitlink(子模块提交哈希)
内容何时拉取clone 时加 --recurse-submodules,或事后 init 加 update
权限要求对子模块远端至少读权限;推送指针需本地写权限

典型使用场景

  • 跨团队共享的内部库:版本必须锁定,不允许随手跟进上游 HEAD
  • 第三方依赖需要少量本地改动:Fork 后以子模块引入,改动可追溯
  • 文档站点、主题、脚本片段在多个项目间复用同一份仓库
  • 审计要求把「依赖的具体提交」写进父仓库历史
  • 构建需要离线可复现:固定提交而不是浮动分支

最小示例

最小示例
git submodule add https://example.com/libs/common.git vendor/common  # 引入并立即克隆
# 父仓库根目录执行,省略路径时默认取仓库名
git status                             # 会看到 .gitmodules 与 vendor/common 两项
cd vendor/common && git log --oneline -1   # 确认子模块停在哪个提交
cd ../.. && git commit -m 'chore: 引入 common 子模块'  # 提交的是指针不是内容
git clone --recurse-submodules https://example.com/app.git   # 克隆时一次拉全
git submodule status --recursive        # 核对各子模块提交,行首 - 表示未初始化

与常见替代方案对比

方案依赖记录方式更适合什么情况
子模块父仓库记录提交哈希代码由外部维护且必须锁定版本
包管理器依赖清单加锁文件语言生态内有成熟包管理
vendoring依赖源码直接入库要求单仓库交付、可接受复制
monorepo同仓库多目录团队能统一发布节奏
父仓库记录的是子模块的一个具体提交,子模块上游更新不会自动进入父仓库:要在子模块内 fetch 并 checkout 到目标提交,再回父仓库 git add 该路径并提交指针变更。
子模块远端地址变更后,老克隆不会自动跟随:需同步 .gitmodules(git submodule sync)并重新 update,否则拉取仍指向旧地址。

易错点

  • 提交父仓库时只看到指针变化,容易忘记先在子模块内提交并推送,别人克隆后拿不到对应内容,构建直接报找不到文件
  • 顶层克隆漏掉 --recurse-submodules,子模块目录是空的,构建以缺文件报错且不易判断原因
  • 对子模块目录 rm -rf 后再克隆上级仓库不会恢复内容:先确认子模块内的改动已提交推送再清理
  • 子模块默认处于游离 HEAD,直接在里面提交容易丢失,改动前先 git switch -c 建分支

数据来源与参考

  • Git 官方文档:git-submodule
  • Pro Git 2nd Edition · 7.11 子模块
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌