我们如何修复 Git 图像合并冲突(以及为什么 GitHub 网站做不到)

我们如何修复 Git 图像合并冲突(以及为什么 GitHub 网站做不到)

真实解析二进制文件冲突、看似诱人的捷径,以及唯一真正有效的修复方法

代码中的合并冲突已经够烦人了。当它们发生在图像文件中时,大多数人都会不知所措——图片没有“差异视图(diff view)”,而且 GitHub 上通常的在浏览器中修复的按钮根本不存在。这篇文章详细介绍了一个完全符合这种情况的真实案例:什么方法无效,什么方法有效。

时至今日,也就是 2026 年 8 月 29 日,这非常值得了解,因为越来越多的小型团队通过 Git 运行内容繁多的网站,非技术人员通过桌面应用程序推送文本和图像更改。这种组合——使用自然语言的内容编辑者、二进制文件和真实的部署流水线——正是这个问题出现的地方。

背景设定:两个分支,一张共享图像

该项目使用简单的双分支工作流。 staging 是进行日常工作的地方——内容编辑可以自由地将更改推送到这里,并自动部署到预览站点。 main 是真实的实时站点,仅在经过审查的 Pull Request 从 staging 合并进来时才会更新。

该团队最近还将站点的图像转换为 WebP,这是一种现代格式,在相同视觉质量下比 PNG 或 JPG 小得多。现在每张图像都成对存在:原始的 .png,以及一个匹配的 .webp,它们通过 HTML <picture> 元素连接在一起,因此支持 WebP 的浏览器会自动获取小文件,而其他所有浏览器则回退到原始文件。

然后几乎在同一时间,在两个不同的分支上发生了两件事:有人对 main上的一个产品屏幕截图进行了直接的小幅修改,绕过了正常的审查流程,而内容编辑者同时分别通过 staging上传了该屏幕截图的一个真正的新版本。这两次编辑都涉及相同的文件路径。双方都不知道对方的操作。

为什么 GitHub 网站无法修复此问题

当内容编辑者打开一个 Pull Request 将 staging 更新引入 main时,GitHub 立即标记了它:“This branch has conflicts that must be resolved.”(此分支具有必须解决的冲突。)通常,点击进去会提供一个基于 Web 的编辑器,并排显示文本文件的两个版本,以便你可以选择保留哪些行。

对于图像来说,该编辑器并不存在。GitHub 基于浏览器的冲突解决功能仅理解基于行的文本——代码、markdown、配置文件。PNG 或 WebP 文件没有可比较的“行”。无论点击多么仔细,都没有按钮、没有视图、没有可以通过网站解决此问题的路径。值得直言不讳地说:内容编辑者没有做错任何事。该工具确实没有办法解决这个特定的问题。

隐藏的第二个问题

解决这个问题需要在本地拉取文件的两个版本并直接比较它们——这就是出现一个更狡猾的问题的地方。 staging 上的新屏幕截图是一次真正的更新,比以前更大、分辨率更高。但是与其配对的 WebP 文件已经很久没有被碰过了——尺寸甚至都不匹配。新的 PNG 是 1024 x 1024 像素;放在它旁边的 WebP 仍然是 750 x 750,完全是从早期版本的图像遗留下来的。

这意味着在冲突中简单地“选边站”——全盘保留任何一个版本——都会发布一对不匹配的文件。PNG 会显示新的屏幕截图,但任何偏好 WebP 的浏览器(大多数现代浏览器)的访问者,都会在不知情的情况下看到一张错误的旧图像。冲突信息会消失;而真正的 Bug 会在未被注意的情况下发布。

第 1 步:正确比较两个版本

第一步不是解决任何问题——它是对照它们共同的起点,比较两个分支上的文件大小和尺寸,以了解实际发生了什么变化以及在哪里发生了变化。

git fetch origin
git diff --stat origin/main origin/staging -- path/to/image.png path/to/image.webp

这表明自从上次共享以来,两个分支都独立地修改了该文件——这是一种真正的三方分歧,而不仅仅是“一方更新”。

第 2 步:确定哪个版本实际上是正确的

并排打开两个 PNG 文件证实了 staging 版本是真正的、预期的更新,而不是错误或损坏的文件。

第 3 步:从正确的来源重新生成 WebP

与其试图合并两个不同的二进制文件——这是不可能的——修复方法是将 WebP 视为 衍生自 PNG 的文件,而不是需要协调的独立文件:

cwebp -q 82 image.png -o image.webp

这生成了一个尺寸正确的 1024 x 1024 的全新 WebP,与新 PNG 完美匹配。 -q 82 只是一个质量设置——文件大小和清晰度之间的良好平衡。

第 4 步:完成合并并验证

git checkout staging
git merge origin/main
git checkout --ours path/to/image.png          # keep the staging PNG
cp image-regenerated.webp path/to/image.webp   # use the freshly regenerated WebP
git add path/to/image.png path/to/image.webp
git commit
git push origin staging

在此之后,Pull Request 显示为干净且可合并的,并且实时站点向每一位访问者正确地提供了新屏幕截图,无论其是否支持 WebP。

被考虑过并遭拒绝的方法

在此过程中出现了一些看似更快的捷径,每一个都值得了解,也值得知道为什么没有使用它。

在桌面 Git 客户端中选择一个版本。 大多数 Git 桌面应用程序允许你通过选择“使用我的版本(use my version)”或“使用他们的版本(use theirs)”来解决二进制冲突——没有比较,只是全盘选择。非技术团队成员可以自己完成此操作。问题在于:这会保留陈旧的、不匹配的 WebP 文件,并发布上面描述的同一个隐性 Bug。很快,但是错误的。

使用策略标志强制合并。 在命令行中, git merge origin/main -X ours (或者 -X theirs)告诉 Git 自动解决所有冲突,方法是全盘选择其中一方,对每个文件都如此,而无需停下来询问:

git merge origin/main -X ours

这与在桌面应用中选择一个版本具有完全相同的缺陷——Git 将冲突的二进制文件视为一个不可分割的块,因此它无法介入并只修复不匹配的一半。

首先从基准分支中删除文件。 理论上:如果实时分支不再有该文件,那么就不会有任何冲突,因此合并应该可以顺利进行。这是直接在一个孤立的分支中测试的,而不是仅仅假设的:

git rm image.png image.webp
git commit -m "test: delete file"
git merge origin/staging

结果并不是一个干净的合并。Git 记得文件在分支分离时是存在的,因此在它发生改变的一方保留时在另一方删除它,仍然会产生冲突——一个“修改/删除(modify/delete)”冲突,带有它自己的警告信息。它只是重新标记了问题,而不是跳过了它。

在冲突上强制推送(Force-pushing)。 一个更极端的选择是通过强制推送用另一个分支的历史记录完全覆盖实时分支。这并非真正的解决冲突——它直接丢弃了一方的修改,有默默丢失他人实际工作的风险。在部署到实时站点的分支上,这是一个非常冒险的举动,而不是一条值得走的捷径。

结论

二进制文件冲突——图像、PDF、编译文件,任何非纯文本的文件——都不能像代码冲突那样解决。没有逐行的差异(diff)可供推理。唯一真正可行的选项是全盘选择一个版本,或者,当这两个版本实际上是像图像及其优化副本这样配对的文件时,从正确的来源重新生成派生文件。了解你所处的情况是战斗的大部分内容。

优点

  • 重新生成派生文件(在本例中为 WebP)保证了一致性,而不是猜测该信任哪一方。
  • 在信任一个看似冒险的捷径之前,首先在一个独立分支中对其进行测试,可以低成本地发现错误的假设。
  • 保留原始文件格式作为后备(在本例中通过 <picture> 元素)意味着,即使优化后的副本暂时损坏,也绝不会为访问者完全破坏页面。

缺点

  • 所有快速的选项(选边站、强制合并策略、删除并重新合并)实际上都没有解决不匹配配对的问题——它们只是让冲突信息消失。
  • 妥善解决此问题需要直接的服务器或命令行访问权限以及图像转换工具,这超出了仅使用浏览器或仅使用桌面应用程序的工作流的范畴。
  • 根本原因——绕过正常审查流程直接编辑实时分支——是一个流程漏洞,一次性技术修复无法防止其再次发生。

警告

本文具有教育意义,用笼统的术语描述了一个真实的情况;特定的文件名、路径和标识符已被泛化。这里显示的命令可能会修改或丢弃真实的文件和分支历史记录——始终首先在隔离的分支或临时副本中进行测试,确保您了解在您的特定情况中“我们的(ours)”和“他们的(theirs)”所指的方向,并在运行任何通过强制手段解决冲突的命令之前,确认您有备份或恢复的方法。在依赖任何命令之前,对照您自己仓库的实际状态对其进行验证。

常见问题解答

  • 为什么 GitHub 的网站无法解决图像文件中的冲突? ——其基于浏览器的冲突编辑器仅理解基于行的文本;无法通过该界面比较或合并像 PNG 或 WebP 这样的二进制数据。
  • 什么是“修改/删除(modify/delete)”冲突? ——它发生在自分支上次共享历史记录以来,一个分支删除了某个文件,而另一个分支对其进行了更改的情况下;Git 会标记这种情况,而不是去猜测哪种修改应该获胜。
  • 从一个分支中删除文件能否避免合并冲突? ——不能。如果文件在分支产生分歧时存在,在文件被另一方修改时将其从一方删除仍然会产生冲突,只是类型不同。
  • 在桌面 Git 客户端中使用“使用我的版本(use my version)”或“使用他们的版本(use theirs)”来处理图像冲突安全吗? ——它解决了冲突,但仅仅是通过保持一个文件原样;像 WebP 副本这样的派生对应文件不会被自动修复。
  • 执行 git merge -X ours 实际上有什么作用? ——它告诉 Git 在整个合并过程中,通过保留当前分支的版本来自动解决每个冲突文件,而无需停下来询问。
  • 为什么要重新生成 WebP 文件而不是直接合并它? ——它是原始图像的压缩派生文件;没有有意义的方法可以“合并”两个不同的压缩版本,因此从正确的来源重新创建它是唯一正确的修复方法。
  • 团队一开始如何避免这种冲突? ——通过保持一致的工作流,其中所有更改在到达实时分支之前都经过工作分支,而不是允许从多个地方直接对实时分支进行编辑。

标签

#git #github #webp #imageoptimization #webdev #devops #versioncontrol #mergeconflict #cicd #webperformance

Free field guide

Linux Server Hardening Checklist

30 practical steps to take a fresh Linux box from default to defensible. Enter your email — you'll get the PDF instantly, plus new posts on Linux, security & AI.