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

- 来源：GitHub Blog
- 作者：Julia Muiruri
- 发布时间：2026-08-05 00:47
- AIHOT 分数：67
- AIHOT 标记：精选
- AIHOT 链接：https://aihot.virxact.com/items/cmsewck8518x6ro2e32q4bksr
- 原文链接：https://github.blog/engineering/turn-one-giant-ai-generated-pull-request-to-a-reviewable-stack

## 精选理由

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

## AI 摘要

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

## 正文

回想一下你最近发布的一个大功能。说实话，你是把它硬塞进一个巨大的 pull request 里，还是拆成了多个范围更小的 pull request？多年来，你一直默默地在两种选择之间纠结：要么看着一个 pull request 膨胀到审阅变成噩梦，要么把它拆成一串更小的 pull request，然后你得像保姆一样盯着它们，手动同步，每次下面引入改动时还要解开各种冲突。

两种选择都有取舍。一种难审阅，另一种难维护。你那天做的决定，倾向于选那个痛苦更少一点的选项。

现在再加上编码智能体。它们生产力极高，根据 Gartner 的预测，到 2028 年它们将在软件开发生命周期的每个阶段带来 50% 的生产力提升。但是，它们无法替你消除如何组织 pull request 的选择。它们反而放大了做出这个选择的必要性。

在这篇文章中，跟随一个示例，看看如何用堆叠式 pull request 来简化审阅。

深入来看：为购物助手添加商品搜索功能

假设你发出一个提示词，要求给购物助手添加商品搜索功能，然后走开，几分钟后——真的就是几分钟——你回来审阅、引导并批准。但仔细看看，那个单独的 pull request 里通常会落下些什么：

一个新的数据模型及其种子数据

一条 API 路由及其校验逻辑

客户端接线、UI，以及空状态/兜底状态/错误状态

……所有这些，甚至更多，都堆在一个巨大的、超过 1000 行的 diff 里。

对于很大程度上基于多年来代码传统写法训练出来的智能体来说，这种模式就是它们默认的交付方式。让我们把这个过程走一遍。

你想在一个现有的 Web 应用上添加商品搜索功能，你的初始状态是：

一个模拟 AI 助手，其回复来自随机行生成器

不一致的商品数据被硬编码并散落在各个组件中

没有目录模块，没有 API，没有数据层——什么都没有

一个 issue 被创建出来以实现该功能，典型的流程是创建一个功能分支，把它分配给一个编码智能体（或多个自定义智能体），拿到整个实现代码的第一版草稿以及更新后的测试……

视频 · 前往原文观看

……你阅读了代码（好吧，你大概只是扫了一眼代码）。然后，你仍然需要手动验证功能行为并进行必要的更新，推送并打开一个带有又长又浅的 AI 生成描述的拉取请求，确保 CI 检查通过，然后自查 diff 再请求审查者。你开始动手……

<reviewer's hat>

审查者：改了 1721 行！！这个描述没什么帮助。我晚点再看吧。

</reviewer's hat>

接下来发生的事情大家都很熟悉：

这个庞大的拉取请求变得难以审查——于是它就……一直搁在那儿。

审查者丢失了上下文，反馈质量也随之下降。

合并变得更加缓慢。

这开启了一个手动、混乱、耗时的流程，在功能落地之前很容易产生冲突，而最终功能落地时也往往审查不足。

GitHub 堆叠式拉取请求

堆叠式拉取请求引入了一种不同且更好的交付结构。其原理很简单：分解。与其试图用一个拉取请求完整解决整个问题，不如将功能拆分成逻辑层次，并识别出达成目标所需的依赖链。这为你和你的智能体提供了一种原生的方式，将原本会落在一个巨型拉取请求中的工作，分解成一系列小而聚焦、可独立审查的层次。

那个难以审查的大型拉取请求，变成了一叠更小、逻辑有序的拉取请求，每个都只关注单一问题，小到审查者能轻松装进脑子里，并且有足够的上下文自然地从先前已审查的拉取请求中流动过来。

让我们来实现它。

堆叠结构

让我们看看分解问题并排列分层堆叠时所涉及的步骤。

首先，也是重要的一点，设置堆叠基线。这一点很关键，因为在整个堆叠管理生命周期中，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、接线、用户体验，从而可以为每一层分配不同的评审人员。数据由数据负责人评审，用户体验由 UI 负责人评审。

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

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

运行以下命令：

gh extension install github/gh-stack

视频 · 前往原文观看

在远古时代，你就能直接开始干活了。但今天不行。现在有智能体在你身边协同工作。这些智能体需要学习堆叠式拉取请求的工作原理，以及如何代表你创建和管理它们。gh-stack 技能就是教它们这些的。

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 已存在。如前所述，每个拉取请求都将针对堆叠基线进行评估，这些检查将对每一层运行。

现在，工作开始了。

第一层：数据目录基础

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

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

用适当的提示词调用数据建模智能体

智能体初始化一个新的堆叠，并设置第一个分支——feat/catalog-data，以 main 作为其基线，使用 gh init stack

检出分支、进行工作并运行验证

（所有检查均通过）？提交该层 : 迭代

视频 · 前往原文观看

给未来评审者的备注：类型是否正确？数据是否经过验证？查询辅助函数是否安全？句号。

第二层：产品搜索 API

遵循类似流程：

用适当的提示词调用后端智能体

智能体在第一层基础 `feat/catalog-data` 之上添加下一层 `feat/search-api`，通过 `gh stack add` 导入已完成的数据访问模块。

检出代码，运行验证，一切正常。

开发者手动测试 API。

（API 正常 && 所有检查通过）？提交该层：迭代。

视频 · 前往原文观看

给未来的审查者备注：输入是否经过验证？响应契约是否稳定？错误/空状态是在这里处理还是推给下游？完毕。

第三层：将聊天功能接入 API

在下一层中，你需要：

用合适的提示词调用 Frontend 智能体

智能体在第二层基础之上添加下一层 `feat/chat-grounding`。其基础分支为 `feat/search-api`，该分支将同时包含数据访问模块和已验证的 API。

检出代码，运行验证，并用 Playwright 运行浏览器测试。

（所有检查通过）？提交该层：迭代。

视频 · 前往原文观看

给未来的审查者备注：每个回答是否都能追溯到真实的 API 响应？当 API 失败或返回空结果时会发生什么？完毕。

第四层：有依据的 UI 与引用标注

你会注意到第三层和第四层虽然作者相同（都是 Frontend 智能体），但分层方式截然不同。这是有意为之。UI 的负责人不应该需要检查底层数据流，反之亦然，这种结构正好保证了这种独立性。

于是，Frontend 智能体：

在第三层基础之上添加下一层 `feat/grounded-ui`，其基础分支为 `feat/chat-grounding`。

检出代码，运行验证，并用 Playwright 运行浏览器测试。

（所有检查通过）？提交该层：迭代。

视频 · 前往原文观看

给未来的审查者备注：每条引用是否都能链接回真实产品？加载、空状态和错误状态是否都已覆盖？完毕。

提交整个栈

四个本地堆叠分支已就绪。下一步是用 `gh stack push` 将它们推送到远程，然后用 `gh stack submit` 在 GitHub 上创建相互关联的拉取请求。

视频 · 前往原文观看

栈映射图与每一层的 CI

切换到 GitHub 界面，四个拉取请求都已打开，在每个请求的顶部你都能看到栈映射图，这是一个在栈内各拉取请求之间一键导航的系统。

视频 · 前往原文观看

审查与更新栈

是时候换个角色，从审查者的视角来看待堆叠式拉取请求的流程了。

<reviewer’s hat on>

栈映射（stack map）是评审者手中的指南针——在栈顶与栈底之间提供导航，指引变更走向成功合并。其移动方向是明确的：自上而下阅读，自下而上评审。

自上而下阅读，是为了获取上下文。这样你在评审过程一开始就能看到最终目标，从而确定方向。“哦，原来我们是想在聊天界面上展示产品卡片。”

自下而上评审，是为了在既定检查点上逐层推进。只有理解了前一层，后一层的实现才有意义。

你不再需要像我们示例中那样，一次性评审一个超过 1,720 行的巨型 pull request，而是可以将评审分散到栈中一个个小而完整的独立目标上。

视频 · 前往原文观看

作为被指定的人工介入评审者，你进入后先看第一层，也就是栈底的那个 pull request，发现自动化的 Copilot Code Review（CCR）捕获了两个问题，而你也同意这两个问题应当修复。

<developer's hat back on>

变更请求发生在栈底，于是你：

将反馈交给第一层的作者——负责该分支的数据建模智能体

建议被采纳、测试、提交并推送

一旦修复落地到 feat/catalog-data 分支，自然而然的下一个问题是：这对第二层、第三层和第四层意味着什么？

由于 feat/catalog-data 分支在评审之后被插队推送，GitHub 会明确标记：“此栈中的部分分支已分叉，必须进行变基（rebase）”，并同时显示“无法作为栈合并”的标记，从而阻止合并。

视频 · 前往原文观看

回到 GitHub 的 pull request 界面上，会出现一个一键式的 Rebase stack 按钮。在使用该按钮之前，有一点值得注意。通过该按钮触发基于 Web 的变基操作会在 GitHub 的服务器上执行，这意味着它会将提交者重置为点击按钮的那个人，生成的提交不会被签名，如果分支保护要求签名提交，那么这一键点击就会悄悄出问题。

更安全的等效做法是在终端中执行 `gh stack rebase`，在本地完成同样的级联变基，同时交互式解决冲突，但这次使用的是你自己的 Git 配置，然后再执行 `gh stack push`。

最后，你需要在整个栈中向上传播变更。栈的其余部分——无论是本地还是 GitHub 上——现在都需要跟上，而最简便的方式莫过于一条同步命令：`gh stack sync`。

这一站式流程从拉取远端更新开始，将 `feat/catalog-data` 之上每一层分支的变基操作级联应用到新提交上，推送变基后的分支，并从 GitHub 同步拉取请求状态。这样一来，变更会逐层向上传递，无需任何人手动触碰第二、第三或第四层。

回到 GitHub 上，所有检查都会重新运行并通过，栈图也会恢复成一条从 `main` 到 `feat/grounded-ui` 的干净、可合并的直线。

视频 · 前往原文观看

开始使用堆叠式拉取请求 >
