如果你曾經開過一個有 47 個變更檔案的 PR,而且 diff 長到 GitHub 乾脆放棄,直接連續顯示十七次「Load Diff」,那這篇就是寫給你的。
GitHub 悄悄上線了可能是這幾年來最大的 Pull Request 更新,而且它正是針對這個問題而來。
來聊聊堆疊式 Pull Request(stacked pull request)。
大型 PR 就是好評審走進去就出不來的地方。
沒有人會仔細讀一份 2000 行的 diff。
有些人會找像 LiveReview 這類 AI 程式碼審查工具來減輕負擔,老實說這確實有幫助,但就算是最好的審查者(人或模型),面對一個緊湊、聚焦的 diff,也會比面對一整面 2000 行的牆表現得更好。
輸入越小,審查越好。無論是誰在審查,這都成立。
Stacked PR 就是 GitHub 的解法:把一個超大的變更拆成一串彼此依賴的小 PR,每一個只審查它自己實際引入的 diff,而不是下面所有東西。

規則很簡單。你需要在同一個 repo 裡有兩個以上的 PR,且符合以下條件:
main)main就這樣。整個技巧就是這個。
基礎性的東西(schema、共用型別)放在最底層。
依賴它的東西(API 路由、UI)放在更上面。

而最讓我意外的是:如果你只是用最普通的 git 手動這樣做,像是把 PR #11 的 base 設成 PR #10 的分支,而不是 main,GitHub 現在會自動把它辨識成一個 stack。
不需要任何特殊工具。
它只是注意到 base 分支形成了一條鏈,然後跳出一個橫幅提示。
Stack 並不是 git 的概念,它完全是建立在你原本就會建立的分支之上的 GitHub UI 概念。
理論講夠了。我在自己的一個 repo 裡實作了一個真正的 stack(peektea,我維護的一個終端機檔案瀏覽器),而且用的是一個無害的測試檔,所以沒有碰到任何真實資料。
以下是實際的終端機操作紀錄,原封不動貼上,瑕疵也都保留。
一開始我想耍帥,用 CLI 擴充套件:
$ gh stack --help
unknown command "stack" for "gh"
對。結果 gh stack 需要 CLI 擴充套件,或者需要比我 $PATH 裡那個版本更新的 gh;而我的版本是來自 Ubuntu 的 apt 套件庫,還沒聽說過這個功能。
軟體嘛,大家都懂。
所以我就照文件裡提到的「老方法」來:純 git 分支,逐層串接 base,不需要擴充套件。



從 git 的角度來看,這就只是 git 一直以來給你的東西:三個分支像疲憊的通勤族一樣,一層疊一層。

到這裡還沒什麼特別的。這就只是分支。
真正的魔法,是當你把它們 push 上去,並用正確的 base 開 PR 時才開始:
$ git push -u origin stack/01-setup-database stack/02-api-endpoints stack/03-setup-frontend
$ gh pr create --base master --head stack/01-setup-database \
--title "demo: setup database layer"
https://github.com/lovestaco/peektea/pull/10
$ gh pr create --base stack/01-setup-database --head stack/02-api-endpoints \
--title "demo: add api endpoints"
https://github.com/lovestaco/peektea/pull/11
$ gh pr create --base stack/02-api-endpoints --head stack/03-setup-frontend \
--title "demo: setup frontend"
https://github.com/lovestaco/peektea/pull/12
注意看 PR #11 的 --base 是 stack/01-setup-database,不是 master。
這一個旗標,就是全部的關鍵。
這就是讓我驚訝的地方。我原本以為還得手動去切什麼設定。
結果我一把鏈條最上層的 PR 開出來,GitHub 直接跳出一個橫幅:「This pull request can be stacked with other pull requests」,旁邊還放著一個「Preview stack」按鈕。

點下去之後,會出現一個小預覽,完整走過整條鏈,從最上面的 PR 一路往下到 main,順序正確、關聯正確,而且除了用正確的 base 分支開 PR 之外,我完全沒有做其他設定。

按下「Create stack」之後,清單裡的每個 PR 都會多出一個很小的進度標記:1/3、2/3、3/3,直接顯示在 Pull Request 清單中。這樣你不用點進任何 PR,就能一眼看出某個 PR 在堆疊裡的深度。


根據 GitHub 官方關於 stacked pull requests 的說明文件,當你對整條鏈都滿意之後,會有一個「merge stack」動作,可以一路往下把所有 PR 依序合併,完全不需要在每次合併之間手動 rebase。
我在 demo repo 裡沒有真的按下去測(光是關掉四個瀏覽器分頁就已經夠混亂了),但從文件和 UI 來看,它就是把過去需要 N 次連續合併加上 N 次 rebase 的流程,變成一個按鈕。
大致上整個流程長這樣,CLI 或手動都可以,結果沒差:

某種程度上,是的,而且這點值得誠實面對。你一直都可以把 PR 開到另一個 PR 的分支上。
這不是新功能,這只是 git 加上 GitHub base branch 下拉選單而已,大家其實十多年來都在這樣串分支。
新的地方在於:GitHub 現在理解了這種關係,會把它當成一等公民來追蹤(我那個 stack 在我按下「Create stack」的瞬間就拿到自己的 stack ID),在整個清單檢視中顯示進度,並且提供一個合併動作,而不是每一層都要你手動「合併、rebase、再合併」的舞步。
所以不,git 本身沒有新增一個 stacked PR 的原生概念。
是 GitHub 的 UI 有了。
如果你 clone 這個 repo 再看分支,老實說,它們和任何其他 feature branch 沒有任何不同。
「stack」只是一份 GitHub 在自己那邊保存的 metadata。

這就是最讓我比平常看到資料庫 migration 畫面還興奮的地方。
想想看,當你把 Claude 指向一個大任務,讓它自己跑好幾個小時之後,會發生什麼事。
過去通常只有兩種糟糕結果:要嘛它回給你一個巨大到無法審查的 PR,要嘛它開出一打彼此完全沒有關聯的 PR,根本看不出哪個依賴哪個。
Stacked PR 讓 agent 有了第三條路:用一個細心的人類會採用的方式來做功能,分層產出可審查的變更,讓依賴鏈本身去表達順序。
先 schema,再是需要 schema 的 API,然後是需要 API 的 UI。你就照著 agent 建構它的順序來審查。

不再需要在「一個無法審查的 3000 行 PR」和「十二個彼此看不出關聯的 PR」之間二選一。
這個 stack 就是 agent 真的怎麼思考這個問題的變更紀錄。

如果你們團隊本來就會手動把工作拆成小 PR,只是一直在承受 rebase 的痛苦,那答案當然是要。這功能把痛苦拿掉了,習慣也保留了。
如果你們團隊現在還是「一個功能一個 PR,不管多大都塞進去」的風格,stacked PR 也是個很好的切入點,因為你可以繼續沿用現在的工作方式(從前一個分支再切出去),而 GitHub 幫你處理以前得靠腦袋記的那些關係。
GitHub 文件仍然把它標成比較新的功能,所以可以預期還是會有一些粗糙邊角(我的 gh CLI 甚至還不知道這個指令存在),但 web UI 這部分我第一次試就完全如預期運作,沒有設定、沒有 opt-in 旗標,只要用正確的 base 分支開 PR,然後看 GitHub 自己把線串起來就好。
放心去疊吧,負責任地疊。還有,如果你要走擴充套件那條路,記得先更新你的 gh CLI,別像我一樣。

AI agent 寫程式很快。它們也會悄悄刪掉邏輯、改變行為,還會引入 bug——而且不會告訴你。你通常要到上線後才會發現。
git-lrc 可以解決這個問題。它會接入 git commit,在每個 diff 進入倉庫之前先審查。60 秒完成設定,完全免費。
歡迎任何回饋或貢獻!它已上線、原始碼可取得,而且任何人都能使用。
⭐ 到 GitHub 幫它點星:
{% github=https://github.com/HexmosTech/git-lrc
原文出處:https://dev.to/lovestaco/ayo-github-quietly-killed-the-unreviewable-mega-pr-3868