Git 子模块 submodule 使用详解
时间:2026-11-15 07:19 作者:emer 分类: 无
Git 子模块 submodule 使用详解
一、什么时候用子模块
一个项目要引用另一个独立仓库(公共组件、SDK、前端 UI 库),既想跟着主仓库走,又想保留对方自己的版本历史。直接拷源码会丢失更新,用普通子目录复制又无法同步,这就是 Git 子模块(submodule)的场景:主仓库里只记录被引用仓库的一个特定提交,而不是全部文件。
二、添加子模块
git submodule add https://github.com/example/ui-lib.git third-party/ui
执行后会发生三件事:
- 在
third-party/ui克隆远端仓库; - 在主仓库生成/更新
.gitmodules(记录子模块路径与 URL); - 主仓库暂存区记录该子模块当前 commit。
三、克隆含子模块的项目
普通 clone 只拿到空目录,需要初始化:
# 方式一:克隆时递归拉取
git clone --recurse-submodules https://github.com/example/main.git
# 方式二:已克隆后再补拉
git submodule init
git submodule update
四、更新子模块到最新
进入子模块目录像操作普通仓库一样拉取,回主仓库提交新指针:
cd third-party/ui
git checkout main
git pull
cd ../..
git add third-party/ui
git commit -m "update ui-lib to latest"
主仓库只保存一个 commit 指针,对方发新版本后主仓库不会自动变,要主动更新并提交。
五、查看与遍历
# 查看所有子模块当前状态
git submodule status
# 一次性更新所有子模块到远端最新
git submodule update --remote
# 批量在所有子模块执行命令
git submodule foreach 'git pull'
git submodule status 前面的 + 号表示子模块当前提交与主仓库记录不一致。
六、移除子模块
不能只删目录,要按规范清理:
git submodule deinit -f third-party/ui
git rm -f third-party/ui
rm -rf .git/modules/third-party/ui
git commit
七、常见坑
- clone 后子模块目录是空的,忘了
submodule init/update; - 在子模块里改代码后回主仓库,只看到“指针变化”,误以为没保存;
- 在子模块处于游离 HEAD 状态直接提交,更新时丢失;
- 换分支后主仓库指针变了却不跑
submodule update,子模块停在旧版本; - 删除子模块只删目录,残留
.gitmodules和缓存导致再次添加报错。
八、子模块的取舍
优点:版本锁定、历史独立、可复用;缺点:操作比普通目录复杂,新手易踩坑。替代方案有 Git 子树(subtree)和包管理器(npm、Composer、Go modules),优先用包管理器,只有确实需要直接引用仓库源码时才用 submodule。
九、总结
submodule 让主仓库引用外部仓库的固定提交:添加用 add,克隆要 init/update,更新先在子模块拉取再回主仓库提交指针,移除必须 deinit + rm + 清理缓存。理解它只存 commit 指针这一点,就能避开大部分版本不同步的问题。