大型拉取请求难以审查并造成瓶颈,尤其是在 AI 帮助短时间内生成大量代码时。 随着拉取请求大小的增加,评审质量也会下降。 审阅者可能会忽略结果、错过问题,或者拖延并将拉取请求搁置,直到拉取请求变得过时并产生合并冲突。
堆叠式拉取请求使大型代码更改便于审查。
堆栈是同一存储库中的一系列拉取请求,每个拉取请求都面向其下方的拉取请求的分支,形成位于单个分支(通常是主分支)上的有序链。 获取一组较小的拉取请求,而不是一个大型拉取请求。 由于每个拉取请求都有自己的重点差异,因此团队成员可以独立评审和批准每个层。
本教程逐步讲解如何将堆叠拉取请求与代理配合使用,以在可单独审阅的层中构建功能。 对于我们的示例,我们将考虑如何将用户身份验证添加到应用。 我们将使用 GitHub Copilot CLI 以及 gh-stack 代理技能。
先决条件
若要将 gh-stack 技能与代理配合使用,首先需要安装 GitHub CLI 以及 gh-stack CLI 扩展。 需要具备以下条件:
- GitHub CLI (
gh) 2.90.0 或更高版本,以及 Git 2.20 或更高版本。- 使用
gh auth login对GitHub CLI进行身份验证。
- 使用
- 可以推送到的 GitHub 存储库。
- 已安装并已登录的 GitHub Copilot CLI
在GitHub CLI中,安装gh-stack扩展和技能。
gh extension install github/gh-stack
gh skill install github/gh-stack
注意
在本教程中,如果你希望自己运行 stack 命令,而不是让 Copilot 这样做,则需要使用 GitHub CLI。
1. 在生成代码之前设计技术栈
一个好的技术栈就像建房子,从坚实的地基开始,搭建墙体框架,安装电线,然后完成石膏板安装。 每一层都依赖于下面一层。 最后,审阅者应该能够从下往上阅读拉取请求,并理解该功能是如何逐步成形的。
- 将功能拆分为层。 每个层都应该是一个连贯的更改,可以单独审查。
- 保持每个层都足够小,这样拉取请求可以快速审阅。 如果某个层需要较长的描述才能评审,它可能太大了。
- 自行决定边界,或与 Copilot 一起制定计划。 无论哪种方式,堆栈的形态都由你决定。
- 按依赖项对层进行排序。 基础性更改位于底部。 任何依赖于它们的内容都会上升。 对于身份验证,可能是:
- 第 1 层:数据模型和迁移
- 第 2 层:CRUD 终结点
- 第 3 层:JWT 中间件和守卫
- 第 4 层:集成和单元测试
示例提示
Propose a layered approach to add user authentication to this app. Order the layers by dependency, keeping each layer independently reviewable.Review my planned layers and flag any that are too large or that depend on a branch above them.
2. 首先构建底层
使用基础创建堆栈。 上述所有内容都取决于正确获取此层。
- 通知 Copilot 你将生成堆叠式拉取请求,并要求它基于计划生成第一层。 智能体使用
gh-stack技能创建堆栈的第一个分支。 - 如果希望自己创建堆栈,请直接使用
gh stack init创建,并使用前缀来保持分支名称整洁,例如gh stack init BRANCH-NAME-1。 - 在继续操作之前,请查看生成的更改。 底层的错误会传播到其上方的每一个分支,因此请在继续操作之前先对底层进行检查。
示例提示
Start the pr-stack and build only the first layer: the user data model and migration.Conduct a review of the generated code and confirm this branch contains only the data model and migration, and nothing that belongs in a later layer.
3. 将每个新代码层叠放在上面
建立基础后,一次构建一层该功能的其余部分。
- 要求 Copilot 添加下一层并结合下方各层的上下文来实现它。 代理程序会将分支添加到分支栈顶部,并在其中提交工作。
- 如果要自行添加分支,请使用
gh stack add BRANCH-NAME-NEXT。 - 如果层开始增长太大,请考虑它是否偏离了计划,或者你实际上需要两个层而不是一个层。
- 在操作过程中为每个层创建新分支,因此每个分支都保持为干净、独立的 diff。
- 准备好创建拉取请求时,请让 Copilot 提交你的堆栈,或者如果要自行执行此操作,请使用
gh stack submit。 - 让每个拉取请求保持独立。 重点标题和对层的简洁、有意义的描述通常足够。
示例提示
Add the next layer in a new branch on top: the CRUD endpoints that use the user model from the branch below.This branch is getting large. Suggest how it could be split into two independently reviewable layers.
4. 在请求评审之前自行查看拉取请求
每个层都很小,使自我审查也更容易。 在让队友参与之前,先逐一检查每个分支。 审阅者应收到你已信任的更改。
- 在每个分支上运行测试、linters 和代码扫描。 在请求评审之前,请 Copilot 帮助你根据标准检查每一层。
- 有关全面审查 AI 生成的更改的方法,请参阅 查看 AI 生成的代码。
5. 从底部开始请求堆栈评审
生成层后,审阅者会看到较小的差异,而不是一大段代码。
- 如果这些依赖项是紧密集成的,请要求从堆栈底部开始进行评审,以便可以在后续评审之前将更改向上集成到整个堆栈中。
- 如果需要不同层的不同人员进行评审,审阅者可以并行工作。 一个人可以查看数据模型,而另一个人则查看终结点,并且两者都无需通读整个功能内容。
6. 根据反馈进行迭代
审阅反馈会分别落在各个图层上,而不是整个对象。 堆栈允许你将正确的层固定在原位,并将更改向上传递。
- 要求 Copilot 修改被审阅者标记的层。 代理将移动到右侧分支,进行更改,并将其提交到该分支。 然后,它会将上面的层重新变基,以便它们包含该修复。
- 将每个修改保留在它所属的层中。 在错误的分支上所做的更改可能会造成混乱,并在更高层引发错误。
- 进行修复时,要求 Copilot 对上述分支进行变基,并同步这些更改。
- 如果要自行在堆栈中移动,请使用
gh stack down、gh stack up或gh stack checkout BRANCH-NAME在分支间导航。 然后,提交更改并运行gh stack rebase --upstack以将更改沿堆栈向上传递。
示例提示
A reviewer flagged that the auth service doesn't handle expired tokens. Fix that on BRANCH-NAME and test the changes.I've rebased the layers above onto this fix. Check that the branch with endpoints still works with the change and flag anything that needs updating.
7. 从底层合并
叠堆按顺序合并,从基于主分支的层开始。 一次性合并层或逐个合并层,GitHub 会自动将下一层的目标重新设为指向 main。
- 从下到上逐个合并堆栈,或者从堆栈中的任意位置开始合并,你所合并的拉取请求下方的所有分支都将从下到上合并。
- 每个层相对于其父级的差异保持完全不变,只有基础发生变化,因此可以轻松一次合并一层,而不会影响正在进行的工作或评审。
- 使用自动合并或合并队列,以便每个分支在批准后立即合并,并且其检查通过。 无需一次性等待整个堆栈。
一旦顶层合并,整个功能就已落地。 每个改动以小而有意识的变更形式进行审查,会比作为一个大型拉取请求(PR)更有效。