跳到正文
北京时间
原文
GitHub Blog· Julia Muiruri·· 2026-08-05精选AI 评分67

GitHub 如何用堆叠式 Pull Request 拆解 AI 生成的巨型代码

Turn one giant AI-generated pull request to a reviewable stack

AI 导读

GitHub 介绍用堆叠式 Pull Request(stacked PR)解决 AI 编码智能体生成巨型代码难以审查的问题。通过将 1,000+ 行的大 diff 按数据、API、接线、UI 拆成 L1-L4 四个独立分层,每层可分配不同审查者。

推荐理由

详细演示了用 GitHub 堆叠 PR 将 AI 生成的大 PR 拆分为逻辑层,配合 gh-stack CLI 和技能,提供可立即采用的审查优化方案。

正文 · AI 翻译

想想你上一次交付的大功能。说实话。你是把它硬塞进一个巨大的 pull request,还是拆成了多个范围更小的 pull request?多年来,你一直不得不默默地在两者之间做选择:要么看着一个 pull request 膨胀到审查它变成一场噩梦,要么把它拆成一连串更小的 pull request,而你得照看它们、手动同步,并且每当底层引入变更时都要理清冲突。

两种选择都有取舍。一种难以审查,另一种难以维护。你当天的决定会倾向于痛苦更少的那一个。

现在再加上编码智能体。它们的生产力惊人,并且预计到 2028 年将在每个 SDLC 阶段带来 50% 的生产力提升, 根据 Gartner 的说法。但是,它们无法替你免除如何组织 pull request 的选择。它们反而放大了做出这一选择的必要性。

在本文中,跟随一个示例,了解如何使用堆叠 pull request 来简化审查。

深入探究:为购物助手添加商品搜索

假设你发出一个提示词,要求为购物助手添加商品搜索,然后走开,几分钟后,真的就是几分钟,你回来进行审查、引导和批准。但仔细看看,那个单一的 pull request 里往往会包含什么:

  • 一个新的数据模型及其种子数据
  • 一条 API 路由及其校验
  • 客户端接线、UI,以及空状态/回退状态/错误状态

……所有这一切乃至更多,全都塞在一个庞大到 1,000+ 行的 diff 里。

Animated gif showing the pull request size grow from 0 lines to over 1,500 lines.

对于主要基于多年来代码传统写法训练出来的智能体而言,这种模式就是它们默认的交付方式。让我们把这件事推演一遍。

你想在一个现有的 Web 应用上添加商品搜索,而你的起始状态是:

  • 一个模拟的 AI Assistant,展示来自随机行生成器的响应
  • 不一致的商品数据被硬编码并散落在各个组件中
  • 没有目录模块,没有 API,没有数据层——什么都没有
Screenshot of the starting state of the website without a product search.

有人开了一个 issue 来实现这个功能,典型流程是创建一个功能分支,把它分配给一个编码智能体(或多个自定义智能体),拿到整个实现代码和更新后测试的初稿……

视频封面视频 · 前往原文观看

……你读代码(好吧,你也许读了代码)。然后,你仍然需要手动验证功能行为并做任何必要的更新,推送并打开一个 pull request,附上它那又长又浅的 AI 生成描述,确保 CI 检查全绿,然后自我审查 diff 再请求审查者。你开始动手了……

<reviewer's hat>

审阅者:改了 1,721 行!!这个描述没什么用。我稍后再审阅。

</reviewer's hat>

接下来发生的事情就很熟悉了:

  • 庞大的 pull request 变得难以审阅——于是它就……搁在那儿了。
  • 审阅者失去上下文,反馈质量随之下降。
  • 合并也会变得更慢。

这会启动一个手动、混乱、耗时的流程,在功能落地之前就容易产生冲突,而最终落地时又审查不足。

GitHub 堆叠式 pull request

堆叠式 pull request引入了一种不同且更好的交付结构。其原理很简单:分解。与其追求用一个 pull request 完整解决整个问题,不如把功能拆解为若干逻辑层,并识别出通往目标的依赖链。这样一来,你以及你的智能体就有了一种原生的方式,把原本会落成一个巨大 pull request 的工作,分解为一连串小而聚焦、可独立审查的层。

那个难以审查的大型 pull request 会变成一叠更小、按逻辑排序的 pull request,每个都只聚焦于单一关注点,小到足以让审查者在脑中把握,并且只带有恰到好处的上下文自然地承接自上一个已审查的 pull request。

让我们把它变为现实。

技术栈结构

我们来看看分解问题并安排分层堆栈时所涉及的步骤。

首先,也是重要的一点,设置堆栈基础。这很关键,因为整个堆栈管理生命周期中的 CI 检查和合并规则都会针对堆栈基础进行评估。

然后,确定核心基础工作单元,并将其放在更靠近基础的位置(堆栈中最低处),再将依赖它的工作分层叠加在其上。

栈层级(L#)/分支要交付的内容依赖于
L1(feat/catalog-data)一个带类型定义的目录,包含种子数据、校验以及数据访问模块main(栈基础)
L2(feat/search-api)已验证 /api/products/search 端点feat/catalog-data
L3(feat/chat-grounding)Chat 调用该 API,并基于真实产品数据作答feat/search-api
L4(feat/grounded-ui)产品引用卡片 + 状态feat/chat-grounding

现在各个独立的关注点已经清晰:数据、API、接线、UX,这使得可以为每一个分配不同的审查者群体。数据由数据负责人审查,UX 由UI 负责人审查。

GitHub 对堆叠式拉取请求的原生支持可以从拉取请求 UI 启动,并通过gh stack CLI 无缝扩展到终端。

安装堆叠式拉取请求 CLI 扩展

运行以下命令:

gh extension install github/gh-stack
视频封面视频 · 前往原文观看

在远古时代,你准备好就可以开始工作了。但今天不行。有智能体与你并肩工作。这些智能体需要学习技术栈如何运作,以及如何代表你创建和管理它们。gh-stack skills教会了它们这些。

gh skill install github/gh-stack

或者,如果你更喜欢:

npx skills add github/gh-stack
视频封面视频 · 前往原文观看

针对上述示例中的具体功能,你的开发工作流拥有自定义智能体,每个智能体都有明确的工作流,并遵循严格的范围界定纪律,以实现小而单一范围的拉取请求这一目标。

层级/分支智能体
L1 (feat/catalog-data)数据建模智能体
L2 ( feat/search-api)后端智能体
L3 ( feat/chat-grounding)前端智能体
L4 ( feat/grounded-ui)前端智能体

设置的最后一步是确认 CI 已存在。如前所述,每个拉取请求都会基于堆栈基底进行评估,而这些检查将针对每一层运行。

现在,工作开始了。

第一层:数据目录基础

如今大多数智能体工作流都是自动化的,并以循环方式自主执行,但为了便于说明,我们将逐步介绍每个步骤。

此时,所有智能体都已熟悉堆叠式拉取请求的工作方式,因此这一阶段的典型工作流是:

  1. 用合适的提示词调用数据建模智能体
  2. 该智能体初始化一个新的堆栈并设置第一个分支——feat/catalog-data,以 main 作为其基底,使用 gh init stack
  3. 检出、工作并运行验证
  4. (All checks == green) ? commit the layer : Iterate
视频封面视频 · 前往原文观看

给未来审查者的提示:类型正确吗?数据经过验证了吗?查询辅助函数安全吗? 就这样。

第二层:产品搜索 API

遵循类似这样的流程:

  1. 用合适的提示词调用后端智能体
  2. 该智能体添加下一层feat/search-api 在第一层之上,其基础为:feat/catalog-data,以通过gh stack add导入已完成的数据访问模块
  3. 检查通过、可运行并执行验证
  4. 开发者手动测试 API
  5. (API works && All checks == green) ? commit the layer : Iterate
视频封面视频 · 前往原文观看

评审者留给未来的备注:输入是否经过验证?响应契约是否稳定?错误/空状态是在这里处理还是推给下游?就这样。

第三层:将聊天接入 API

在下一层中,你需要:

  1. 用合适的提示词调用前端智能体
  2. 该智能体添加下一层feat/chat-grounding 在第二层之上。其基础为:feat/search-api,它将从数据访问模块和已验证的 API 两者分支出来。
  3. 检查通过、可运行并使用 Playwright 运行浏览器测试
  4. (All checks == green) ? commit the layer : Iterate
视频封面视频 · 前往原文观看

给未来的评审者备注:每个答案是否都能追溯到真实的 API 响应?当 API 失败或什么都不返回时会发生什么?就这样。

第四层:有依据的 UI 与引用

你会注意到,第三层和第四层尽管出自同一位作者(前端智能体),却被清晰地分层。这是有意为之。UI 的负责人不应该去检查底层的数据流,反之亦然,而这种结构正好支持这种独立性。

所以,前端智能体:

  1. 在第三层之上添加下一层 feat/grounded-ui 以第三层为基础,它的基础是:feat/chat-grounding
  2. 检查无误、可运行,并用 Playwright 运行浏览器测试
  3. (All checks == green) ? commit the layer : Iterate
视频封面视频 · 前往原文观看

给未来的评审者备注:每个引用是否都链接回真实的产品?加载、空状态和错误状态是否都已覆盖?就这样。

提交整个技术栈

四个本地堆叠分支已准备就绪。接下来用 gh stack push 将它们推送到远程,然后用 gh stack submit 在 GitHub 上创建关联它们的拉取请求。

视频封面视频 · 前往原文观看

每一层的堆栈图和 CI

切换到 GitHub,四个拉取请求都已打开,在每个拉取请求的顶部,你会看到一个 堆栈图,这是一个在堆栈中各拉取请求之间一键导航的系统。

视频封面视频 · 前往原文观看

审查和更新堆栈

是时候换个视角,看看审查者浏览堆叠式拉取请求的历程了。

<reviewer’s hat on>

栈映射图是评审者的指南针——一种在栈顶与栈底之间导航、朝着成功合并前进的辅助工具。其移动是有方向性的:自上而下阅读,自下而上评审。

  • 自上而下阅读,是为了获取上下文。这让你在评审流程的一开始就掌握最终目标,从而能够确定方向。“哦,原来我们是想在聊天界面上展示产品卡片。”
  • 自下而上评审,是为了在预先确定的检查点上逐步构建。只有理解了前一层,每一层的实现才有意义。

正如我们示例中所见,你不再需要面对一个 1,720+ 行的单个 pull request 并一次性完成评审,而是可以将评审分散到栈中一个个小型、自包含的目标上。

视频封面视频 · 前往原文观看

作为被指定的人类在环审核者,你介入并查看第一层,也就是栈底部的拉取请求,发现自动 Copilot Code Review(CCR)捕获了两个你也认为应当修复的问题。

<developer's hat back on>

变更请求在栈底部被提出,于是你:

  • 将反馈交给第一层作者,即拥有该分支的数据建模智能体
  • 建议被应用、测试、提交并推送
  • 一旦修复落到 feat/catalog-data 上,接下来自然的问题是:这对第二层、第三层和第四层意味着什么?

由于分支 feat/catalog-data 在审核后被不合时宜地推送,GitHub 明确标记:“此栈中的某些分支已分叉,必须进行 rebase”,并伴有“无法作为栈合并”的标记,从而阻止了合并。

视频封面视频 · 前往原文观看

回到 GitHub 上的拉取请求界面,会出现一个一键式 Rebase stack 按钮。在使用该按钮之前,有一点重要事项值得注意。使用此按钮触发基于网页的 rebase 会在 GitHub 的服务器上运行,这意味着它会将提交者重置为点击该按钮的人,由此产生的提交不会被签名,而如果分支保护要求签名提交,那么这一次点击就会悄无声息地将其破坏。

从终端执行更安全的等效操作是:gh stack rebase 在本地执行同样的级联 rebase,同时交互式地解决冲突,但这一次使用你自己的 Git 配置,然后 gh stack push。

最后,你将沿着整个堆栈向上传播。 堆栈的其余部分,无论是本地还是 GitHub 上的,现在都需要跟上,而这再简单不过了,只需一条同步命令 gh stack sync。

一体化流程首先从 origin 拉取,将 feat/catalog-data 之上每个分支级联 rebase 到新提交上,推送 rebase 后的分支,并从 GitHub 同步 pull request 状态。这样,变更会向上层层传递,无需任何人手动处理第二、三、四层。

回到 GitHub,所有检查重新运行、通过,堆栈图重新恢复为从 main 到 feat/grounded-ui 的一条干净、可合并的直线。

视频封面视频 · 前往原文观看

开始使用 堆叠式 pull request >

来源:GitHub Blog · github.blog