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

