Communitygithub.com

堆疊式 PR 合併落地

將一連串堆疊的 PR(每個 PR 基於前一個 PR 的分支)逐層合併到基礎分支(develop/main/master),無需為中間分支設定 CI 觸發允許清單。支援透過 merge-async API 處理 GitHub 原生堆疊。

堆疊式 PR 合併落地 是什麼?

這個技能專門用於處理多個 PR 形成鏈式堆疊(PR N 基於 PR N-1 的分支)的情況,並將它們依序合併到真正的基礎分支(如 develop 或 main)。其核心原則是:不依賴中間分支的 CI 作為合併關卡,而是依靠基礎分支的保護機制。它會即時將每一層 PR 的目標分支重新設定為基礎分支,讓原生的 CI 驗證該層。技能會先透過衝突檢測與內容依賴分析,決定正確的合併順序,接著逐層執行重新設定基礎、同步基底、執行 CI、取得審核批准,最後合併。對於 GitHub 原生堆疊功能建立的 PR,由於無法使用標準的 `gh pr edit --base` 或 `gh pr merge` 指令,它會改用特殊的非同步合併 REST API(`merge-async`),並在每個層合併後處理可能發生的衝突(例如工作流程檔案編輯衝突或格式掃描衝突)。它也提供了安全指南,例如禁止強制推送、禁止改寫歷史、合併前必須取得人工審核批准。這個技能取代了依賴中間分支 CI 允許清單的反模式,讓開發者能更流暢、更安全地將堆疊式 PR 落地。

相容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/sblattj/agent-chandlery/tree/main/skills/landing-stacked-prs

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

將堆疊式 PR 合併落地到基礎分支

當多個 PR 形成鏈式堆疊(PR N 基於 PR N-1 的分支)且需要合併到真正的基礎分支時使用。 關鍵原則:中間分支的 CI 不是關卡,基礎分支的保護才是。 因此,在合併前一刻將每一層的目標分支重新設定為基礎分支,讓原生 CI 驗證它。 絕對不要依賴將中間分支名稱硬編碼到 CI 觸發條件中。

儲存庫:將 -R owner/repo 傳遞給 gh,或從 clone 的目錄執行(gh repo view --json nameWithOwner -q .nameWithOwner 會自動解析)。以下,R 即為該 owner/repo。

原則

  • 合併順序由真正的依賴關係決定(內容依賴 + 被多層編輯的檔案)。先確定此順序;不要為了方便而重新排序。
  • 每一層只能合併到基礎分支,一次合併一層。絕對不要將子層合併到舊的先決條件分支。
  • 在一層的 CI 生效前,先將目前基礎分支合併到該層,使其 diff 縮減為僅包含自身的變更,並讓它看到所有已合併的內容。
  • 頂端分支包含所有層的累積變更;在頂端分支(重新設定目標到基礎分支後)執行一次 CI 即可驗證整個堆疊。可將其作為可選的預先檢查。

步驟 0:確定順序並檢測衝突

確認儲存庫真正的基礎分支(從 git 遠端取得的預設分支,例如 develop 或 main;使用者的代理指令檔案(AGENTS.md / CLAUDE.md)可能會列出已知的基礎分支)。 對於每個 PR,針對基礎分支檢測衝突,而不是針對其過時的堆疊基礎分支(後者會顯示為 CLEAN 而誤導你):

git fetch origin <base> <head> --quiet
git merge-tree --write-tree origin/<base> origin/<head> | grep -iE 'CONFLICT|changed in both' || echo "clean vs base"

順序經驗法則:先決條件內容排在需要它的關卡之前(例如,先做 lint 清理,再做 lint 啟用);當兩層編輯同一個檔案時,先編輯的層優先合併。

步驟 1..N:依序處理每一層

PR=<number>
gh pr edit $PR -R $R --base <base>     # 重新設定目標,從堆疊分支改為基礎分支
# 為 head 分支建立一個獨立的工作目錄,讓你的主要 checkout 保持乾淨:
WT=../<repo>-<head>                    # 任何位於主 checkout 目錄外的路徑
git worktree add "$WT" <head> 2>/dev/null || true
git -C "$WT" fetch origin --quiet
git -C "$WT" merge origin/<base>       # 同步基礎分支;解決衝突(見下文)
git -C "$WT" push
gh pr ready $PR -R $R                  # 當它是當前處理的層時
gh pr checks $PR -R $R --watch         # 原生 CI,base=<base>
# 僅在審核批准後:
gh pr merge $PR -R $R --squash --delete-branch
# 然後移至下一層(其重新設定目標 + 同步基礎分支會吸收此層)

GitHub 原生堆疊(merge-async)

使用 GitHub 原生堆疊功能建立的 PR 會拒絕上述標準步驟:

  • gh pr edit --base:GraphQL: Cannot change the base branch because the pull request is part of a stack. (updatePullRequest)
  • gh pr merge --auto:GraphQL: Auto-merge is not supported for stacked pull requests.
  • gh pr merge:This pull request is part of a stack and must be merged using the asynchronous merge REST API.

使用非同步 API,從底層 PR 開始(GitHub 會自動將下一個 PR 的目標重新設定為基礎分支,等待約 30 秒):

sha=$(gh pr view $PR -R $R --json headRefOid -q .headRefOid)
u=$(gh api -X PUT repos/$R/pulls/$PR/merge-async -f merge_method=squash -f sha=$sha \
      -f merge_action=direct_merge --jq '.details.uuid // .status')
gh api repos/$R/pulls/$PR/merge-async/$u --jq '.status+" "+(.details.message//"")'   # 輪詢直到狀態不再是 "pending"
  • 用這種方式合併一個堆疊 PR 也會合併所有位於它下方的未關閉 PR,所以只合併你真正想落地的那一層。
  • 每次 squash 合併後,下一層在兩者都觸及的檔案上(例如 ADR、版本列表)通常會變為 DIRTY 狀態:將基礎分支合併到該層,解決衝突,推送,等待 CI,然後再用 merge-async 合併它。
  • 在不撤銷過時審核的儲存庫中,審核批准在基礎分支同步後仍然有效(請檢查分支保護中的 required_pull_request_reviews.dismiss_stale_reviews)。真正的關卡通常是對每個觸及路徑的 CODEOWNERS 批准,而不是批准數量(pr-approval-gate 會列出差距)。
  • 為了讓每個未關閉的 PR 在合併落地期間與其基礎分支保持同步,請執行基礎同步掃描:將基礎分支合併到每個未關閉 PR 的分支中並推送(使用一般合併,不要強制推送)。

衝突解決

  • 被多層編輯的工作流程檔案(可重複使用的建置工作流程、CI 編排工作流程): 保留雙方的編輯,不要選邊站。
  • 格式掃描衝突(例如 Spotless/Prettier 的套用掃描層與基礎分支上的內容變更衝突): 對於衝突的檔案,採用基礎分支的內容,然後對其重新執行格式化工具 (mvn -pl <module> -am spotless:apply 或 pnpm fmt)。採用掃描方的變更會遺失真正的 變更;採用基礎分支的內容而不重新格式化則會導致格式檢查失敗。格式化工具執行需要 建置來解析(有些儲存庫從需要雲端登入的私有註冊表拉取依賴項;如果建置無法取得依賴項,請重新整理登入狀態)。

安全

  • 不要強制推送、改寫歷史或推送到受保護的分支。
  • 遵循儲存庫的提交作者慣例(有些禁止 AI 共同作者標記)。
  • 在每次合併到基礎分支之前,暫停並等待人工審核批准。
  • 如果存在中間分支 CI 允許清單的 PR(此技能旨在取代的反模式),建議關閉它;如果它已經合併,則還原新增的分支名稱和事件類型。

相關技能

  • opening-a-stacked-pr-sequence:開啟堆疊(基礎分支、合併順序)以及每種合併類型的重建堆疊修復(stack.sh restack)。
  • pr-approval-gate:每一層還需要哪些程式碼擁有者的批准。
  • resolve-pr-threads-and-conflicts-and-merge:在處理完討論串和批准後落地單一層。

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

相關技能