跳到主要内容

堆叠 PR 搭了一遍,比想象中朴素

abanana
abanana

· 阅读约 5 分钟

前几天在 dev.to 上看到有人写 GitHub 的堆叠式拉取请求,说这功能是悄悄上线的,没什么官方公告。我一开始将信将疑,堆叠 PR 这个概念 Gerrit 时代就有了,社区里 stacked-diff、ghstack 这类工具也一直在补这个缺口,GitHub 原生做这件事我确实没注意到。于是花了一个晚上在自己的仓库里复现了一遍,这篇笔记记一下过程和几个容易卡住的点。先说结论:确实能用,而且不需要装任何额外工具,普通 git 分支就能触发,这是最出乎我意料的地方。

规则本身

规则一句话就能说清:最底部的 PR 以主干为目标,其后的每个 PR 都以上一个 PR 的分支为目标,而不是主干。也就是说你把一个功能拆成三层的话,分支结构是这样:

main
 └── db-layer        ← PR #1,base 是 main
      └── api-layer  ← PR #2,base 是 db-layer
           └── frontend  ← PR #3,base 是 api-layer

原文作者是拿数据库层、API 端点、前端这三层做演示的,我照着搭了个类似的链。关键操作就一步:开 PR 的时候把 base 分支从 main 改成前一个分支。改完之后 GitHub 会自己识别出堆叠关系,你不用打任何标记。

这一步我一开始没搞懂为什么不需要额外声明,后来才想明白——base 指向本身就是依赖关系的完整表达,GitHub 以前也存这个信息,只是没把它当成一个可交互的对象,现在等于是在已有元数据上加了一层视图。

验证它真的生效了

配好之后别急着干活,先验证。打开堆叠链顶部的那个 PR,如果生效了会看到一条横幅,提示这个 PR 可以和其他 PR 堆叠,旁边有预览堆叠的按钮。点「Create stack」之后,链上每个 PR 的列表里会出现进度徽章,我这里是 1/3、2/3、3/3。

💡 小技巧:验证就用顶部那个 PR,底部和中间的 PR 界面上不一定有横幅。我第一次拿最底下的 PR 去看,什么都没有,还以为没配对,这个坑我也是绕了两次才躲开。

还有一个点值得单独记:GitHub 会给堆叠分配一个独立的堆叠 ID,把它当一级对象来跟踪。但这个堆叠只存在于 GitHub 侧,git 分支本身没有任何特殊变化,你本地 git log 看不出任何堆叠痕迹。也就是说这套东西完全建立在 PR 的元数据上,哪天你想拆掉这个堆叠,分支层面不用动任何东西。

merge stack 才是省事的部分

单看堆叠本身,可能觉得只是列表好看了一点。真正让我觉得值的是「merge stack」:按顺序一次性合并整条链,省掉手动 rebase 和逐个合并的过程。以前堆叠 PR 最烦的就是底部 PR 合并之后,上面每一层都要 rebase、force push、再逐个点合并,链一长就是纯粹的体力活。现在这一步被收进一个操作里了。

这里留一个坑:我试了 gh stack 命令,返回的是「unknown command」。看起来要么需要装 CLI 扩展,要么得升级 gh 版本,我本地这个版本还不支持。命令行这条路我还没跑通,等之后版本更新了再补一条笔记。

和 AI 生成代码的关系

原文里有一个观点我比较认同,想单独展开说说。作者认为堆叠 PR 给 AI 编码代理提供了第三种选择:以前让 agent 做一个完整功能,基本只有两条路——要么生成一个巨大的 PR,审查的人看到 diff 就想关掉;要么拆成一堆互相没有关联的 PR,顺序全靠口头约定,合并的时候照样乱。堆叠相当于第三条路:按可审查的层次构建功能,用依赖链把顺序表达出来,审查的人从底往上逐层看,每一层都是能独立理解的大小。

我自己让 agent 写东西的时候确实一直卡在这个矛盾上:限制它的改动范围,功能做不完整;放开范围,diff 就没法读。我之前记的另一篇笔记里写过类似的问题,当时的解法是手动拆 commit,很累。如果堆叠链能被 agent 直接构造出来,人工介入的点就从「拆」变成了「审」,这个分工我觉得是对的——拆是机械劳动,审是必须自己做的判断,后者不能外包。

不过这里要老实说一句,agent 自动构造堆叠链这件事我自己还没实际跑过,上面这段更多是看了演示之后的推断,不是验证过的结论。等真的让 agent 搭过一次再回来更新。

评论区一瞥

顺手看了眼原文评论区,有一条调侃说「GitHub 重新发明了 commit」,挺好笑,也不算全错——堆叠 PR 某种意义上就是把「一系列有依赖关系的提交」用 PR 的粒度重新表达了一遍。也有人说文章里的前后对比数据说服他去试了。这类功能确实是这样,光看概念描述没感觉,跑一遍才知道省不省事。

划重点

第一,堆叠 PR 不需要额外工具,普通分支加改 base 指向就能触发,验证方式是看顶部 PR 的横幅;第二,堆叠只是 GitHub 侧的元数据,分支本身无变化,想拆随时能拆;第三,merge stack 是核心收益所在,按序合并整条链,手动 rebase 的体力活没了;第四,gh stack 命令目前在我这里还不存在,CLI 这条路留个坑。这篇就记到这里,你可以拿一个自己正在做的功能照着搭一条三层链试试,如果踩到别的坑,欢迎回来留言,我一起补一条笔记。

abanana
abanana

把自己踩过的坑整理成一篇能复现的笔记,写给三个月前的自己看。

查看主页 →