GitHub官方推出的CLI扩展,自动管理堆叠式PR——将大改动分解成可独立审查的小PR链。自动处理分支变基、PR基分支设置等繁琐操作。
一个用于管理堆叠分支和拉取请求的 GitHub CLI 扩展。
堆叠 PR 将大型变更分解为一条由小的、可审查的拉取请求组成的链,这些请求相互依赖。gh stack 自动化了繁琐的部分 — 创建分支、保持它们的变基、设置正确的 PR 基础分支,以及在各层之间导航。
gh extension install github/gh-stack
需要 GitHub CLI (gh) v2.0+。
安装 gh-stack skill,让你的 AI 编码智能体知道如何使用堆叠 PR 和 gh stack CLI:
gh skill install github/gh-stack
# 开始一个新的堆栈(创建并检出第一个分支)
gh stack init
# ... 在第一个分支上进行提交 ...
# 在上面添加另一个分支
gh stack add api-endpoints
# ... 进行提交 ...
# 推送所有分支
gh stack push
# 查看堆栈
gh stack view
# 打开一个 PR 堆栈
gh stack submit
堆栈是一个有序的分支列表,其中每个分支都建立在其下方的分支之上。堆栈的底部基于一个主干分支(通常是 main)。
frontend → PR #3 (base: api-endpoints) ← 顶部
api-endpoints → PR #2 (base: auth-layer)
auth-layer → PR #1 (base: main) ← 底部
─────────────
main (trunk)
堆栈的底部是距离主干最近的分支,顶部是距离主干最远的分支。每个分支继承其下方的分支。导航命令(up、down、top、bottom)遵循这个模型:up 远离主干,down 朝向主干移动。
当你提交时,gh stack 为每个分支创建一个 PR,并在 GitHub 上将它们链接为一个堆栈。每个 PR 的基础设置为堆栈中其下方的分支,这样审查者只会看到该层的差异。
堆栈元数据存储在 .git/gh-stack 中(一个 JSON 文件,不提交到仓库)。这跟踪哪些分支属于哪个堆栈及其顺序。在中断的变基期间的变基状态单独存储在 .git/gh-stack-rebase-state 中。
在当前仓库中初始化一个新堆栈。
gh stack init [flags] [branches...]
在本地初始化一个新堆栈。在交互模式(无参数)下,会提示输入分支名称,并提供使用当前分支作为第一层的选项。
当显式给出分支名称时,现有分支会自动被采用,任何缺少的分支都会被创建。除非用 --base 覆盖,否则主干默认为仓库的默认分支。
自动启用 git rerere,以便冲突解决方案在变基中被记住。
# 交互模式 — 提示输入分支名称
gh stack init
# 非交互模式 — 预先指定分支
gh stack init feature-auth feature-api feature-ui
# 使用不同的主干分支
gh stack init --base develop feature-auth
# 将现有分支采用到堆栈中
gh stack init feature-auth feature-api
在当前堆栈顶部添加一个新分支。
gh stack add [flags] [branch]
在当前 HEAD 处创建一个新分支,将其添加到堆栈顶部,并检出它。必须在堆栈的最上层分支上运行。如果未给出分支名称,会提示输入。
你可以选择在 add 流程中暂存更改并创建提交。当 -m 在没有显式分支名称的情况下提供时,分支名称会自动生成为日期+slug 格式(例如,03-24-add_login)。
注意:-A 和 -u 互斥。
# 按名称创建分支
gh stack add api-routes
# 以交互方式提示输入分支名称
gh stack add
# 暂存所有更改、提交并自动生成分支名称
gh stack add -Am "Add login endpoint"
# 仅暂存跟踪文件、提交并自动生成分支名称
gh stack add -um "Fix auth bug"
# 提交已暂存的更改并自动生成分支名称
gh stack add -m "Add user model"
# 暂存所有更改、提交并使用显式分支名称
gh stack add -Am "Add tests" test-layer
# 仅暂存跟踪文件、提交并使用显式分支名称
gh stack add -um "Update docs" docs-layer
# 提交已暂存的更改并使用显式分支名称
gh stack add -m "Refactor utils" cleanup-layer
按堆栈编号、拉取请求编号、PR URL 或分支名称检出一个堆栈。
gh stack checkout [<stack-number> | <pr-number> | <pr-url> | <branch>]
一个纯数字首先被解释为堆栈或 PR 编号(GitHub UI 中显示的仓库范围标识符)。如果没有任何内容与该数字匹配,它会被尝试作为分支名称。
当引用远程堆栈时,该命令会在 GitHub 上获取堆栈,拉取分支,并在本地设置堆栈。如果堆栈已在本地存在且匹配,它会切换到该分支。如果本地和远程堆栈的组成不同,你会收到解决冲突的提示。
当提供分支名称时,该命令仅针对本地跟踪的堆栈进行解析。
在交互式终端中不带参数运行时,会打开一个可搜索的选择器,列出你可用的所有堆栈 — 本地跟踪的堆栈和仅存在于 GitHub 上的堆栈。每行显示堆栈编号、其底部和顶部分支、基础分支、一个汇总其多少 PR 已合并、打开、关闭或尚未推送的状态栏,以及堆栈是本地可用还是仅在远程。用"全部/本地/远程"标签筛选或输入 / 搜索;完全合并的堆栈会被省略。选择仅远程堆栈会在切换到它之前先将其克隆到本地。
# 按堆栈编号检出堆栈
gh stack checkout 7
# 按 PR 编号检出堆栈
gh stack checkout 42
# 按 PR URL 检出堆栈
gh stack checkout https://github.com/owner/repo/pull/42
# 按分支名称检出堆栈(仅本地)
gh stack checkout feature-auth
# 交互模式 — 从所有可用堆栈中选择(本地和远程)
gh stack checkout
从远程拉取并对堆栈进行级联变基。
gh stack rebase [flags] [branch]
从 origin 获取最新更改,然后确保堆栈中的每个分支在其提交历史中都有上一层的末端。从主干向上按顺序对分支进行变基。如果分支的 PR 已合并,变基会自动切换到 --onto 模式,以正确地在合并目标之上重放提交。
如果发生变基冲突,操作会暂停并打印有冲突的文件和行号。解决冲突、用 git add 暂存,并用 --continue 继续。要撤销整个变基,使用 --abort 将所有分支恢复到变基前的状态。
# 对整个堆栈进行变基
gh stack rebase
# 仅对当前分支以下的分支进行变基
gh stack rebase --downstack
# 仅对当前分支以上的分支进行变基
gh stack rebase --upstack
# 对堆栈分支进行变基,无需从主干拉取或与主干进行变基
gh stack rebase --no-trunk
# 解决冲突后
gh stack rebase --continue
# 中止变基并恢复所有内容
gh stack rebase --abort
# 变基并将提交者日期保留为作者日期
gh stack rebase --committer-date-is-author-date
交互式重构当前堆栈。
gh stack modify [flags]
打开一个终端 UI 用于重构堆栈。你可以删除、折叠、插入、重命名和重新排列分支。所有更改在预览期间暂存,保存时一次性应用。
如果 PR 堆栈已在 GitHub 上创建,之后运行 gh stack submit 来推送更改并重新创建堆栈。
删除 (x):从堆栈中移除一个分支及其提交。保留本地分支和相关联的 PR。
折叠下 (d):将分支的提交吸收到下方的分支中(朝向主干)。折叠的分支从堆栈中移除。
折叠上 (u):将分支的提交吸收到上方的分支中(远离主干)。折叠的分支从堆栈中移除。
插入 (i/I):在堆栈中插入一个新的空分支。i 在光标下方插入;I 在上方插入。
重新排列 (Shift+↑/Shift+↓):将分支上移(远离主干)或下移(朝向主干)在堆栈中。
重命名 (r):在本地和堆栈元数据中重命名分支。
撤销 (z):撤销上一个暂存的操作。
前置条件
# 打开修改 TUI
gh stack modify
# 解决冲突后继续
gh stack modify --continue
# 中止并恢复到上一个状态
gh stack modify --abort
在一个命令中获取、变基、推送并同步 PR 状态。
gh stack sync [flags]
执行整个堆栈的同步:
获取 — 从 origin 获取最新更改。
协调远程堆栈——在本地镜像 GitHub 上的堆栈。当 GitHub 上的堆栈新增了 PR(远程进度领先于本地堆栈)时,这些 PR 对应的分支会被拉取下来,并自动追加到本地堆栈。当本地与远程堆栈确实发生分叉时(例如,你在本地添加了一个分支,同时 GitHub 上的堆栈又添加了其他 PR),系统会提示你解决分叉问题(参见下文的“分叉的堆栈”)。在非交互式终端中,发生分叉会中止同步(不会推送或更新任何内容)。
快进 trunk——将 trunk 分支快进到与远程一致(如果已发生分叉则跳过)。
级联变基——将堆栈中的所有分支变基到各自更新后的父分支上(仅在 trunk 发生移动时执行)。如果检测到冲突,所有分支都会恢复到原始状态,并建议你运行 gh stack rebase,以交互方式解决冲突。
推送——推送所有分支(如果发生过变基,则使用 --force-with-lease)。
同步 PR——从 GitHub 同步 PR 状态,并报告每个 PR 的状态。
同步堆栈——将堆栈中处于打开状态的 PR 关联成 GitHub 上的一个堆栈;如果远程堆栈对象尚不存在,则创建它;如果只创建了一部分,则进行更新。仅当存在两个或更多 PR 时才会执行此操作;sync 永远不会创建 PR(请使用 gh stack submit)。
清理——在交互式终端中,提示删除已合并 PR 对应的本地分支。使用 --prune 可自动清理。
当远程仅领先于本地(即在本地堆栈顶部新增了 PR)时,更新会自动拉取,无需提示,因此可安全地在自动化流程中运行 sync。只有当堆栈确实发生分叉时,Sync 才会发出提示。
当任一堆栈都不是另一个堆栈的完整前缀时——例如,你在本地添加了一个分支,同时 GitHub 上的同一堆栈又添加了其他 PR——sync 无法自动合并两者。在交互式终端中,它会提供三个选项:
将远程堆栈作为事实来源——使用远程堆栈的组成替换本地堆栈,并拉取所有缺失的分支。如果你当前所在的分支已不在远程堆栈中,系统会将你切换到最近的仍然存在的分支。此操作要求工作区状态干净,不得存在未提交的更改。
删除 GitHub 上的堆栈——删除 GitHub 上的堆栈对象并停止同步。你的 PR 和本地分支不会受到影响(仅移除 GitHub 上的堆栈);可使用 gh stack submit 重新创建堆栈(如果想更改其结构,请先运行 gh stack modify)。这是让 GitHub 与本地堆栈保持一致的方式,因为与 sync 不同,submit 还会为尚未提交的分支创建 PR。
取消——中止同步,不推送分支,也不更新任何 PR。
在非交互式终端中,发生分叉会中止同步(成功退出),且不会推送分支或更新 PR;请通过取消堆栈关系并重新创建堆栈来解决。
gh stack sync
# Sync and automatically prune merged branches
gh stack sync --prune
将当前堆栈中的活动分支推送到远程。
gh stack push [flags]
通过一次 git push 推送所有活动分支(不包括已合并和已加入队列的分支),并对每个分支执行显式的 --force-with-lease 检查。此次更新不是原子操作:即使某个分支被拒绝,租约检查通过的其他分支也可能已经更新。修复被拒绝的分支后重新运行该命令;已经更新的分支不会发生变化。此命令不会创建或更新拉取请求——请使用 gh stack submit 完成该操作。
gh stack push
gh stack push --remote upstream
推送所有分支,并在 GitHub 上创建或更新 PR 和堆栈。
gh stack submit [flags]
为堆栈中的每个分支创建一个堆叠式 PR,并将这些分支推送到远程。
创建 PR 后,submit 会自动在 GitHub 上创建一个堆栈,将这些 PR 关联在一起。如果该堆栈已经存在于 GitHub 上(例如由之前的 submit 创建),新的 PR 将被添加到堆栈顶部。
如果堆栈中的每个 PR 都已合并,则该堆栈已经完成,无法继续扩展——在其顶部创建的新 PR 会直接以 trunk 为目标,而不是链接到已合并的 PR。在这种情况下,submit 会自动为尚未合并的分支创建一个以 trunk 为根的新堆栈,并将其创建到 GitHub 上,同时保持已合并的堆栈不变。
在交互式终端中,submit 会在单个屏幕中打开一个支持鼠标和键盘操作的全屏编辑器。默认包含所有尚无 PR 的分支——可在左侧面板中取消选择不需要的分支(Ctrl+X)。由于每个 PR 都基于其下方的分支,取消选择某个分支也会取消选择堆叠在其上方的分支;重新包含某个分支,则会重新包含其下方的分支。在右侧起草每个 PR 的标题和描述(支持 Markdown 预览以及跳转到 $EDITOR),并选择设为可供审查或草稿,然后按 Ctrl+S 一次性提交所有 PR。传入 --auto(或在 CI 中运行)可跳过编辑器并使用自动生成的标题。
如果这些分支已经有打开的 PR,但 GitHub 上尚不存在堆栈,你可以按 Ctrl+B 将这些 PR 关联成一个堆栈。
在编辑器中,新 PR 默认设为可供审查;可以通过“可供审查 ↔ 草稿”切换项将任意 PR 改为草稿。使用 --auto 时,新 PR 默认创建为草稿,除非传入 --open。
gh stack submit
gh stack submit --auto
gh stack submit --open
在 GitHub 上将 PR 关联成堆栈,而不在本地进行跟踪。
gh stack link [flags] <branch-or-pr> <branch-or-pr> [...]
根据分支名称、PR 编号或 URL,在 GitHub 上创建或更新堆栈。此命令不会存储或修改任何 gh stack 本地跟踪状态。它适用于在本地使用其他工具管理分支(例如 jj、Sapling、git-town),并且只想创建一个 PR 堆栈的用户。
参数按堆栈顺序提供(从底部到顶部)。创建或查找 PR 之前,分支参数指定的分支会自动推送到远程。对于已经有打开 PR 的分支,将使用现有 PR;对于尚无 PR 的分支,则会自动创建新 PR,并使用正确的基准分支形成链式关系。如果现有 PR 的基准分支与预期链条不符,系统会自动修正。
如果这些 PR 尚未处于某个堆栈中,则创建一个新堆栈。如果其中部分 PR 已经处于某个堆栈中,则更新现有堆栈,将新 PR 包含进来。现有 PR 永远不会从堆栈中移除——更新只会追加内容。
# Link branches into a stack (pushes branches, creates PRs, creates stack)
gh stack link feature-auth feature-api feature-ui
# Link existing PRs by number
gh stack link 10 20 30
# Link existing PRs by URL
gh stack link https://github.com/owner/repo/pull/10 https://github.com/owner/repo/pull/20
# Add branches to an existing stack of PRs
gh stack link 42 43 feature-auth feature-ui
# Use a different base branch and mark PRs as ready for review
gh stack link --base develop --open feat-a feat-b feat-c
一次合并一个或多个堆叠式 PR。
gh stack merge [<stack-number> | <pr-number>]
堆栈中截至并包含你所选拉取请求的所有成员,都会通过一次“全有或全无”的操作合并到基准分支中:只要有任何一个 PR 无法合并,就不会合并任何 PR。
不传参数时,将使用当前活动的本地堆栈。传入堆栈编号,可合并一个当前未检出的堆栈(纯远程操作);传入拉取请求编号,则可直接合并截至该 PR 的所有内容。
在交互式终端中,一个简短的向导会引导你选择要合并的 PR、选择合并方式并进行确认。在非交互式终端中,或使用 --yes 时,会在不提示的情况下合并整个堆栈(或截至指定 PR 的所有内容);除非明确指定合并方式,否则将使用上次使用的合并方式。
合并前只检查拉取请求的基本状态(处于打开状态且不是草稿);执行合并时,由 GitHub 检查分支保护和仓库规则,因此任何相关失败都会返回给你。堆叠式 PR 合并不支持绕过合并要求。
如果基准分支使用合并队列,堆栈会被加入队列,而不是直接合并。合并方式由队列决定,因此向导会跳过合并方式步骤;任何 --merge-method(或 --squash、--rebase、--merge)标志都会被忽略并显示警告。选中的拉取请求会被一起加入队列,但会随着队列处理而分别合并——它们可能会分成不同批次落入基准分支,而不是一次性全部合并。
# Merge the current stack (interactive picker)
gh stack merge
# Merge a stack you don't have checked out, by stack number
gh stack merge 7
# Merge everything up to and including PR #42
gh stack merge 42
# Merge the whole current stack without prompting, squashing
gh stack merge --yes --squash
查看当前堆栈。
gh stack view [flags]
显示堆栈中的所有分支、它们的顺序、PR 链接,以及带相对时间戳的最近一次提交。输出会通过分页器显示(遵循 GIT_PAGER、PAGER 的设置,默认使用 less -R)。
gh stack view
gh stack view --short
gh stack view --json
从本地跟踪中移除栈,并在 GitHub 上取消堆积。也可作为 gh stack delete 使用。
gh stack unstack [<stack-number>] [flags]
不提供参数时,命令针对活跃栈(包含当前检出分支的栈)进行操作——在 GitHub 上取消堆积并移除本地跟踪。
提供栈号(在 github.com 栈 UI 中显示的标识符)以在 GitHub 上取消特定栈的堆积。这在仓库中的任何位置都可以工作,无论该栈是否在本地检出——栈号直接通过 GitHub API 取消堆积。如果该栈也在本地跟踪,其本地跟踪也会被移除。
使用 --local 仅移除本地跟踪,无需联系 GitHub。
GitHub 决定哪些拉取请求可以取消堆积:排队待合并或启用自动合并的 PR 保持堆积状态。当某些拉取请求仍处于堆积状态时,栈会被保留(本地跟踪如有,保持不变)。
# 从本地跟踪和 GitHub 移除当前栈
gh stack unstack
# 按栈号取消特定栈的堆积
gh stack unstack 7
# 仅移除本地跟踪
gh stack unstack --local
在当前栈中的分支之间移动,无需记住分支名称。
gh stack up [n] # 向上移动 n 个分支(默认 1)
gh stack down [n] # 向下移动 n 个分支(默认 1)
gh stack top # 跳至栈顶
gh stack bottom # 跳至栈底
gh stack trunk # 跳至主干分支
gh stack switch # 交互式选择分支进行切换
导航命令限制在栈的边界内——从顶部向上或从底部向下是无操作的,并显示消息。如果你在主干分支上,向上移动到栈的第一个分支。
gh stack up # 向上移动一层
gh stack up 3 # 向上移动三层
gh stack down
gh stack top
gh stack bottom
gh stack trunk # 跳至主干分支(例如 main)
gh stack switch # 显示交互式选择器
分享关于 gh-stack 的反馈。
gh stack feedback [title]
在 gh-stack 仓库中打开 GitHub 讨论以提交反馈。可选择为讨论帖子提供标题。
gh stack feedback
gh stack feedback "Support for reordering branches"
创建一个简短的命令别名,以减少输入。
gh stack alias [flags] [name]
将一个小的包装脚本安装到 ~/.local/bin/,将所有参数转发给 gh stack。默认别名名称是 gs,但你可以通过作为参数传递来选择任何名称。设置后,你可以运行 gs push 而不是 gh stack push。
在 Windows 上,不支持自动别名创建——命令打印创建批处理文件或 PowerShell 函数的手动说明。
# 创建默认别名(gs)
gh stack alias
# → 现在 "gs push"、"gs view" 等都可以工作
# 创建自定义别名
gh stack alias gst
# 移除别名
gh stack alias --remove
gh stack alias gst --remove
# 1. 启动栈(创建并检出第一个分支)
gh stack init
# 2. 在第一层进行工作
# ... 编写代码、提交 ...
# 3. 添加下一层
gh stack add api-routes
# ... 编写代码、提交 ...
# 4. 推送所有内容并创建堆积 PR
gh stack submit
# 5. 审查者请求对第一个 PR 进行更改
gh stack bottom
# ... 进行更改、提交 ...
# 6. 将栈的其余部分变基到你的修复上
gh stack rebase
# 7. 推送更新的分支
gh stack push
# 8. 当第一个 PR 被合并时,同步栈
gh stack sync
# → 提示修剪已合并的分支(或使用 --prune 自动修剪并避免提示)
如果要最小化按键次数,使用 -Am 标志将暂存、提交和分支创建折叠到单个命令中。当你不提供分支名称时,会从提交消息中自动生成一个,格式为日期+slug(例如,03-24-auth_middleware)。
当分支还没有提交时(例如,刚初始化后),add -Am 会直接在该分支上暂存和提交,而不创建新分支。一旦分支有了提交,add -Am 会创建一个新分支,检出它,并在那里提交。
# 1. 启动栈
gh stack init auth
# → 创建 auth 并检出它
# 2. 为第一层编写代码
# ... 编写代码 ...
# 3. 在当前分支上暂存和提交
gh stack add -Am "Auth middleware"
# → auth 没有提交,所以提交落在这里
# (不创建新分支)
# 4. 为下一层编写代码
# ... 编写代码 ...
# 5. 创建下一个分支并提交
gh stack add -Am "API routes"
# → auth 已有提交,所以从提交消息创建新分支,
# 检出它,并在那里提交
# 6. 继续进行
# ... 编写代码 ...
gh stack add -Am "Frontend components"
# → 创建另一个分支并在那里提交
与典型的工作流相比,无需命名分支、单独运行 git add 或 git commit。每个 gh stack add -Am "..." 都完成所有工作。任何时候想控制它时,传递一个明确的分支名称:gh stack add -Am "API routes" api-routes。
交互式屏幕(提交、修改和查看)和所有彩色命令输出(状态消息、提示)自动适应终端的背景,使其在深色和浅色主题上都可读。背景从终端检测;如果终端不报告它(某些 SSH 或 tmux 设置),则使用深色调色板。
如果检测出错,设置 GH_STACK_THEME 以强制调色板:
# 强制使用浅色调色板
export GH_STACK_THEME=light && gh stack view
本项目根据 MIT 开源许可证条款许可。请参考 LICENSE 文件了解完整条款。