子模块默认锁定在特定commit,不自动跟随分支;要使其随上游分支更新,必须在.gitmodules中配置branch字段并配合git submodule update --remote使用,否则仅git submodule update始终检出原commit。

子模块默认只记录某个 commit,不自动跟随分支更新;要让它“活”起来、随上游分支自动前进,必须显式配置分支跟踪,并用 --remote 触发拉取——否则 git submodule update 永远只会回到父仓库 commit 记录的那个旧版本。
如何让子模块绑定到远程分支(而非固定 commit)
关键在 .gitmodules 中声明 branch 字段,否则 --remote 会退化为拉取默认分支(通常是 main 或 master),且无法感知你想要的稳定分支名。
- 编辑
.gitmodules,为对应子模块添加branch = stable(例如) - 运行
git config -f .gitmodules submodule.<name>.branch stable</name>(<name></name>是.gitmodules中[submodule "xxx"]的 xxx) - 执行
git add .gitmodules && git commit -m "set submodule xxx to track stable branch" - 注意:仅改本地
.git/config无效,父仓库其他人 clone 后不会继承该配置
git submodule update --remote 实际做了什么
它不是“更新子模块代码”,而是:先 cd 进子模块目录 → 执行 git fetch origin <branch></branch>
→ 再 git checkout origin/<branch></branch>
(硬切)。所以它完全忽略子模块当前工作区状态,也不 merge/rebase 本地修改。
- 若想保留本地改动并合并远端更新,改用
git submodule update --remote --merge - 若子模块已设置
branch,--remote就按那个分支拉;否则 fallback 到远程默认分支 - 加
--recursive可处理嵌套子模块,但要注意深层子模块是否也配了branch - 执行后,父仓库的子模块指针(gitlink)仍指向旧 commit,需手动
git add path/to/submodule && git commit提交新指针
切换父仓库分支时子模块没动?检查 --recurse-submodules
普通 git checkout feature/login 不会影响子模块 —— 即使新分支在 .gitmodules 里写了不同 branch,也不会自动触发 update --remote。
- 正确做法是:
git checkout --recurse-submodules feature/login - 该命令会:先切父分支 → 再对每个子模块执行
git submodule update --init(即检出父分支记录的 commit) - 但它不会执行
--remote;如需同步分支最新,仍得额外跑一次git submodule update --remote --recursive - 如果父分支之间子模块
branch设置不同,建议在切换后立刻git submodule sync,确保.git/config里的 URL 和 branch 与当前.gitmodules一致
为什么 git submodule foreach 'git pull' 不推荐
它绕过了父仓库对子模块版本的管控逻辑,容易造成“本地能跑、CI 报错”的割裂状态。
- 子模块目录下
git pull会更新工作区和 HEAD,但父仓库的 gitlink 仍指向旧 commit - 下次别人
git submodule update,会强行覆盖你刚 pull 的内容,导致协作混乱 - 更隐蔽的问题:如果子模块当前在 detached HEAD 状态,
git pull会失败(因无 tracking branch) - 真正安全的做法始终是:先
--remote更新子模块 → 再git add+commit父仓库指针
最易被忽略的一点:子模块的 branch 配置只影响 --remote 行为,对 git submodule update(无参数)完全无感;很多人配了却没效果,就是因为后续更新仍用错命令。











