11. 多仓库依赖怎么管?Git 子模块统一版本一键更新

「Git」系列已发布: 00. 改崩代码不敢回退?Git 版本控制 3 分钟救你狗命 | 01. Git 装完不会配?Windows/macOS/Linux 三端配置一次到位 | 02. Git 配置总踩坑?全局/仓库/SSH 密钥一次搞懂 | 03. add/commit/push 别再死记:Git 工作流核心概念实操 | 04. Git 分支管理:5 步彻底搞懂 branch 与 merge | 05. clone/fetch/pull/push 分不清?Git 远程协作实战 | 06. 给版本打上永久印记:Git 标签管理与发布实战 | 07. 切分支怕丢改动?Git stash 暂存恢复保护现场 | 08. rebase 不是 merge 的替代品:Git 故事线重构进阶 | 09. Git 日志与调试:log/blame/diff 让历史无所遁形 | 10. 提交代码自动检查:Git Hooks 实战,质量不再靠人工 | 11. 多仓库依赖怎么管?Git 子模块统一版本一键更新(本文)

公共库被 N 个项目引用,版本一乱全崩溃。Git 子模块统一管理依赖版本,一键同步更新,彻底终结“我本地是好的”惨案。

远程仓库操作(第 05 篇)和分支管理(第 04 篇)要熟练。最好在 GitLab/GitHub 上准备至少两个测试仓库,跑一遍完整流程。

一、子模块是什么

子模块(Submodule)让你把一个 Git 仓库嵌到另一个 Git 仓库的子目录里。主仓库只记录子模块的"指针"——指向子模块的某个具体提交哈希,而不是存子模块的文件内容。
什么时候用:

  • 多个项目共用同一个库,各项目独立管自己的版本依赖。
  • 一个大项目拆成多个独立仓库,通过子模块拼起来。
  • 依赖第三方库但想锁死版本(而不是靠包管理器)。
    子模块和 Monorepo 怎么选:
维度 子模块 Monorepo
仓库结构 多个独立仓库 单一仓库
版本管理 各仓库独立 统一版本
构建 要额外协调 统一构建
团队权限 可按仓库粒度控制 需要更细粒度
历史追溯 各自独立 统一历史

1.1 子模块基础操作

1.1.1 添加子模块

# git submodule add <仓库URL> [路径]
git submodule add git@gitlab.com:team/shared-lib.git libs/shared-lib

# 这行命令干了三件事:
# 1. 克隆 shared-lib 到 libs/shared-lib/
# 2. 创建 .gitmodules 文件,记录子模块映射
# 3. 在暂存区添加子模块记录(行为像个"文件",内容是提交哈希)
# 确认
git status

# new file:   .gitmodules
# new file:   libs/shared-lib
cat .gitmodules

# [submodule "libs/shared-lib"]
# path = libs/shared-lib
# url = git@gitlab.com:team/shared-lib.git
# 提交
git commit -m "chore: 添加 shared-lib 子模块"

1.1.2 克隆含子模块的项目

# 方式一:克隆后手动初始化
git clone git@gitlab.com:team/main-project.git
cd main-project
git submodule init
git submodule update

# 方式二:一步到位
git clone --recurse-submodules git@gitlab.com:team/main-project.git

# 方式三:已经克隆了
git submodule update --init --recursive

1.1.3 更新子模块

# 更新到主仓库记录的提交
git submodule update

# 更新到子模块远程仓库的最新提交
git submodule update --remote

# 只更新某一个
git submodule update --remote libs/shared-lib

git submodule update和git submodule update --remote 的区别:

命令 拿到哪个版本
git submodule update 主仓库记录的提交
git submodule update --remote 子模块远程默认分支的最新提交

1.2 在子模块里改代码

# 进入子模块
cd libs/shared-lib

# 这时候是 detached HEAD 状态
# 先切到工作分支
git checkout main

# 正常改、提交
echo "// 新增的工具函数" >> utils.go
git add .
git commit -m "feat: 添加字符串处理工具函数"

# 推到子模块的远程
git push origin main

# 回主仓库
cd ../..

# 主仓库检测到子模块更新
git status

# modified:   libs/shared-lib (new commits)
# 提交子模块引用更新
git add libs/shared-lib
git commit -m "chore: 更新 shared-lib 子模块到最新版本"
git push origin main

1.2.1 递归操作


# 递归拉取所有子模块
git pull --recurse-submodules

# 推送时检查子模块变更是否已推送
git push --recurse-submodules=check

# 推送时自动推送子模块变更
git push --recurse-submodules=on-demand

# 对所有子模块执行同一条命令
git submodule foreach 'git status'
git submodule foreach 'git log --oneline -5'

1.3 子模块高级操作

1.3.1 修改子模块远程 URL

# 直接改 .gitmodules
vim .gitmodules

# 同步到 .git/config
git submodule sync

# 或用命令直接改
git config --file .gitmodules submodule.libs/shared-lib.url git@new-gitlab.com:team/shared-lib.git
git submodule sync

1.3.2 切换子模块跟踪分支

.gitmodules里可以指定子模块跟踪的分支:

[submodule "libs/shared-lib"]
path = libs/shared-lib
url = git@gitlab.com:team/shared-lib.git
branch = develop

git submodule update --remote会拉develop的最新提交,而不是默认的main。

# 设置跟踪分支
git config -f .gitmodules submodule.libs/shared-lib.branch develop
git submodule update --remote

1.3.3 移除子模块

# 完整步骤
git submodule deinit -f libs/shared-lib
rm -rf .git/modules/libs/shared-lib
git rm -f libs/shared-lib

# Git 1.8.5+ 简化版
git submodule deinit libs/shared-lib
git rm libs/shared-lib

1.4 替代方案

1.4.1 Git Subtree

Subtree 的好处是不需要额外学git submodule命令,子仓库代码直接合到主仓库里,对协作者完全透明。

# 添加子树
git subtree add --prefix=libs/shared-lib git@gitlab.com:team/shared-lib.git main --squash

# 拉更新
git subtree pull --prefix=libs/shared-lib git@gitlab.com:team/shared-lib.git main --squash

# 推送修改回原仓库
git subtree push --prefix=libs/shared-lib git@gitlab.com:team/shared-lib.git main

子模块
vs 子树,怎么选:

场景 用哪个
团队需要独立管理子仓库权限和发布周期 Submodule
子仓库频繁改,希望统一构建 Subtree
依赖第三方库,需要严格锁版本 Submodule
协作者不熟 Git 高级功能 Subtree
多个主仓库共享同一子仓库 Submodule

1.5 Git Repo(Android 风格)

Google 的repo工具管理大量 Git 仓库集合,通过 XML manifest 声明所有仓库和版本。

<!-- default.xml -->
<manifest>
<remote name="origin" fetch="<https://git.example.com>" />
<default remote="origin" revision="main" />
<project name="platform/frameworks/base" path="frameworks/base" />
<project name="platform/packages/apps/Settings" path="packages/apps/Settings" revision="release-12.0" />
</manifest>


# 初始化
repo init -u <https://git.example.com/platform/manifest> -b main

# 同步所有仓库
repo sync

# 对所有仓库跑命令
repo forall -c 'git status -s'

1.6 子模块实践建议

1.6.1 CI/CD 中处理子模块


# .gitlab-ci.yml

  variables:
    GIT_SUBMODULE_STRATEGY: recursive
# 或者手动
  before_script:
    - git submodule update --init --recursive

1.6.2 提交前检查

# 别名
git config --global alias.subupdate 'submodule update --init --recursive'

# pre-push hook 检查子模块未推送变更
# .git/hooks/pre-push
#!/bin/bash
git submodule foreach --quiet '
if git status --porcelain | grep -q .; then
echo "警告:子模块 $name 有未提交的修改"
fi
'

1.6.3 文档化

README 里写清楚:


# 子模块说明
本项目使用 Git 子模块管理以下依赖:
-`libs/shared-lib`:团队共享工具库([仓库](https://gitlab.com/team/shared-lib))
-`libs/proto-definitions`:gRPC Proto 定义([仓库](https://gitlab.com/team/proto-definitions))

# 克隆后请执行:
git submodule update --init --recursive

1.7 文章引用

二、 系列文章 · Git 从入门到精通

本系列共 20 篇,建议从 00 开始顺序阅读。

序号 文章 状态
00 改崩代码不敢回退?Git 版本控制 3 分钟救你狗命 已发布
01 Git 装完不会配?Windows/macOS/Linux 三端配置一次到位 已发布
02 Git 配置总踩坑?全局/仓库/SSH 密钥一次搞懂 已发布
03 add/commit/push 别再死记:Git 工作流核心概念实操 已发布
04 Git 分支管理:5 步彻底搞懂 branch 与 merge 已发布
05 clone/fetch/pull/push 分不清?Git 远程协作实战 已发布
06 给版本打上永久印记:Git 标签管理与发布实战 已发布
07 切分支怕丢改动?Git stash 暂存恢复保护现场 已发布
08 rebase 不是 merge 的替代品:Git 故事线重构进阶 已发布
09 Git 日志与调试:log/blame/diff 让历史无所遁形 已发布
10 提交代码自动检查:Git Hooks 实战,质量不再靠人工 待发布
11 多仓库依赖怎么管?Git 子模块统一版本一键更新 待发布
12 GitFlow vs GitHub Flow vs 主干:团队工作流 3 选 1 待发布
13 仓库越来越大?Git LFS 让图片视频不再撑爆仓库 待发布
14 合并冲突不是噩梦:Git 合并策略与冲突解决 待发布
15 常用命令记不住?Git 别名让操作变成快捷键 待发布
16 仓库太大会卡顿?git gc + bfg 清理历史垃圾实战 待发布
17 把密码提交进 Git?安全实践:密钥管理与审计日志 待发布
18 IDE 里的 Git 怎么用?VS Code/IntelliJ 集成效率翻倍 待发布
19 删了分支找不回?Git 常见问题排查与修复指南 待发布

上一篇:提交代码自动检查:Git Hooks 实战,质量不再靠人工 | 下一篇:GitFlow vs GitHub Flow vs 主干:团队工作流 3 选 1

📌 本文首发于微信公众号 【运维知识点滴】——专注运维实战干货,微信搜索“运维知识点滴”或扫下方二维码关注,更新不迷路。

微信公众号【运维知识点滴】 微信小程序【A5单据合并&大试卷拆分转A4打印】
运维实战干货 · 关注追更 Word 转 PDF · 试卷/发票打印神器 · 免费
运维实战干货 · 关注追更 Word 转 PDF · 试卷/发票打印神器 · 免费

💡 小程序【A5单据合并&大试卷拆分转A4打印】是干嘛的? 一个 Word 转 PDF 的打印神器,专治“文档版式和打印机对不上”:

  • A3 试卷打不出来? 学校发的电子版试卷多是 A3,家用打印机只吃 A4——上传 Word 一键转换:可缩到一页 A4(页数不变),也可对半拆成两张 A4(1:1 原大、字更清晰),两种模式自选;
  • 版式不乱:真实渲染引擎原样转换、绝不重排内容,转出的 PDF 和原 Word 排版一致,公式/表格/图片不跑版;
  • 两张 A5 单据拼一张 A4:出租车票、火车票等报销凭证拼版后,一次打印、整齐粘贴;
  • 隐私省心:无需注册,转换结果留存 5 天、最多下载 5 次即自动删除,不留任何历史记录;
  • 微信扫码即用,免费、免安装,支持 doc / docx(≤50MB)。

上一篇:10. 提交代码自动检查:Git Hooks 实战,质量不再靠人工