关于 mergify-stack
mergify-stack 是使用 Mergify CLI 将 Git 分支组织为短小依赖栈的工作流技能,面向编码 Agent。它规定栈编辑与 PR 更新方式,并要求每个提交都能独立通过 CI。
主要功能
- 01创建并更新分支栈与 PR
- 02同步栈并查看提交与 PR 状态
- 03使用非交互式栈命令改写提交
- 04让 PR 内容与提交信息保持一致
- 05要求提交可独立通过 CI,PR 保持草稿
适用场景
- 01将无关工作拆分到不同分支
- 02合并后同步现有栈
- 03在 Agent 会话中修改或重排提交
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mergifyio/ mergify-cli --skill mergify-stack安装后在 Claude Code 中输入/mergify-stack确认已加载。
详细介绍
原文在翻译后有更新,译文可能不是最新。
Mergify 栈工作流
栈的理念
一个分支就是一个栈。保持栈精简、专注:
- 一个栈应只包含彼此依赖的提交
- 原因:栈越长,合并所需的时间越长
主动管理栈:
- 如果现有栈可以拆分为彼此独立的栈,请主动提出
- 收到新任务时,如果可以在单独的分支上完成,就这样做;不确定时请先询问
- 对于无关的更改,默认创建新分支
核心约定
- 推送:使用
mergify stack push(绝不使用git push) - 修复:使用
git commit --amend(绝不通过创建新提交来修复问题) - 修订说明:修订已有 PR 的提交(即已推送的提交)时,请在
mergify stack push之前附加一条mergify stack note,记录修订提交的原因。该说明会显示在 PR 的“Revision history”评论和 JSON 标记中,让审阅者无需比较差异即可了解原因。 - 修复堆栈中间的提交:先暂存所有本地更改(
git stash -u),然后使用mergify stack edit <SHA-or-Change-Id-prefix>在目标提交处暂停变基。使用git commit --amend修订该提交,然后运行git rebase --continue,接着运行mergify stack push,最后运行git stash pop。此操作为非交互式——绝不要为此使用git rebase -i。(不带参数调用mergify stack edit会回退到完全交互式的git rebase -i,在 Agent 环境中会卡住——务必传入提交前缀。) - 重新排序:先暂存所有本地更改(
git stash -u),然后使用mergify stack reorder(按所需顺序列出所有提交)或mergify stack move(移动单个提交),而不是手动运行git rebase -i——此操作为非交互式,并可避免GIT_SEQUENCE_EDITOR引号问题 - 修正提交:先暂存所有本地更改(
git stash -u),然后使用mergify stack fixup <SHA>...将提交合并到其父提交中(会丢弃所列提交的消息)。此操作为非交互式——绝不要为此使用git rebase -i。 - 压缩提交:先暂存所有本地更改(
git stash -u),然后使用mergify stack squash SRC... into TARGET [-m "msg"]将多个提交合并为一个,并可选择自定义消息。此操作为非交互式——绝不要为此使用git rebase -i。 - 重写提交说明:先暂存所有本地更改(
git stash -u),然后使用mergify stack reword <SHA> -m "new message"就地修改提交说明。指定-m时,此操作为非交互式——绝不要为此使用git rebase -i。 - 丢弃提交:先暂存所有本地更改(
git stash -u),然后使用mergify stack drop <SHA>...从堆栈中移除提交。此操作为非交互式——绝不要为此使用git rebase -i。 - 提交标题:遵循 Conventional Commits 规范(例如:
feat:、fix:、docs:) - PR 标题和正文:
mergify stack会将提交说明的标题复制为 PR 标题,并将提交说明的正文复制为 PR 正文——因此,撰写提交说明时应将其视为 PR 描述。所有应出现在 PR 中的内容(工单引用、背景信息、测试计划)都必须写入提交说明。 - 工单引用:在提交说明正文中加入工单/问题引用(例如:
MRGFY-1234、Fixes #123),不要另外添加到 PR 中。 - PR 生命周期完全由
mergify stack管理:绝不要使用gh pr edit或 GitHub MCP 修改 PR 标题、正文或标签——这些更改会在下次推送时被覆盖。绝不要手动关闭或合并 PR——mergify stack会处理整个 PR 生命周期(创建、更新和清理)。 - 草稿 PR:绝不要将 PR 标记为可供审阅——所有 PR 都应保持为草稿。用户审阅后会手动将其移出草稿状态。
- 每个提交都必须单独通过 CI:堆栈中的每个提交都会成为一个 PR。每个 PR 都会单独运行 CI,因此每个提交都必须完整自洽——无需依赖堆栈中的后续提交,就能独立编译、通过代码检查和测试。需要格式化或代码检查修复时,必须将其包含在引入问题的提交中,不得推迟到后续提交。
常见错误
| 错误做法 | 正确做法 | 原因 |
|---|---|---|
git push | mergify stack push | Git push 会绕过堆栈管理,破坏 PR 之间的关系 |
| 新建提交来修复 lint/拼写错误 | git commit --amend(HEAD)或 git commit --fixup <SHA> + git rebase --autosquash(堆栈中间的提交) | 每个提交对应一个 PR;修复提交会产生不需要的额外 PR |
gh pr edit --title "..." | 编辑提交消息,然后运行 mergify stack push | 每次 push 时,PR 标题和正文都会从提交消息覆盖 |
gh pr merge 或 gh pr close | PR 生命周期完全由工具管理——无需操作 | PR 生命周期完全由堆栈工具管理 |
在 main 上运行 git commit | 先运行 mergify stack new <name> | 在默认分支上运行 mergify stack push 会失败 |
使用 git rebase -i 修复提交 | mergify stack fixup <SHA> | 无需交互——可在 LLM/Agent 会话中使用;不会启动编辑器 |
使用 git rebase -i 合并提交 | mergify stack squash A B into X [-m "..."] | 无需交互——可在 LLM/Agent 会话中使用;不会启动编辑器 |
使用 git rebase -i 更改提交消息 | mergify stack reword <SHA> -m "..." | 无需交互——可在 LLM/Agent 会话中使用;不会启动编辑器 |
使用 git rebase -i 修改堆栈中间的提交 | mergify stack edit <SHA-or-Change-Id-prefix>,然后运行 git commit --amend,再运行 git rebase --continue | 无需交互——会在目标提交处暂停变基,不会启动编辑器 |
使用 git rebase -i 删除提交 | mergify stack drop <SHA>... | 无需交互——可在 LLM/Agent 会话中使用;不会启动编辑器 |
GIT_SEQUENCE_EDITOR='sed -i ...' git rebase -i(任何变体) | 使用 mergify stack {edit,fixup,squash,reorder,move} 中的一种命令 | 手写的序列编辑器脚本很脆弱;每种常见的改写操作都有现成的非交互命令 |
| 将 lint 修复留到之后的提交中 | 在导致问题的提交中包含修复 | 每个提交都会独立运行 CI;后续提交无法挽救之前的提交 |
| 在工作区有未提交更改时变基/重新排序/检出/同步 | 先运行 git stash -u,之后再运行 git stash pop | 这些操作期间,未提交的更改可能丢失或引发冲突 |
| 在没有说明的情况下修改已推送的提交 | 在运行 mergify stack push 前,运行 mergify stack note -m "why" | 修改原因会记录在 PR 的 Revision history 表格和 JSON 标记中,评审者无需对比差异即可了解改动 |
命令
mergify stack new NAME # Create a new stack/branch for new work
mergify stack push # Push and create/update PRs (also registers as a GitHub-native stack by default)
mergify stack push --no-github-native # ...without registering it as a GitHub-native stack
mergify stack checkout BRANCH # Checkout an existing stack from GitHub (e.g. someone else's)
mergify stack checkout PR_URL # Same, from any PR in the stack — middle included
mergify stack sync # Fetch trunk, remove merged commits, rebase
mergify stack list # Show commit <-> PR mapping for current stack
mergify stack list --json # Same, but machine-readable JSON output
mergify stack reorder C A B # Reorder all commits (pass SHA or Change-Id prefixes)
mergify stack move X first # Move commit X to the top of the stack
mergify stack move X last # Move commit X to the bottom of the stack
mergify stack move X before Y # Move commit X before commit Y
mergify stack move X after Y # Move commit X after commit Y
mergify stack fixup X # Fold commit X into its parent (drops X's message)
mergify stack fixup X Y Z # Fold each into its parent (multi-fixup)
mergify stack squash X into Y # Reorder X adjacent to Y, fold X into Y (keeps Y's message)
mergify stack squash X Y into Z -m "msg" # Fold X Y into Z with a custom message
mergify stack reword X -m "msg" # Change commit X's message non-interactively
mergify stack reword X # Change commit X's message via $GIT_EDITOR (TTY only)
mergify stack edit X # Pause the rebase at X so you can `git commit --amend` it (X is required; no-arg form is interactive)
mergify stack drop X # Drop commit X from the stack
mergify stack drop X Y Z # Drop multiple commits in one rebase
mergify stack note -m "why" # Attach an amend reason to HEAD (shown in PR revision history)
mergify stack note <SHA-or-Change-Id-prefix> -m "why" # Attach to a specific commit in the stack
mergify stack note --append -m "more" # Append to an existing note
mergify stack note --remove # Remove the note from a commit
使用 mergify stack checkout 检出 GitHub 上已有的栈(例如同事的栈)。参数可以是栈的远程分支名称——无论使用什么前缀,不假设其中包含作者名称——也可以是栈中任意拉取请求的 URL,包括位于中间的拉取请求。该命令会获取所有堆叠的拉取请求、创建本地分支并设置跟踪关系。
本地分支默认使用栈分支名称,并移除你自己的栈分支前缀。因此,你从 feature/login 推送的栈检出后仍为 feature/login,之后运行 mergify stack push 会更新同一批拉取请求。对于不使用你前缀的栈(例如同事的栈),命令会使用最后一段名称并发出警告:你可以基于这些提交进行工作,但从该分支运行 mergify stack push 会创建一个单独的栈,而不是更新他们的拉取请求。使用 --branch 可覆盖名称。拉取请求 URL 自带 owner/repo,因此在这种情况下会忽略 --repository。
使用 mergify stack sync 将栈更新到最新状态。该命令会获取最新的主干分支,检测哪些拉取请求已合并,从本地分支中移除相应提交,并对剩余提交执行变基。在已有栈上开始新工作之前,请先运行此命令。
使用 mergify stack list 查看哪些提交已推送、它们对应哪些拉取请求,以及栈是否与远程保持同步。它还会显示每个拉取请求的 CI 状态、审核状态和合并冲突。使用 --verbose 查看详细的检查名称和审核者名称。需要以编程方式解析输出时,请使用 --json;其中包含完整的 CI 检查详情和审核数据。
GitHub 原生堆栈(默认启用)
mergify stack push 默认会通过 GitHub 自己的 Stacks API 注册堆栈,因此 GitHub 会将其渲染为堆栈。可以在单次调用时使用 --no-github-native 选择退出,也可以针对每个仓库运行 git config mergify-cli.stack-github-native false。
Change-Ids、分支布局和修订历史都不会改变,而且会静默降级:如果 API 不可用(较旧的 GitHub Enterprise、仓库不支持该功能),推送会报告 not registered on GitHub,并像以往一样成功。
需要了解三件事:
- 堆栈至少需要 2 个拉取请求。 GitHub 会拒绝只有 1 个 PR 的堆栈,因此只有一项更改的堆栈会保留为普通 PR;如果一个 2-PR 堆栈失去其中一个成员,它会被解散,而不是重新注册。
Depends-On:标头会被移除。 已注册的堆栈本身就表示两个拉取请求之间的依赖关系,因此 CLI 不再在 PR 描述中重复写入该标头。它取决于是否已注册,而非是否启用了该标志:当推送降级为not registered on GitHub时,会在同一次推送中重新写入标头,Mergify 也会继续对堆栈排序。无论哪种情况,都不会触碰提交消息——该标头只存在于渲染后的 PR 描述中。- 堆栈评论也会被移除。 GitHub 会在拉取请求页面上列出已注册堆栈的成员,因此 CLI 不再发布置顶的 “This pull request is part of a Mergify stack” 表格,并会删除较早的推送在每个未关闭成员上留下的表格。它与标头一样取决于是否已注册:推送降级时仍会发布并更新表格,因为没有其他地方会显示堆栈。无论哪种情况,修订历史评论都不会受到影响——GitHub 不会渲染类似内容。
- 注册会改变 PR 的合并方式。 堆栈处于已注册状态时,GitHub 会拒绝对其成员使用经典合并端点(
PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge→ 403)。这是 GitHub 的约定,不是我们的约定;这正是存在--no-github-native的原因。
推送仍然开销很小。刷新提交(amend、reword、force-push)不会影响注册,在顶部添加更改也会扩展同一个堆栈。只有当推送移动了某个拉取请求的基分支时——例如重新排序、移除成员或在中间插入更改——才会先解散注册,并在最后重新构建,因为 GitHub 会拒绝堆栈中的 PR 更改基分支。中断的推送只会使堆栈处于未注册状态,不会处于半注册状态。它也可能导致堆栈缺少 Depends-On: 标头,因为这些标头会在推送结束时恢复;重新运行 mergify stack push 即可处理这两种情况,因为每次推送都会重新渲染描述,并根据堆栈当前的结构重新计算 Depends-On: 标记——即使使用 --keep-pull-request-title-and-body 也是如此:描述会根据拉取请求自身的正文而非提交消息重新渲染,标记仍会被移除后重新附加,而不是沿用。
修改说明
mergify stack note 用于记录修改提交的原因。说明会随堆栈一起传递:
- 原因会根据提交 SHA 存储在本地的
refs/notes/mergify/stack下。 - 执行
mergify stack push时,原因会写入更改的修订历史;推送后的头部提交上的说明会替换为完整修订历史(人工摘要 + `` JSON 标记)。Git notes(而不是 PR 评论)是机器可读的事实来源;PR 的“Revision history”评论由它们生成。 - 合并时,Mergify 会将头部提交的历史说明复制到合并/压缩提交上,因此在基分支上运行
git log --notes=mergify/stack,即可查看每项更改为何被修订。
何时添加说明——任何时候,只要你修改或重写了一个已有 PR 的提交(即该提交至少推送过一次),就应添加说明。说明回答“此修订版为何不同?”,这样审阅者就不必对比新旧 SHA 来找出原因。
工作流——在执行 mergify stack push 之前添加说明:
# Edit HEAD, then:
git commit --amend
mergify stack note -m "address review: rename foo() to bar()"
mergify stack push
# Or for a mid-stack commit (after the rebase that amended it):
mergify stack note <SHA-or-Change-Id-prefix> -m "fix lint reported in CI"
mergify stack push
说明针对的是单个提交,而不是单个修订版本。每次修改(或进行其他历史重写)都会生成新的提交 SHA,因此必须针对新的 SHA 再次运行 mergify stack note——之前的说明仍关联在旧 SHA 上,不会自动转移。仅当当前目标提交已有说明且你想添加另一条原因时,才使用 --append;使用 --remove 可清除说明。自上次推送以来未发生变化的提交上的说明会保留,但不会新增修订记录行。
重要:任何提交前都要检查分支
在暂存或提交任何内容之前,务必检查当前分支并评估堆栈状态:
git branch --show-current
mergify stack list
- 如果你位于
main(或仓库的默认分支):你必须先创建功能分支 - 切勿直接在
main上提交——mergify stack push会失败 - 此检查必须在
git add之前进行,不能等到git commit之后
重要:执行会修改工作树的操作前,先暂存本地更改
在运行任何会重写历史或切换分支的操作之前,检查是否有未提交的更改,并将其暂存起来:
git status --short # Check for uncommitted changes
git stash -u # Stash tracked + untracked changes if any
需要进行此检查的操作:
mergify stack edit <commit>(修复堆栈中间的提交)mergify stack reorder/mergify stack movemergify stack fixupmergify stack squashmergify stack rewordmergify stack dropmergify stack checkoutmergify stack syncmergify stack new(会切换到新分支)git checkout <branch>
操作完成后,恢复暂存的更改:
git stash pop
如果跳过此步骤,未提交的工作可能会在没有提示的情况下丢失,或导致变基冲突。
开始新工作
当你被要求开始一项新工作、创建新功能,或处理与当前堆栈无关的任务时:
- 检查当前分支:
git branch --show-current - 创建新堆栈:
mergify stack new <branch-name>- 使用描述性分支名称,并遵循以下模式:
type/short-description(例如:feat/add-login、fix/memory-leak)
- 使用描述性分支名称,并遵循以下模式:
- 按照约定式提交规范进行提交
- 推送:
mergify stack push
添加到现有堆栈
继续处理现有功能分支上的工作时:
- 检查当前分支:
git branch --show-current- 如果位于正确的分支:继续提交
- 如果位于
main:先使用git checkout <branch>切换到功能分支,或创建新堆栈
解决冲突
变基导致冲突时(在 git rebase -i 或 mergify stack push 期间):
- 在编辑器中解决冲突
- 使用
git add暂存已解决的文件 - 使用
git rebase --continue继续
如要中止:git rebase --abort
解决冲突后,运行 mergify stack push,以同步更新后的堆栈。