你在执行 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: unset 或 binary,说明该文件被当成二进制处理。
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 历史里的旧版本文件
- Git LFS pointer 文件没被真实文件替换
- Rebase 之后 commit 消失了
- Force push 覆盖了队友的 commit
- AI 帮你解决合并冲突
- AI package-lock 冲突处理
- CRLF 转换让单次 commit 产生几千行 diff
标签: #git #version-control #排查