GitHub 破坏了 Git:悄然删除你的代码的 Merge Queue Bug
Source: Dev.to
(请提供您希望翻译的正文内容,我将按照要求保留源链接并仅翻译文本部分。)
Source: …
GitHub 不再是 Git 的那一天
在 2026 年 4 月 23 日 16:05 UTC,一次回归错误潜入了 GitHub 的合并队列。接下来的三个半小时里,全球的工程师们审查 pull request,点击 “merge”,并看到一切看起来完全正常:
- 绿色勾选。
- 干净的 diff。
- 没有警告。
实际上,幕后发生的事情令人毛骨悚然。
一个 +29 / -34 的完全合理的 diff 会被批准并排队。最终落到 main 上的却是一个 +245 / -1,137 的提交。成千上万行其他工程师已经交付、审查并继续前进的代码,全部消失。随后每一次合并都在这段破损的历史之上进行。
UI 显示零问题。状态页没有任何宕机。平台在大家面前撒了谎。
实际出了什么问题
GitHub 的合并队列通过为队列中的每个 PR 创建一个 临时分支 来工作。通常,这个临时分支是从 main 的最新提交加上 PR 的差异开始的,CI 在其上运行,若通过则合并。
在 4 月 23 日,队列开始从 错误的起点 构建这些临时分支。它没有从当前的 main 最新提交分支,而是从特性分支最初与 main 分叉的地方开始,可能会回溯数十甚至数百个提交。随后它把整个临时分支的内容推送到 main。
因此,如果你的特性分支在进入队列时落后 main 50 次提交,那么这次“合并”会在悄无声息地 移除这 50 次其他人工作的提交,作为你的提交落地的副作用。CI 能通过是因为临时分支本身内部是一致的。main 之所以崩溃,是因为临时分支与 main 当前状态毫无关联。
根本原因: 一个用于未发布功能的特性标志本应屏蔽的、调整合并基准计算的新代码路径未被完整 gating,导致新行为泄漏到生产环境,并作用于所有 squash‑merge 组。
使这个 bug 特别棘手的三点
-
PR UI 在说谎。
你看到的是+29 / -34。实际落地的提交是+245 / -1,137。工程师批准的内容并不是最终合并的内容。这违背了代码审查系统最基本的契约。 -
完全没有任何提示。
没有合并冲突。没有检查失败。PR 上也没有横幅。团队只有在有人注意到main上本该存在的代码竟然不见了时才发现问题。 -
随仓库活跃度而放大。
仓库合并越快,特性分支与main的漂移就越大,每一次错误合并造成的破坏也就越严重。最依赖合并队列的团队受到的冲击最大。
人力成本
这不是一个理论上的问题。工程团队整个下午都在处理事故:在提交图中逐一查找、手动恢复被删除的代码、跨多个仓库协调恢复,并提交需要数天才能得到响应的支持工单。
- 某组织报告称,每个团队在 GitHub 的合并队列中都受到了影响,每个团队都有数十个错误提交,且在有人注意到之前,已有数百个现有提交被覆盖。
- 仅有一家公司的报告称其经历了 超过 200 个被毁的 PR。
GitHub 后来表示,在 4 月 22–23 日的影响窗口期间,230 个仓库中的 2,092 个 pull request 受到影响。GitHub 首席运营官在 X 上的早期信息称该数字为 2,804 个 PR,而一些社区成员根据各公司实际遭遇,对这两个数字提出了强烈质疑。
该事件未被 GitHub 的自动监控检测到,因为它影响的是 merge‑commit 正确性 而非可用性。GitHub 直到 19:38 UTC 在客户支持询问量上升后才发现此回归。修复——一次回滚并强制部署——在 20:43 UTC 完成。共计三小时三十三分钟的静默腐败。
为什么状态页面毫无用处
如果你在 4 月 23 日查看 GitHub 的状态页面,可能什么也没有看到。没有报告任何重大故障,也没有部分故障。
这是因为 GitHub 的状态页面计算方式 特意将“性能下降”排除在停机时间之外。平台本身并没有宕机。开发者仍然可以推送代码、打开 PR,并点击 merge。然而,点击 merge 时悄悄破坏了他们的代码库,这一点并没有在仪表盘上记录为一次事件。
这揭示了一个重要的缺口。正常运行时间和正确性并不相同。一家银行即使处理了你的交易,但如果记录错误,也不能算是“正常”。GitHub 完成了合并操作;只是产生了错误的结果。状态页面并未设计来捕捉这种类型的故障。
这并非一次孤立的糟糕日子
如果这只是一次性事件,处理起来会更容易。但 2026 年 4 月对 GitHub 来说确实是一段异常艰难的时期。
在合并队列事件发生四天后,即 4 月 27 日,GitHub 的 Elasticsearch 集群因可能的僵尸网络攻击而超负荷——搜索驱动的 UI 界面不再返回结果。拉取请求列表变为空白。问题(Issues)也消失……(原文在此截断)。
GitHub 事件 – 2026 年 4 月
事件时间线
- 2026 年 4 月 22‑23 日 – merge‑queue 中的一个 bug 导致合并提交生成错误。UI 显示为绿色的正常合并,但底层历史已被破坏。
- 2026 年 4 月 28 日 – GitHub 首席技术官 Vlad Fedorov 发布了关于可靠性的道歉。同日上午,还发布了一则独立的安全披露:Wiz 的研究人员在 GitHub 的
git push流水线中发现了一个关键的远程代码执行漏洞(CVE‑2026‑3854,CVSS 8.7)。构造的git push可以在 GitHub 服务器上执行未沙箱化的代码。该修复在 75 分钟 内部署完成。
五天内三大故障 – merge‑queue 正确性问题、搜索崩溃,以及核心
git push路径的 RCE。
规模压力
Fedorov 解释说,GitHub 最初计划在 2025 年 10 月进行 10 倍容量提升。但到 2026 年 2 月,AI 驱动的开发工具(Copilot、Cursor、Codex)激增,迫使进行 30 倍的重构。当前峰值:
- 每日 9000 万 次合并的 PR
- 每日 14 亿 次提交
更深层的架构问题
GitHub 的合并队列通过 不同的代码路径 构建合并提交,而不是手动的 “Merge pull request”。这种重复导致两个地方的行为可能悄然出现分歧。
- 委托风险 – 队列自动化了人类本会做的事,但一旦它加入了自己的逻辑,就可能产生没有人编写或批准的提交。
- 超出 GitHub 的模式 – 任何拥有写入权限的自动化系统(队列、机器人、AI 代理)在执行人类不会做的操作时,都可能引入不可见的失效模式。
教训:
不要回避合并队列,而是要确保所有写入 main 的操作尽可能保持 乏味、易于理解 的 Git 操作,避免审阅者无法审计的新颖逻辑。
有人真的会离开吗?
简短回答: 可能不会有显著数量的离开。
- 黏性: CI 流水线、webhooks、RBAC 策略、Actions 工作流、第三方应用权限、团队结构和 PR 历史使得迁移需要数月时间。
- 实用主义心态: GitHub 是开源的默认平台,也是大多数集成的首选。开发者很少因为一周的糟糕体验而更换工具。
应当改变的方面:
信任基线。即使是短暂的数据损坏,也需要有可靠的恢复计划。
团队的即时行动:
- 验证 – 审计合并队列组(≥ 2 个 PR)中 4 月 22‑23 日窗口的 squash 合并。
- 记录假设 – 列出构建/部署流水线中假设 Git 历史正确的部分,并将这些假设显式化以供审查。
GitHub 所称的行动
- 扩大对 merge‑correctness validation 的测试覆盖。
- 添加回归检查,在生产前验证在受支持的合并配置下生成的 Git 内容。
- 将对性能敏感的代码从旧的 Ruby 代码库迁移到 Go。
- 将系统迁移到公有云基础设施,以满足 30× scale 的要求。
2024 年 4 月 23 日的 bug 源于新代码路径上的特性标记不完整;即时修复是回滚,长期修复则是为多 PR 合并队列组提供更丰富的测试覆盖。
要点
在 2026年4月23日,GitHub 的合并队列打破了版本控制的核心约定:你批准的就是会被合并的内容。它在没有任何错误提示、没有状态页记录、界面干净的情况下悄然发生。
- 代码仍然保存在 Git 对象存储中,但分支历史出现错误。
- 没有自动化系统能够安全地修复所有受影响的仓库;工程师必须手动介入。
结论: Git 应该是所有其他工具构建之上的那层乏味、可靠的基础。当这层基础变得“有趣”时,往往是以最糟糕的方式出现。
如果你觉得这篇文章有帮助,欢迎在下方留言或关注,获取更多我们(有时过于)信赖的工具深度解析。
