mcpField
M
mergify-stack

mergify-stack

github.com/mergifyio开发3287 次安装1 天前更新
代码仓库

关于 mergify-stack

mergify-stack 是使用 Mergify CLI 将 Git 分支组织为短小依赖栈的工作流技能,面向编码 Agent。它规定栈编辑与 PR 更新方式,并要求每个提交都能独立通过 CI。

主要功能

  1. 01创建并更新分支栈与 PR
  2. 02同步栈并查看提交与 PR 状态
  3. 03使用非交互式栈命令改写提交
  4. 04让 PR 内容与提交信息保持一致
  5. 05要求提交可独立通过 CI,PR 保持草稿

适用场景

  1. 01将无关工作拆分到不同分支
  2. 02合并后同步现有栈
  3. 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 pushmergify stack pushGit 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 closePR 生命周期完全由工具管理——无需操作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 move
  • mergify stack fixup
  • mergify stack squash
  • mergify stack reword
  • mergify stack drop
  • mergify stack checkout
  • mergify stack sync
  • mergify stack new(会切换到新分支)
  • git checkout <branch>

操作完成后,恢复暂存的更改:

git stash pop

如果跳过此步骤,未提交的工作可能会在没有提示的情况下丢失,或导致变基冲突。

开始新工作

当你被要求开始一项新工作、创建新功能,或处理与当前堆栈无关的任务时:

  1. 检查当前分支:git branch --show-current
  2. 创建新堆栈:mergify stack new <branch-name>
    • 使用描述性分支名称,并遵循以下模式:type/short-description(例如:feat/add-login、fix/memory-leak)
  3. 按照约定式提交规范进行提交
  4. 推送:mergify stack push

添加到现有堆栈

继续处理现有功能分支上的工作时:

  1. 检查当前分支:git branch --show-current
    • 如果位于正确的分支:继续提交
    • 如果位于 main:先使用 git checkout <branch> 切换到功能分支,或创建新堆栈

解决冲突

变基导致冲突时(在 git rebase -i 或 mergify stack push 期间):

  1. 在编辑器中解决冲突
  2. 使用 git add 暂存已解决的文件
  3. 使用 git rebase --continue 继续

如要中止:git rebase --abort

解决冲突后,运行 mergify stack push,以同步更新后的堆栈。

评论

支持 Markdown