TT Lab
博客

한국어 English 日本語 中文

开始
博客

博客

一次性检查了45个处于打开状态的PR:五种合并状态与三个冲突,无需克隆即可重新应用

外传:这不是关于已合并PR的流程,而是关于维护处于打开状态的PR的流程。本文中出现的PR在2026-11-13时均处于「进行中」。 实际上,这些内容取自GitHub的PR及检查记录、文档、各项目的贡献规则原文、贡献账目的查询原始数据以及实测记录。时间均为韩国时间。

目录

一句话总结

对象 上游项目中处于打开状态的45个PR。均于10月中旬到月底之间提交且皆非草稿。个人仓库以及所属社区仓库的7个旧PR将单独计算
查询 gh pr view --json mergeable,mergeStateStatus,reviewDecision 每个PR一次
分布 BLOCKED 24 · UNSTABLE 9 · CLEAN 7 · BEHIND 3 · DIRTY 2。初次查询时为 DIRTY 3、UNKNOWN 4
处理 三个冲突中有一个通过在新的基准之上重新应用而解决。另外两个需要重写,目前正在进行中
열린 PR 45건의 병합 상태를 한 번에 조회했다 2026-11-13 재조회 · gh pr view --json mergeStateStatus ① 상태별 건수: 충돌은 DIRTY 뿐이다 BLOCKED 24 리뷰 대기 UNSTABLE 9 검사 미통과 CLEAN 7 병합 가능 BEHIND 3 뒤처짐 DIRTY 2 충돌 ② 충돌 원인 셋과 처리(첫 조회의 DIRTY 는 3건) 이웃 줄이 고쳐짐 dynamo#16465 · 문서 2개 새 main 위로 재기준 +15/−4 그대로 · 해소 문서를 통째로 다시 씀 dcgm-exporter#760 새 문서에 맞춰 다시 쓰기 새 판 4.9.0 미실측 · 진행 중 주 버전 개편(v3) catwalk#676 새 형식으로 다시 쓰기 새 Go 도구로 빌드 · 진행 중 BEHIND 3건은 충돌이 아니다: 기준 브랜치만 앞서 있다 첫 조회의 UNKNOWN 4건은 다시 조회해야 값이 나온다
图:打开的45个PR的合并状态分布与三个冲突的原因及处理

1. 为什么要检查:打开的PR放着不管也会过时

提交PR的那天既没有冲突也能通过检查。然而,在等待评审的几周时间里,基准分支一直在变动。 其他人修改了相同的文件,文档结构发生了变化,运行检查的Runner环境也变了。我们的PR一行代码未改, 但不知哪天就被贴上了「冲突」标签,或者原本能通过的检查变成了失败。

站在维护者的角度来看,发生冲突的PR是无法进行评审的PR。当轮到评审时如果发现存在冲突,就会直接跳过该顺序。 因此,当打开的PR增加到数十个之后,我们决定不再只等收到通知时才去查看,而是改为一次性查询整体并按状态进行分类。

这篇文章记录了2026-11-13凌晨进行的这次检查。与第1–6篇不同,本文不追踪单个已合并的PR,而是探讨维护多个打开的PR的流程。 由于这些是尚未得出结果的PR,因此不会记录任何PR「已被接受」。

2. 查询:五种状态值与UNKNOWN

首先创建打开的PR列表(gh search prs --author <계정> --state open),并对每个PR执行一次以下命令。

gh pr view <번호> --repo <소유자>/<저장소> --json mergeable,mergeStateStatus,reviewDecision

mergeable 只能告知是否存在冲突(MERGEABLE·CONFLICTING·UNKNOWN), mergeStateStatus 将「现在是否可以点击合并按钮」 进行了更细致的划分。GitHub的GraphQL文档中说明的含义以及此次统计的数量如下。

值 文档含义(要点) 数量 我们的解读方式
BLOCKED 合并被阻止 24 大部分在等待评审。评审判定结果为:“需要评审”17,无判定5,批准1,更改请求1
UNSTABLE 可以合并,但存在未通过状态的检查 9 打开检查记录,首先确认原因是否由我们的修改引起
CLEAN 可以合并且检查通过 7 我们无需做任何事。其中获得批准的有1件
BEHIND PR分支落后于基准 3 不是冲突。仅基准分支超前
DIRTY 无法干净地创建合并提交 2 冲突。将原因追溯到具体的PR

在此学到了两件事。

初次查询的 UNKNOWN 并非最终结果。 在初次查询中,有四项显示为 UNKNOWN 。GitHub的REST文档说明,如果此值为空,则意味着计算是否可合并的后台任务刚开始,建议过一段时间再请求。实际上在大约4分钟后用相同的命令再次查询,发现其中三项变成了 BLOCKED,一项变成了 UNSTABLE 。越是长时间未查询的PR,在初次查询时越容易出现该值,因此批量检查脚本应当将 UNKNOWN 的项目收集起来再次查询。

CLEAN 并不意味着「即将合并」。 这七项仅代表技术上没有阻碍,合并与否由维护者决定。 相反, BLOCKED 的24项大多数也不是因为有什么需要修改,而是处于等待评审顺序的状态。在45项中,需要我们动手的只有 DIRTY 和 UNSTABLE 中原因在我们这一侧的项目。 UNSTABLE 的九项原因各不相同,本文中不再细分。

有时检查失败的原因并非由于我们的修改。在一天前的11-12看到的 containers/kubernetes-mcp-server#1518 在获得一名评审员批准的情况下,浏览器测试变为了失败状态。同样的测试仅在旧基准上失败,原因似乎是检查Runner的镜像发生了变化。 我们保持修改内容不变,将其重新应用到新基准之上。随后获得了第二个批准,在本次查询中显示为 CLEAN 。 该PR也正在进行中。

3. 三个冲突的原因各不相同

初次查询的 DIRTY 有三项。仅凭发生冲突这一事实无法得知应该做什么,因此我们查清了这三项分别是因为哪个PR合并而导致产生了冲突。 原因各不相同,因此处理方式也不同。

我们的PR 造成冲突的合并 该合并的规模 原因 处理方式
ai-dynamo/dynamo#16465 4个文档,+15/−4 #17229, 11-06 04:34 29个文件,+97/−3068 我们修改的行的相邻行被修改 在新的基准之上重新应用。解决
NVIDIA/dcgm-exporter#760 1个文档,+16/−7 #768, 11-07 03:45 961个文件,+161632/−27808 包含被修改段落的文档被整篇重写 需要根据新文档重写。进行中
charmbracelet/catwalk#676 3个文件,+63/−0 #611, 10-29 23:22 73个文件,+39578/−18179 主版本重构导致模块路径和配置格式发生变化 需要用新格式重写。进行中

相邻行被修改的情况。 dynamo 的PR是一项为四处已废弃功能的文档补充缺失的废弃标记的修改。在此期间,上游合并了一个移除相关集成代码的PR,修改了同一文档中紧邻的两行。在重新应用之前,我们首先确认的是「我们的修改是否仍然必要」。打开新基准分支的四个文件一看,废弃标记确实仍然缺失。于是我们在新基准的文件中重新应用了相同的修改,修改量保持 +15/−4 不变。再次查询时,冲突解除,并恢复到了等待评审(BLOCKED)状态。

文档被整篇重写的情况。 dcgm-exporter 的PR修改了导出调试转储流程的段落。然而,提交新版本(4.9.0)的一个PR修改了961个文件,并将该文档整篇重写了。由于该PR在创建后仅30分钟便被合并,因此无法提前获知。在新文档中,我们修改的段落已经消失,转储放置的位置也发生了变化。这不是通过重新应用就能解决的冲突。我们必须先测算新文档中残留的一行复制命令是否在新镜像中也会失败,但新版本尚未测算(未实测)。测算后,若将其重写为单行PR或者问题已消失则选择关闭,这才是正确的做法。

主版本被重构的情况。 catwalk 的PR是一项添加提供者配置的修改。在此期间,v3被合并,导致模块路径和配置文件格式发生了变化。无法直接应用旧格式的文件,必须用新格式重写,并尝试用新版本的工具进行构建。这也是正在进行中。

BEHIND 并非冲突。 这三项只是基准分支领先,合并提交可以干净地创建。这次我们没有去动它们。既然没有冲突,我们认为没必要在评审开始前急着重新应用。有些项目对评审开始后的强制推送比较反感,因此最好减少不必要的重新应用。

审视这三项就会发现,冲突并非取决于「我们的修改与他人的修改重叠的程度」,而是取决于「我们的修改所依赖的基础改变了多少」。如果是相邻行级别,重新应用即可;如果段落或格式消失了,则需要重新评估修改本身。

4. 无需克隆重新应用:Git Data API与SSH签名

把几十个仓库全部下载下来再重新应用会带来沉重的负担。为了修复几行文档的PR去下载数GB大小的仓库也是一种浪费。因此,我们不下载仓库,而是通过GitHub的Git Data API来进行重新应用。在本地既不创建提交,也不切换分支。流程如下。

  1. 将Fork的默认分支与上游保持同步。只有当新的基准提交存在于Fork中时,才能在其上创建提交。
  2. 仅获取新基准中需要修改的文件,重新应用相同的修改,并运行可以运行的测试。
  3. 为每个文件创建blob(POST git/blobs),将新基准的树指定为 base_tree 来创建树(POST git/trees).
  4. 直接拼装提交对象的主体并用SSH密钥进行签名。
  5. 附加签名并创建提交(POST git/commits),确认响应的 verification.verified 是否为真。
  6. 仅在为真时,将PR分支的ref移动到新的提交。移动的目标仅限于我自己的Fork分支。

第4步中签名对象正是git在保存提交时使用的原样字符。

tree <새 트리 SHA>
parent <새 기준 커밋 SHA>
author <이름> <메일> <epoch 초> +0900
committer <이름> <메일> <epoch 초> +0900

<커밋 메시지>
ssh-keygen -Y sign -n git -f <개인 키 파일> <위 내용을 담은 파일>

在此记录一下碰到的问题。

学到代价最高的一课是关于文件模式(file mode)。最初制作的工具将上传文件的模式固定为 100644 。在 10-31 将 containers/podman-compose#1585 重新变基(rebase)后,原本是可执行文件的脚本变成了普通文件,30 个集成测试以 PermissionError 失败。内容连一个字符都没有不同,因此仅看 diff 的正文是无法发现这种变更的。第二天,我们修复了工具以使其遵循基准树的模式,并用相同的内容重新创建了提交。从那以后,在使用签名工具后,必定会确认以下事项。这个 PR 也在进行中。

gh pr diff <번호> --repo <소유자>/<저장소> | grep -n 'mode'

正常情况下应该没有任何输出。 old mode 100755 和 new mode 100644 如果出现,则是非预期的变更。

5. 未提交的内容:不同项目的门槛

顺便检查了一下,在仅注册了的 Issue 中,有没有可以连结为 PR 的。结论是「现在没有要提交的」。倒不是因为做不出补丁,而是因为项目的规则规定了顺序。

也有提交了一个的情况。这是修复了 KubeVirt 的 virt-operator 在不丢弃任何证书的情况下重复打印“丢弃陈旧的 CA”日志这一问题的 kubevirt/kubevirt#19889 。KubeVirt 的 AI 贡献政策要求在提交前审查和理解 AI 生成的代码的所有行,并建议披露工具的使用。因此,我们在提交中附上了 Assisted-by 拖尾(trailer)并作为 草稿 PR 进行了提交。在人工完整阅读 diff 后,将其更改为审查请求状态。这个 PR 也在进行中,合并后会单独写一篇文章。

6. 如何测试无法在 Mac 上运行的测试

上述 KubeVirt 更改的单元测试无法在 Mac 上运行。因为测试所附带的软件包依赖于 Linux 专有软件包。我们没有选择“测试会让 CI 去跑吧”然后直接提交,而是通过以下方法进行了直接测量。

  1. 在 Mac 上仅交叉编译用于 Linux 的测试二进制文件。由于只使用仓库的 vendor 目录,因此无需下载任何新内容。
  2. 将该二进制文件放入 TT Lab 集群的临时 Pod 中并仅执行它。Pod 使用节点上已经下载的镜像,丢弃所有 capability,以非 root 用户身份、在没有服务账户令牌、内存上限为 1Gi 的情况下启动。结束后会被删除。
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 GOTOOLCHAIN=local GOPROXY=off \
  GOFLAGS="-mod=vendor -buildvcs=false" go test -c -o <시험 바이너리> <패키지 경로>
测试对象 结果
新写的 2 个测试,修改前的代码 0 通过,2 失败
新写的 2 个测试,修改后的代码 2 通过
CA 捆绑包相关测试 27 个中 27 个通过
整个软件包(打乱顺序 3 次) 160 个中 160 个通过,约 0.53 秒
邻近的 apply 软件包整体 219 个中 219 个通过

环境是 Linux 内核 7.0、x86_64、Go 1.26.6。我们还测量了修改前的症状。在安装了 KubeVirt v1.9.0 的 TT Lab 集群中,统计了领导者(leader)operator 的日志 24 分钟,发现相同的行有 711 行。这与每调整 237 次在 CA 捆绑包集中打印一行的次数相吻合。

这里也有一个陷阱。最初编写的测试在 修改前的代码中也通过了。因为测试环境的默认日志记录器过滤掉了信息级别的日志,导致“不应打印日志”的测试由于日志被过滤而通过了。直到修改为在测试内部提高日志详细程度并在结束后恢复后,修改前的代码才出现 2 个全部失败的情况。如果跳过了“必须先确认新测试在修改前的代码中会失败”这一原则,我们差点就提交了一个什么都守护不了的测试。

7. 心得:照看开放 PR 的诀窍

蒂蒂的一言。 我在图书馆塞了好多张“请买这本书”的小纸条,过了一段时间去看,有些纸条因为书架号变了得重写,还有些纸条说得先去盖馆员老师的章。所以现在我塞了纸条不会忘掉,而是偶尔会一次性拿出来,读一读看它们是不是依然合用。

链接

所有资料均于 2026-11-13 查阅。

登录后即可点赞

评论

还没有评论。

登录后即可发表评论