二进制文件合并冲突 — 手动无法 resolve

合并分支时遇到 PNG、PDF、Excel 等二进制文件冲突,Git 无法生成可读 diff,导致 merge 卡死。本文给出明确选边、工具 diff、预防策略的完整修复路径。

你在执行 git merge feature/update-assets 后,终端出现一行让人困惑的提示:CONFLICT (content): Merge conflict in assets/report.xlsx。打开文件,里面不是熟悉的 <<<<<<< 标记,而是乱码或一片空白。Git 把二进制文件整个标记为冲突,却无法告诉你哪里不一样。此时手动编辑无济于事,git mergetool 也可能弹出空白界面。本文逐步讲清楚为什么会这样,以及如何安全恢复。

常见原因

1. 两个分支都修改了同一个二进制文件

最高命中率。设计师在 main 更新了 Logo PNG,工程师在 feature 也替换了同一个文件。Git 没有办法做文本级别的三路合并,只能整体报冲突。

怎么判断git log --oneline --all -- assets/logo.png 若在两个分支都能看到提交记录,就是这种情况。

2. 文件被 .gitattributes 定义为 binary,但实际上是文本

某些 .gitattributes 规则如 *.svg binary*.json binary 会让 Git 把本该可读的文件当二进制处理,导致普通 merge 策略失效。

怎么判断git check-attr diff assets/icon.svg,若输出 diff: unsetbinary,说明该文件被当成二进制处理。

3. 文件体积超过 Git 内部 diff 阈值

超大文件(通常 50 MB 以上)即使是文本,Git 也可能跳过 diff 直接报冲突。

怎么判断git cat-file -s HEAD:path/to/file 查看文件大小,超过 50 MB 即可判断。

4. 合并工具未配置 binary driver

git mergetool 没有为该扩展名配置对应 driver,弹出的是默认文本编辑器,无法处理二进制内容。

怎么判断git config merge.tool 为空或为纯文本工具(如 vimdiff),而冲突文件是 .xlsx.psd.docx

5. LFS 与非 LFS 分支合并

一个分支用 Git LFS 管理图片,另一个分支直接提交了真实二进制内容。合并后指针文件与真实内容混在一起,造成二进制冲突。

怎么判断git show HEAD:assets/image.png | head -3,若显示 version https://git-lfs.github.com/spec/v1,说明是 LFS 指针,而对方分支可能是真实二进制。

6. 提交者使用不同操作系统导致 Office 文件内部元数据差异

Office 文档(.docx.xlsx)在 Windows 与 macOS 上保存时内部 XML 元数据不同,即使内容相同,二进制层面也会出现差异,在合并时触发冲突。

怎么判断git log --follow --diff-filter=M -- docs/report.docx 若提交者来自不同平台,大概率属于此类。

最短修复路径

Step 1:先打标签备份,避免操作失误

git tag backup/before-binary-merge HEAD

Step 2:查看冲突文件列表

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

Step 3:选择保留哪个版本

如果确定保留当前分支(ours)版本:

git checkout --ours assets/report.xlsx
git add assets/report.xlsx

如果确定保留合并进来的分支(theirs)版本:

git checkout --theirs assets/report.xlsx
git add assets/report.xlsx

Step 4:如果需要手动挑选内容,先把两版本都导出

git show HEAD:assets/report.xlsx > /tmp/ours_report.xlsx
git show MERGE_HEAD:assets/report.xlsx > /tmp/theirs_report.xlsx

用 Excel / Figma / Photoshop 手动对比后,把最终版本复制回原路径,再 git add

Step 5:完成合并提交

git merge --continue
# 或者
git commit -m "resolve: binary conflict in report.xlsx — kept theirs version"

Step 6:如果整个 merge 需要放弃,干净退出

git merge --abort

预防建议

  • .gitattributes 中为二进制文件配置 merge=binary,明确告知 Git 不要尝试文本合并:*.png merge=binary
  • 使用 Git LFS 管理所有大型二进制资产,避免将真实二进制内容直接提交到 Git 对象库。
  • 为 Office 文档设置专属 merge driver,例如 *.docx merge=ours,并在团队 .gitattributes 中统一规范。
  • 建立”二进制资产负责人”制度,一次只允许一个人修改同一个设计文件,通过 PR 流程协调。
  • 重要二进制资产变更前,先在 PR 描述里注明,避免两个分支同时修改。
  • CI 中加 git diff --name-only --diff-filter=U 检查,检测到未解决的冲突时阻断 merge。
  • 对于经常冲突的文件,考虑使用外部存储(S3、CDN)结合版本号命名方案,完全绕开 Git 合并。

常见问答 (FAQ)

Q: git checkout --ours--theirs 分别对应哪个版本? A: --ours 是你正在合并进来的基础分支(即执行 git merge 时所在的分支),--theirs 是你合并进来的那个分支。在 rebase 场景中两者含义会对调,请注意区分。

Q: 我不小心 git add 了一个乱码的冲突文件,提交了怎么办? A: 使用 git revert HEAD 撤销提交,或者 git reset HEAD~1 退回到合并前(注意 --hard 会丢弃工作区,用之前确认 git tag backup)。

Q: 为什么 git mergetool 对 PNG 弹出空白编辑器? A: 因为默认 mergetool 是文本编辑器,无法理解二进制格式。对 PNG 应改用 --ours / --theirs 直接选边,或配置 merge.tool=opendiff(macOS Xcode FileMerge 支持部分图片格式)。

Q: 能否写一个脚本批量处理所有二进制冲突,全部选 theirs? A: 可以。git diff --name-only --diff-filter=U | xargs -I{} git checkout --theirs {} 然后 git diff --name-only --diff-filter=U | xargs git add,再 git merge --continue。执行前请确认选 theirs 对所有文件都是正确决策。

相关阅读

标签: #git #version-control #排查