PAYATHON 2026

Git 合并时如何保留目标分支的 .gitmodules 配置?

数据小陈

Git 没有一种仅靠提交到仓库的配置,就能在所有合并操作中强制保留目标分支 .gitmodules 的机制。题目中的三个条件无法通过 .gitattributes 和自定义 merge driver 同时满足。

如果必须保证最终合并提交中的文件内容不变,可以先暂停合并,再从合并前的目标分支恢复 .gitmodules,最后提交。团队要自动执行这一流程,需要统一的合并入口;属性配置无法拦截任意合并操作。

为什么 merge=ours 仍会被覆盖?

.gitattributes 中的配置:

.gitmodules merge=ours

表示在需要执行文件级三方内容合并时,使用名为 ours 的自定义 driver。ours 不是 Git 内置的文件级 driver,需要另行定义:

git config merge.ours.driver true

true 返回成功,且不修改当前版本的临时文件。因此,当这个 driver 被调用时,它可以保留当前侧内容,但不能禁止对文件的所有修改。例如,只有来源分支相对共同祖先修改了 .gitmodules 时,Git 可以直接采用来源版本,无需调用内容合并 driver。参见 gitattributes 官方文档。

fast-forward 也不会执行这种文件内容合并,而是直接推进分支指针。因此,即使 driver 配置正确,也无法覆盖所有情况。参见 git merge 官方文档。

下面的命令定义的是另一个名为 ignore 的 driver:

git config merge.ignore.driver .git-merge-ignore

它与 .gitmodules merge=ours 不匹配。即使改成匹配的名称,也需要确保命令存在且可执行,并且仍受上述调用条件限制。driver 定义属于 Git 配置,提交 .gitattributes 不会把这些定义自动安装到其他成员的本地配置中。参见 自定义 merge driver 文档。

如何可靠保留目标分支的文件?

以下流程适用于题目中两个分支均跟踪根目录 .gitmodules 的情况。先提交或暂存好现有工作,确保工作区和暂存区干净,并在仓库根目录操作。

以将 develop 合并到 master 为例:

git switch master
git merge --no-ff --no-commit develop

这两个参数需要一起使用:--no-commit 在创建合并提交前暂停,--no-ff 防止 fast-forward 直接更新目标分支。仅使用 --no-commit 无法暂停 fast-forward。参见 git merge 参数说明。

如果提示 Already up to date.,说明无需合并,到此结束。如果命令因其他错误失败,应先处理错误;只有已经进入待提交或冲突待解决的合并状态,才能继续下面的步骤。

此时尚未创建合并提交,HEAD 仍指向合并前的 master。从它恢复 .gitmodules:

git restore --source=HEAD --staged --worktree -- .gitmodules

这会同时恢复暂存区和工作区中的文件。即使 .gitmodules 出现内容冲突,也可以用该命令明确采用目标分支版本。参见 git restore 官方文档。

然后查看其他未解决的冲突:

git diff --name-only --diff-filter=U

若有输出,逐一解决并暂存。确认没有未解决的冲突后,检查 .gitmodules 的暂存内容是否与合并前一致:

git diff --cached --exit-code HEAD -- .gitmodules

预期没有差异,退出码为 0。审查其他改动后完成提交:

git commit

反向合并时,切换到 develop,并将合并来源改为 master:

git switch develop
git merge --no-ff --no-commit master

随后执行相同的恢复、冲突处理和提交步骤。恢复命令不需要硬编码分支名,因为此时的 HEAD 就是合并前的 develop。

这个流程保证最终提交中的 .gitmodules 与合并前一致。合并命令执行期间,工作区中的文件仍可能暂时发生变化。

团队如何自动应用这个流程?

可以把上述流程封装为仓库内共享脚本,要求成员和 CI 通过该脚本发起合并。脚本应检查工作区状态,识别是否真正进入合并状态,恢复 .gitmodules,并在存在其他冲突时暂停提交。执行共享脚本不需要每位开发者注册 merge driver,但所有入口都必须遵守该流程。

如果仍允许直接运行任意 git merge、通过 GUI 合并,或由托管平台生成合并提交,共享脚本就无法保证这些入口的行为。需要强制执行时,可在受控合并流程中增加校验:对于要求保留目标版本的合并提交,比较其 .gitmodules 与代表合并前目标分支的第一父提交,发现变化则拒绝集成。该规则需要平台或服务端配合部署。

保留 .gitmodules 后还要注意什么?

.gitmodules 中的 branch 主要决定 git submodule update --remote 跟踪哪个远程分支。父仓库实际记录的 submodule 提交由 gitlink 保存,保留 .gitmodules 不代表 common 的提交也保持不变。合并后仍需单独检查 submodule 提交是否符合预期。参见 git submodule 官方文档。

恢复整个 .gitmodules 会舍弃来源分支对该文件的所有修改,包括新增 submodule、URL 调整和路径变更。若以后需要接收这些修改,应改为只保留特定配置项,或安排单独的配置更新。

备注:内容仅供参考。