@sameenkarim:gh stack CLI 现已支持 git worktree,你可以管理每个分支位于独立 worktree 中的堆叠 PR,同时仍能……

X AI KOLs Following 工具

摘要

GitHub 的 gh stack CLI 扩展(v0.2.0)现已支持 git worktree,让开发者可以管理每个分支位于独立 worktree 中的堆叠 PR,同时仍可使用级联堆叠 rebase 等功能。此次发布还介绍了一个更新提醒器,以及一个可安装的 AI agent skill,用于处理堆叠分支。

gh stack CLI 现已支持 git worktree 🥞 你可以管理每个分支位于独立 worktree 中的堆叠 PR,同时仍能利用级联堆叠 rebase 等功能 https://github.com/github/gh-stack/releases/tag/v0.2.0…
查看原文
查看缓存全文

缓存时间: 2026/10/04 19:15

gh stack CLI 现在支持 git worktrees 🥞 你可以管理多个 stack,其中每个分支位于独立的 worktree 中,同时仍然可以享受级联 stack rebase 等便利功能 https://github.com/github/gh-stack/releases/tag/v0.2.0


github/gh-stack

来源:https://github.com/github/gh-stack

GitHub Stacked PRs

一个用于管理堆叠分支(stacked branches)和拉取请求(pull requests)的 GitHub CLI 扩展。

Stacked PR 会将大型改动拆分为一系列小而可审查的 PR,它们彼此层层递进。gh stack 自动化了其中繁琐的部分——创建分支、保持分支 rebase 更新、设置正确的 PR base 分支,以及在各层之间导航。

安装

gh extension install github/gh-stack

需要 GitHub CLI(https://cli.github.com/)(gh)v2.0+ 以及 Git 2.36+。

更新

gh extension upgrade stack

官方、未固定版本的稳定发布安装会在后台检查更新的最新版本(https://github.com/github/gh-stack/releases/latest),成功的检查结果会缓存 24 小时。当有可用更新时,命令可以在成功或操作失败之后,将升级提示附加到 stderr 中,同样最多每 24 小时一次。失败时,原始错误会先显示,退出码保持不变。这同样适用于非交互式使用;stdout 和 JSON 输出不会改变。

检查和提醒的时间戳按用户在所有仓库之间共享。它们以 YAML 格式存储在 GitHub CLI 状态目录(macOS/Linux 上默认为 ~/.local/state/gh)下的 gh-stack/state.yml 中。命令绝不会为发布检查而等待。较短的命令可能在提示准备好之前就已经结束。失败或被中断的尝试可以在 15 分钟冷却期后重试,而不必等待一整天。已完成的检查会被缓存,因此后续命令可以显示提示。不会发生自动升级,且开发版、本地链接安装、预发布版和固定版本安装均被排除在外。帮助、版本和补全命令不会运行通知器。用法错误和用户明确取消的操作不会显示升级提示。设置 GH_STACK_NO_UPDATE_NOTIFIER=1 可禁用这些检查和提示,包括在 CI 或脚本中。任何非空值都会禁用通知器。可选的检查失败不会影响命令退出码;设置 GH_DEBUG=1 可获取诊断信息。安装包含此功能的版本,才能收到后续版本的升级提示。

AI 代理集成

安装 gh-stack skill,让您的 AI 编码代理了解如何使用 stacked PR 和 gh stack CLI:

gh skill install github/gh-stack

快速开始

# 开始一个新的 stack(创建并检出第一个分支)
gh stack init

# …… 在第一个分支上提交代码……

# 在其上方再添加一个分支
gh stack add api-endpoints

# …… 提交代码……

# 推送所有分支
gh stack push

# 查看 stack
gh stack view

# 提交一组 PR
gh stack submit

工作原理

Stack 是一个有序的分支列表,其中每个分支都构建在其下方的分支之上。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)

Stack 的底部是离主干最近的分支,顶部是离主干最远的分支。每个分支都继承自其下方的分支。导航命令(up、down、top、bottom)遵循这一模型:up 远离主干移动,down 朝主干移动。

当您提交时,gh stack 会为每个分支创建一个 PR,并在 GitHub 上将它们链接为一个 Stack。每个 PR 的 base 都设置为 stack 中位于其下方的分支,因此审查者只会看到该层的 diff。

本地跟踪

Stack 元数据存储在 /gh-stack(一个 JSON 文件,不会提交到仓库中),其中 `` 是 Git 的公共目录。在普通 clone 中,这位于 .git/gh-stack。所有链接的 worktree 共享此目录,包括 stack 归属关系、顺序和 PR 元数据。

gh-stack 的恢复日志 gh-stack-rebase-state 和 gh-stack-modify-state 也位于公共目录中,并记录涉及的 worktree。Git 自身的 HEAD、索引、rebase 和 cherry-pick 标记仍然按 worktree 分开。

升级时,不冲突的旧版 worktree 目录会被自动整合,原始文件会保留为备份。存在冲突的定义会中止迁移,而不是任选其一;错误信息会指出需要协调的文件。请先在原始 worktree 中完成或中止遗留的进行中操作。不要在同一个 clone 中混用新旧版本的 gh-stack。在切换预览阶段或 gh-stack 版本之前,请先完成已暂停的操作。如果恢复流程报告执行生命周期不兼容,请使用与之匹配的构建版本在其记录的原始 worktree 中完成或中止该操作;请勿编辑或删除日志文件。

Git worktrees

您可以在链接的 worktree 中维护相互独立的 stack,也可以将一个 stack 的分支分散到不同的 worktree 中。rebase 和 sync 会自动更新拥有每个受影响分支的干净 worktree。处于脏状态、繁忙、缺失或所有者已变更的 worktree 会中止操作;无关的 worktree 不会被改动。无法安全快进的主干可以使用现有的远程 fetch 回退方案。

变更操作在整个 clone 范围内串行执行,而只读视图仍可正常使用。已暂停的操作必须先继续或中止,才能执行另一次变更操作。即使在其他地方调用了 --continue 或 --abort,恢复流程仍会在记录的 worktree 中运行。gh-stack 绝不会自动 stash 变更、创建/删除 worktree 或抢占其他检出。变更锁只用于协调 gh-stack 进程,不涉及任意 Git 命令、编辑器或其他工具。在重写历史期间,请保持受影响的 worktree 处于空闲状态。

在暂停期间,只需在报告的 worktree 中进行所要求的冲突解决编辑和暂存操作。导航到已被其他 worktree 检出的分支时,会报告其路径并在不切换的情况下失败。如果希望改为获取该路径,请为 up、down、top、bottom、trunk 或带显式目标的 checkout 添加 --print-path。未被占用的目标会先在此处检出,然后打印本 worktree 的路径;成功的 stdout 只包含绝对路径和一个换行符。

请参阅worktree 工作流,其中提供了一个 shell 包装脚本,可在切换目录之前检查错误。

modify 支持分散在多个 worktree 中的 stack。它会预检暂存动作和级联操作所需的所有者,在每个分支所有者的 worktree 中进行重命名,并在接收方所有者的 worktree 中执行 cherry-pick 或 rebase。未被占用的分支使用发起操作的 worktree;其他 worktree 绝不会被切换到不同的分支。被丢弃/折叠的分支及其 worktree 保持完整。如果原始分支最近的存活分支归其他所有者所有,modify 会保持保留的原始分支处于检出状态,并报告存活分支的路径。主干只被读取。

使用 git init --separate-git-dir 创建的仓库支持主 worktree 调用,以及通过现有的绝对或相对 core.worktree 反向链接进行主所有者发现,包括存储在主 config.worktree 中的链接。唯一的发现限制是没有主 worktree 反向链接的链接 worktree 调用:需要该主所有者的操作会失败并给出可操作的指导建议,而不受影响的 worktree 则继续执行。管理目录永远不会被当作检出目标。

命令

gh stack init

在当前仓库中初始化一个新的 stack。

gh stack init [flags] [branches...]

在本地初始化一个新的 stack。在交互模式下(不带参数),会提示输入分支名称,并可选择使用当前分支作为第一层。当给出显式分支名称时,已存在的分支会被自动采纳,缺失的分支会被创建。主干默认为仓库的默认分支,除非使用 --base 覆盖。已被其他 worktree 检出的现有分支可以在此处未检出的情况下被采纳。如果最终分支已被其他地方占用,init 会报告其所有者,并且您当前的检出保持不变。

自动启用 git rerere,以便在多次 rebase 之间记住冲突解决方案。

标志说明
-b, --base <branch>stack 的主干分支(默认为仓库的默认分支)

示例:

# 交互式 —— 提示输入分支名称
gh stack init

# 非交互式 —— 提前指定分支
gh stack init feature-auth feature-api feature-ui

# 使用不同的主干分支
gh stack init --base develop feature-auth

# 将现有分支纳入 stack
gh stack init feature-auth feature-api

gh stack add

在当前 stack 顶部添加一个新分支。

gh stack add [flags] [branch]

对于现有 stack,会在当前 HEAD 处创建一个新分支,将其添加到 stack 顶部并检出。必须在 stack 的最顶部分支上运行。如果没有给出分支名称,会提示输入。已被其他 worktree 检出的现有分支同样可以被采纳,无需切换任何检出。提交/暂存快捷方式不能用于采纳由其他 worktree 拥有的分支,会在暂存或更改 stack 归属关系之前失败。

从不属于 stack 的分支上交互式运行时,add 会提议改为初始化一个新的 stack。提供或自动生成的分支名称将成为第一层;如果没有提供,则使用标准的 init 提示流程。

您可以选择在 add 流程中暂存变更并创建提交。当提供 -m 而未给出显式分支名称时,分支名称会以 日期+slug 格式自动生成(例如 03-24-add_login)。

标志说明
-A, --all暂存所有变更(包括未跟踪的文件);需要 -m
-u, --update仅暂存已跟踪文件的变更;需要 -m
-m, --message <msg>在创建分支之前,使用此消息创建一个提交

注意: -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

gh stack checkout

通过 stack 编号、拉取请求编号、PR URL 或分支名称检出一个 stack。

gh stack checkout [<stack-number> | <pr-number> | <url> | <branch-name>]

纯数字会首先被解释为 stack 编号或 PR 编号(GitHub UI 中显示的仓库内标识符)。如果没有匹配该数字的内容,则会将其作为分支名称尝试。

当引用远程 stack 时,命令会从 GitHub 拉取 stack,拉取各分支,并在本地建立该 stack。如果该 stack 已存在于本地且内容匹配,则切换到对应分支。如果本地和远程 stack 的构成不同,系统会提示您解决冲突。

当提供分支名称时,命令会先检查本地跟踪的 stack。如果该分支没有被本地跟踪,命令会在远程 stack 中查找该分支,并拉取匹配的 stack。如果有多个 stack 匹配,请使用 stack 编号或 PR 编号显式选择。

在交互式终端中不带参数运行时,首先会检查当前分支是否属于远程上未被本地跟踪的某个 stack,并提议检出它。如果没有唯一匹配项或您选择拒绝,则会打开一个可搜索的选择器,列出您可访问的每个 stack——包括本地跟踪的 stack 和仅存在于 GitHub 上的 stack。每一行会显示 stack 编号、其底部和顶部分支、base 分支、一个状态栏(汇总其 PR 中已合并、开放、关闭或尚未推送的数量),以及该 stack 是本地可用还是仅在远程。可以使用 All / Local / Remote 标签页进行筛选,或输入 / 进行搜索;已完全合并的 stack 会被省略。选择仅存在于远程的 stack 时,会先将其克隆到本地,然后再切换过去。

示例:

# 通过 stack 编号检出 stack
gh stack checkout 7

# 通过 PR 编号检出 stack
gh stack checkout 42

# 通过 PR URL 检出 stack
gh stack checkout https://github.com/owner/repo/pull/42

# 通过分支名称检出 stack
gh stack checkout feature-auth

# 交互式 —— 从所有可用 stack(本地和远程)中选择
gh stack checkout

gh stack rebase

从远程拉取更新,并在整个 stack 上执行级联 rebase。

gh stack rebase [flags] [branch]

从 origin 获取最新的变更,然后确保 stack 中每个分支的提交历史都包含前一层的顶端提交。按从主干向上的顺序对分支进行 rebase。如果某个分支的 PR 已经被合并,rebase 会自动切换到 --onto 模式,以便将提交正确重放到合并目标之上。

如果发生 rebase 冲突,操作会暂停,并打印出包含行号的冲突文件。解决冲突后,使用 git add 暂存,然后用 --continue 继续。要撤销整个 rebase,请使用 --abort 将所有分支恢复到 rebase 之前的状态。

标志说明
--downstack仅 rebase 从主干到当前分支的分支
--upstack仅 rebase 从当前分支到顶部的分支
--no-trunk跳过主干——仅在 stack 分支之间相互 rebase(不 fetch,不 rebase 主干)
--continue解决冲突后继续 rebase
--abort中止 rebase,并将所有分支恢复到 rebase 之前的状态
--remote <name>从哪个远程 fetch(默认为自动检测到的远程)
--committer-date-is-author-date在 rebase 期间将提交者日期设置为作者日期。别名:--preserve-dates

保留日期的 rebase 使用 Git 的 merge 后端,因此该日期设置能够在冲突后保留。使用 gh stack rebase --continue 恢复;继续操作使用 Git 保存的设置,而不会重复执行仅在启动时生效的选项。

参数说明
[branch]目标分支(默认为当前分支)

示例:

# rebase 整个 stack
gh stack rebase

# 仅 rebase 当前分支下方的分支
gh stack rebase --downstack

# 仅 rebase 当前分支上方的分支
gh stack rebase --upstack

# 在不从主干拉取、也不对主干 rebase 的情况下 rebase stack 分支
gh stack rebase --no-trunk

# 解决冲突后继续
gh stack rebase --continue

# 中止 rebase 并恢复一切
gh stack rebase --abort

# rebase 并将提交者日期保留为作者日期
gh stack rebase --committer-date-is-author-date

gh stack modify

交互式地重构当前 stack。

gh stack modify [flags]

打开一个终端 UI 来重构 stack。您可以删除、折叠、插入、重命名和重新排列分支。所有变更都在预览期间暂存,并在保存时一次性应用。如果 PR stack 已在 GitHub 上创建,请在之后运行 gh stack submit,以推送变更并重建 stack。

标志说明
--continue解决冲突后继续
--abort中止 modify 会话,并将 stack 恢复到 modify 之前的状态

操作:

  • 删除(Drop,x):从 stack 中移除一个分支及其提交。本地分支和相关 PR 会被保留。
  • 向下折叠(Fold down,d):将某分支的提交吸收到其下方的分支中(朝主干方向)。被折叠的分支将从 stack 中移除。
  • 向上折叠(Fold up,u):将某分支的提交吸收到其上方的分支中(远离主干方向)。被折叠的分支将从 stack 中移除。
  • 插入(Insert):在 stack 中的某个位置插入一个新分支。

相似文章