SuperAPI AI 工具集

stack-merge

部署与运维

查看项目

更新于 10-22 10:57

技能介绍

用途 stack-merge 用于把整条 Graphite downstack(从目标分支一路到 main 的 PR 链)一次性落入 main:以管理员绕过分支保护的快进推送完成合并,再显式关闭所有在范围内的 PR。它替代 Graphite 的合并队列:产生与队列相同的结果(线性历史、所有 PR 合并),但不必等待队列。全程不做逐 PR 的 CI 门禁,不在 main 上产生合并提交,也不对每个 PR 执行 gh pr merge。

前提条件(开始时验证,不满足即中止)

  • main 分支具备管理员/绕过权限:用 gh api repos/rivet-dev/rivet/branches/main/protection 检查用户是否在 bypass_pull_request_allowances.users[] 中或拥有仓库管理员身份。
  • 已初始化 Graphite:gt ls 可成功执行。
  • 工作区干净:git status --short 为空;若有进行中的 rebase/merge 需先解决。
  • worktree 状态正常:git worktree list 并用 git worktree prune 清理可修剪项;陈旧 worktree 会导致 rebase 报 "already checked out at" 错误。

不适用场景

  • 用户只想普通地合并单个 PR:应使用 gh pr merge。
  • 用户不是仓库管理员:推送会被拒绝,应改用 Graphite 队列。
  • 堆叠顶部 CI 失败且用户在意:队列会拒绝,此时也应暂停。

执行流程(8 个阶段,每阶段含校验门或确认门,仅在用户明确批准最终命令后才写入)

  1. 范围:根据目标分支名,沿 gh pr view <branch> --json baseRefName 从目标逐级回溯到 main,得到第 8 阶段要关闭的有序 PR 列表。不要依据 gt ls 的视觉顺序推断父级,应使用每个 PR 的 baseRefName。脚本:scripts/list_merge_path.sh <target-branch>。
  2. 同步:执行 gt sync --no-interactive,拉取最新 main 并清理已合并的上游分支;检查合并路径中是否出现 (needs restack) 标记。
  3. 解冻:列出合并路径中的冻结分支(gt ls | grep frozen)。gt sync 会静默跳过冻结分支,若目标与 main 之间存在冻结分支,链条会一直锚定在旧 main SHA。对每个分支执行 gt unfreeze <branch>(仅改元数据,不重写);执行前向用户展示完整列表并确认。
  4. 下栈 restack:从目标分支执行 gt restack --downstack,自 main 向上逐级 rebase 到目标。遇冲突交给用户处理(解决后 gt continue),不代为决定冲突取舍。完成后验证 git merge-base --is-ancestor origin/main <target> 必须为 YES;若为 NO 说明期间 main 变动,需再次 gt sync 并循环。
  5. 校验门(只读):运行 scripts/validate.sh <target-branch>,检查:FF 安全性(git merge-base --is-ancestor origin/main origin/<target> 必须为 YES);是否存在 main 上而不在目标中的提交(git rev-list --count origin/<target>..origin/main 必须为 0,否则快进推送会删除 main 上的提交);合并路径每个分支的本地与远程差异(restack 会重写 SHA,远程需更新);提交整洁度(信息性、不阻断):Ralph 风格提交(模式 ^[a-z]+((.+?))?!?:\s*[?US-\d+]?\s*-\s,见 scripts/detect_ralph.sh)、相对 baseRefName 未压缩的分支(>1 个提交);以及在临时 worktree 中用 git merge origin/main --no-commit --no-ff 做冲突预览,若冲突则回到第 4 阶段。任一检查失败即停止并呈现结果。
  6. 确认门:任何写入前向用户展示 main 推送前后 SHA、将落入的提交数、第 8 阶段将关闭的 PR 列表(合并路径)、将被强制推送的分支列表;要求用户明确回复 "yes",不允许沉默继续。
  7. 推送(两次写入,按序):先批量强制推送全部合并路径分支,使各分支远程头与其 restack 后本地 SHA 一致(若 restack 未重写则为无害空操作),使用 git push origin --force-with-lease <branch1> <branch2> ... <target>,单事务多 refspec 比逐个推送更快,必须用 --force-with-lease,绝不用 --force。随后将 main 快进推至目标顶端:git push origin origin/<target>:main,推送输出中会标注绕过规则("Bypassed rule violations for refs/heads/main")。
  8. 关闭 PR 与清理:并行关闭所有 head 已可从 main 到达的开放 PR(既覆盖范围内 PR,也覆盖合并路径之下同样落入的 PR):先取 main SHA,列出开放 PR 的 number 与 headRefOid,用 git merge-base --is-ancestor 判断后逐个 gh pr close 并附说明注释;base 为 main 的 PR 可能已自动合并,此时 gh pr close 会提示已关闭,属正常。之后 gt sync 清理本地已合并分支。最终报告 main SHA、关闭的 PR 数、仍 OPEN 的 PR(应无)。

为何要显式关闭:GitHub 仅在 head ⊆ PR 的 base 分支时才自动以 MERGED 关闭;堆叠 PR 的 base 多为同级分支而非 main,故不会触发该检查,其较慢的后台扫描最终可能以 CLOSED(而非 MERGED)关闭,且不可靠、可能耗时 5 分钟以上,因此自行关闭。

使用方式与脚本

  • scripts/list_merge_path.sh <target>:按 baseRefName 遍历枚举范围内 PR。
  • scripts/validate.sh <target>:执行第 5 阶段全部只读检查。
  • scripts/detect_ralph.sh <target>:标记合并路径中的 Ralph 风格提交。
  • scripts/exec_merge.sh <target> --confirm:批量强制推送 + 快进推送(第 7 阶段),需显式 --confirm 才执行。 参考文档:references/gotchas.md(设计过程中的所有陷阱,写任何推送命令前先读)、references/phases.md(各阶段细节、示例输出、决策树)。

核心规则

  • 绝不跳过 FF 安全检查:向 main 推送前 git merge-base --is-ancestor origin/main origin/<target> 必须为 YES,否则会删除 main 上的提交。
  • gt ls 的视觉顺序不是 PR 父级顺序,始终使用 gh pr view <N> --json baseRefName;baseRefName 可以是真实分支名或 graphite-base/<pr#>,两者均有效。
  • gt sync 会静默跳过冻结分支;冻结即退出 restack,目标与 main 之间的任何冻结分支都会破坏合并路径。
  • 所有强制推送使用 --force-with-lease;远程已变动时安全中止,--force 会盲目覆盖。
  • gt restack 遇冲突交给用户,不替其选择,用户解决后运行 gt continue。
  • 第 8 阶段显式关闭 PR,不等待自动关闭。
  • 第 8 阶段关闭所有可从 main 到达的 PR,而不仅是 list_merge_path.sh 的输出;该脚本会在 graphite-base/<N> 边界停止,可能遗漏其下方 PR。

限制

  • 依赖 main 的管理员/绕过权限,非管理员推送会被拒绝。
  • 本资料未提供收费、价格、平台支持范围等信息。