GitHub Stacked PRs:大 PR 终于有了原生拆分方案

一个所有人都遇到过的问题

打开一个 Pull Request,发现改了 80 个文件、2000 多行代码。Review 的人看到这个数字,叹口气,要么硬着头皮翻完给个 LGTM,要么拖上几天甚至几周都没人碰。

这不是个例,而是几乎所有用 GitHub 做协作开发的团队都会面对的日常。PR 越大,Review 质量越差,合并周期越长,冲突概率越高——这是一个恶性循环。

用过 Phabricator 的人会告诉你,stacked diffs 早就解决了这个问题。Google 和 Meta 内部的代码审查流程也依赖类似机制。但在 GitHub 上,想实现类似的工作流一直需要借助第三方工具(Graphite、Aviator 等),体验割裂且配置繁琐。

现在,GitHub 官方终于下场了——Stacked PRs 作为原生功能进入 GitHub,配合全新的 gh stack CLI 扩展,开发者可以直接在 GitHub 生态内完成「拆分、审查、合并」的完整闭环。

什么是 Stacked PRs

简单说,一个 Stack 就是一系列有序的 Pull Request,每个 PR 以上一个 PR 的分支为 base,形成一条链式结构,最终指向主分支:

graph LR A[main] --> B["PR #1 · auth-layer ← bottom"] B --> C["PR #2 · api-endpoints"] C --> D["PR #3 · frontend ← top"]

每个 PR 只包含自己那一层的变更。Reviewer 看到的 diff 是当前层与下方一层(更靠近 main 的那一层)之间的差异,而不是相对于 main 的巨大 diff。这意味着:

  • 每个 PR 都足够小且聚焦,Reviewer 能在几分钟内完成审查
  • 各层可以独立 Review,不需要等整条链全部就绪
  • 上下文不会丢失,GitHub UI 顶部的 Stack Map 让人随时了解全局进度

A stack is a series of pull requests in the same repository where each PR targets the branch of the PR below it, forming an ordered chain that ultimately lands on your main branch.

gh stack CLI:终端里的完整工作流

GitHub 为 Stacked PRs 提供了一个专门的 CLI 扩展 gh stack。安装方式很直接:

1
gh extension install github/gh-stack

装好之后,整个工作流非常流畅:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 初始化一个 Stack,创建并切换到第一个分支
gs init auth-layer
# 写代码、提交 ...

# 添加新层
gs add api-routes
# 继续写代码、提交 ...

gs add frontend
# 继续写代码、提交 ...

# 推送所有分支
gs push

# 创建(或更新)所有 PR
gs submit

其中 gsgh stack 的可选别名(通过 gh stack alias 设置)。

几个关键命令值得了解:

命令作用
gs init <name>初始化 Stack,创建第一个分支
gs add <name>在当前 Stack 顶部添加新层
gs push推送所有分支(使用 --force-with-lease
gs submit推送分支并创建/更新 PR,建立 Stack 关联
gs sync一站式同步:fetch → 快进主分支 → rebase → push → 同步 PR 状态

gs sync 是日常用得最多的命令——本地改完代码后跑一次,就能把整条 Stack 同步到最新状态。完整的命令列表(包括 viewcheckoutup/down 导航等)可参见 CLI 命令参考

GitHub UI 中的原生体验

Stacked PRs 不只是一个 CLI 工具,GitHub 的 Web UI 也做了原生适配:

Stack Map 导航:每个 PR 页面顶部会展示一个 Stack Map,显示当前 PR 在整条 Stack 中的位置,点击即可跳转到其他层。Reviewer 不需要在 PR 列表里来回翻找。

聚焦的 Diff 视图:每个 PR 只展示本层的变更,而非累积的全量 diff。这是 Stacked PRs 的核心价值所在。

分支保护规则照常生效:CI 检查和分支保护是针对最终目标分支(比如 main)执行的,而不是直接 base 分支。这意味着你不需要为 Stack 做任何特殊的规则配置。

合并机制:自底向上

Stack 的合并遵循自底向上的原则——必须从最底部未合并的 PR 开始,可以一次合并一个,也可以一次合并连续的多个。

具体流程是这样的:

  1. 最底部的 PR 通过了 CI 和 Review,点击合并
  2. 合并后,剩余 PR 会自动 rebase,下一个未合并的 PR 自动将 base 更新为 main
  3. 继续 Review 和合并,直到整条 Stack 全部落地

支持 squash merge、merge commit 和 rebase merge 三种合并方式。如果仓库启用了 merge queue,Stack 中的 PR 也可以正常走 queue 流程。

一个实用的细节是:如果底部连续几个 PR 都已经通过检查,可以一步把它们全部合并,不需要逐个点。但要注意,必须从最底部的未合并 PR 开始连续向上,不允许跳过中间层单独合并上方的 PR。

与 AI Agent 的结合

一个值得关注的点是 gh stack 提供了 AI Agent 集成能力:

1
npx skills add github/gh-stack

这让 AI 编程助手(如 Claude Code 等)能够理解 Stack 的概念,在生成大规模变更时自动拆分为合理的 Stack 结构,或者在已有 Stack 上继续开发。

在 AI 辅助编程越来越普遍的今天,Agent 生成的代码量往往远超手写——如果这些变更能自动按逻辑拆分成小 PR,Review 压力会显著降低。

社区怎么看

这个功能在 Hacker News 上引发了热烈讨论,截至发稿时已获得近 900 个赞和 500 多条评论。

社区的反应大致分几个方向:

用过 Phabricator 的老兵们很兴奋。 不少人表示,离开 Meta/Google 后最怀念的就是 stacked diffs 工作流。现在 GitHub 终于有了原生支持,不再需要第三方工具的黏合。

对第三方工具的影响。 Graphite、Aviator 等专门做 stacked PRs 的工具会受到直接冲击。当平台方亲自下场,第三方的生存空间不可避免地被压缩。

关于 jujutsu(jj)的讨论。 很多人提到 jujutsu 作为新一代版本控制前端,天然支持类似工作流。不过 gh stack 的优势在于零成本——你不需要换掉 Git,也不需要学习新的版本控制概念。

一些务实的担忧。 有人指出 rebase 链条越长,冲突处理越麻烦;也有人担心 Stack 中间层被要求大改时的级联影响。这些是实际使用中需要面对的问题,但多数人认为利大于弊。

当前状态与限制

需要注意的是,Stacked PRs 目前仍处于 Private Preview 阶段,需要申请加入等候名单才能使用(目前申请主要向企业用户开放,笔者并未获取到试用资格):

Sign up for the waitlist

这意味着功能还在持续迭代中,正式 GA 的时间和最终形态可能还会有变化。

写在最后

大 PR 的问题在行业里存在了太久。不是大家不想拆,而是 GitHub 原生不支持这种工作流,手动维护分支链条的心智负担太重。

Stacked PRs 的出现降低了这个门槛——gs initgs addgs submit,三个命令就能把一个大变更拆成结构清晰的 PR 链。对于追求代码质量和 Review 效率的团队来说,这是一个值得尽早尝试的功能。

参考资料