📌 项目地址:github/gh-stack | ⭐ 756 颗星 | 🔧 Go | 📜 未标注
先解决一个现实问题
一个 PR 带 40 个文件、上千行改动,评审人打开就想关掉。
问题不在代码质量,是评审单元太大。人脑一次能消化的上下文有限,超过几百行的 diff,理解成本就开始失控。
栈式 PR(Stacked PR)的思路是:把一个大改动按依赖关系拆成一条分支链,每个分支对应一个独立 PR,base 指向链中底下的分支。GitHub 会为每个 PR 显示单独一层增量,评审人按顺序逐层看,每层都足够小。
仓库地址:https://github.com/github/gh-stack
github/gh-stack 是 GitHub 官方的 CLI 扩展,用 Go 写,目前 756 个 star。它把创建分支、管理顺序、推送、设 base、批量开 PR 这些繁琐环节自动化。
安装
gh extension install github/gh-stack
要求 GitHub CLI v2.0 以上。装完直接 gh stack。
栈长什么样
栈是有序分支列表,每层继承自下面那层。最底下是 trunk,默认是仓库的默认分支(通常 main):
frontend → PR #3 (base: api-endpoints) ← top
api-endpoints → PR #2 (base: auth-layer)
auth-layer → PR #1 (base: main) ← bottom
─────────────
main (trunk)
bottom 离 trunk 最近,top 最远。导航命令按这个模型:up 朝远离 trunk 方向走,down 朝靠近 trunk 方向走。
提交时,gh stack 为每个分支各建一个 PR,每个 PR 的 base 自动指向栈里下一层的分支。评审人看 api-endpoints 的 PR,只会看到它相对于 auth-layer 的新增内容,不包含下层代码。这是整个设计的核心。
五条命令跑通流程
# 初始化栈,创建并切换到第一个分支
gh stack init
# 在第一个分支上提交
# ...
# 在栈顶再加一个分支
gh stack add api-endpoints
# 在这个分支上提交
# ...
# 推送所有分支
gh stack push
# 查看栈结构、分支状态和 PR 状态
gh stack view
# 为每个分支创建 PR,base 自动指向下一层
gh stack submit
这套流程代替了最繁琐的手工操作。没有工具时,栈的顺序、每个 PR 的 base、推送先后全靠人记,层数一多就乱。gh stack submit 自动处理 base 关系,你只负责按顺序写代码。
栈元数据存在 .git/gh-stack,一个 JSON 文件,不提交进仓库。它记录哪些分支属于哪个栈、顺序是什么。rebase 中断时的恢复状态单独存在 .git/gh-stack-rebase-state。
这意味着栈关系是本地状态。换机器、重新 clone,GitHub 上也看不到”栈”——服务端只看到一组 PR 的 base 互相指来指去。要恢复栈,得重新 init。这两个文件不能乱删。
init 的两个模式
gh stack init 不传参数时是交互模式,提示输入分支名,也可以用当前分支当第一层。
传分支名就直接执行:
gh stack init feature-auth
已有分支自动收进栈,不存在的分支自动创建。trunk 默认是仓库默认分支,用 --base 覆盖。
init 有个实际影响很大的行为:自动启用 git rerere。这个功能原本默认关闭,作用是把你解决冲突时做的选择记下来,下次遇到同样 hunk 的冲突,直接用上次的结论。
栈式开发里 rebase 频繁,同一批代码的冲突会反复出现。第一次手工解决后,后面的 rebase 自动套用结果。
我提一句:第一次解决冲突时想清楚再动手。你选的方案会被记住并自动重复。选错了,后面每次 rebase 都沿错的结果走。
AI 编码工具也能用
gh-stack 提供了 skill 集成:
gh skill install github/gh-stack
装完后,AI 编码代理能识别栈式 PR 的工作方式:哪些分支可以 rebase,base 关系怎么维护。AI 改代码开始常见,这个能力算实用。
我对这个工具的判断
栈式 PR 的前提是评审可以分层。每层 PR 是独立评审单元,按顺序看,每层改动量小,上下文连贯。如果团队习惯一次性合并全部改动,栈式 PR 价值不大。
另一个现实问题:层数越多,底层分支的 rebase 越频繁。顶层依赖整个下层,中间任何一层动,上面全部跟着 rebase。rerere 能减少重复冲突的手工成本,但冲突本身消不掉。
gh-stack 把栈式开发最麻烦的部分——分支创建、rebase、base 维护、批量推送——压成几条命令。GitHub 官方维护,处理 PR base 关系比第三方脚本可靠。本地存储元数据是核心设计,也是决定用之前需要接受的最大限制。
用栈式 PR,这个工具目前是最省事的方案。装一次,跑一遍 init + submit,能直观感受到省了多少事。