返回所有文章
發佈於 · 作者 Renaud Deraison

一個儲存庫,一支代理艦隊:Bromure Agentic Coding 中的 git worktree

讓兩個編碼代理對著同一個儲存庫工作,它們就會在同一份工作樹上相互碰撞。Git worktree 解決了這個問題——在一份共用歷史之上開出 N 份彼此獨立的簽出。Bromure Agentic Coding 把它變成一鍵操作的原語:輸入一個任務名稱,就能得到一條獨立的分支、一份獨立的簽出,以及一個為它工作的代理,各自在拋棄式 VM 內的獨立分頁裡運作。

一個儲存庫,一支代理艦隊:Bromure Agentic Coding 中的 git worktree

編碼代理跑得飛快;拖慢它們的是你的儲存庫。Bromure Agentic Coding 為每個任務提供專屬的分支、專屬的簽出,以及專屬的 代理,各自在自己的分頁裡運作。

編碼代理跑得飛快。拖慢它們的是你的儲存庫。讓兩個代理對著同一份簽出工作,它們就會相互碰撞:同一份工作樹、同一批未提交的檔案、同一條分支。一個代理執行 git checkout,另一個代理的成果就在腳下被抽走了。

Git 用 worktree 解決了這個問題。Bromure Agentic Coding 把 worktree 變成一鍵操作的原語:每個任務都有自己的分支、自己的簽出,以及自己的代理,各自在自己的分頁裡運作。

首先,什麼是 git worktree?

一次 git clone 給你一份工作樹:一個裝著簽出檔案的目錄、一個 HEAD、一個索引。要同時在兩條分支上工作,你得騰挪(git stash、切換、工作、切回、git stash pop,然後祈禱一切安好),或者再做一份 clone——那會複製整個物件資料庫,而且不共用你本地分支的任何檢視。

git worktree add 是第三種選擇。它在你既有的儲存庫上附加一份額外的工作樹:

git worktree add -b fix-login ../fix-login HEAD

這會給你一個新目錄,裡面是一條新分支上的完整簽出,與你的主簽出共用同一個 .git 物件庫。每份 worktree 都保有自己的 HEAD、索引與未提交狀態;在其中一份裡提交,會出現在所有 worktree 中。建立 worktree 不會複製任何物件,因此幾乎沒有成本。把成果合併回去只是一次本地的 git merge:不必 push、不必 pull、不必往返遠端。

Worktree 讓你在一份共用歷史之上,擁有 N 份彼此獨立的工作目錄。

為什麼 worktree 適合代理式編碼

一個自主編碼代理需要一份可以任意翻攪的工作樹:編輯檔案、執行建置、在思考時把做到一半的狀態擱著。這種東西你沒法共用。

Worktree 恰好對應你委派工作的方式:

  • 一個任務、一份 worktree、一個代理。「重構網站」和「修好那個時好時壞的測試」各自拿到一份私有的簽出。誰都無法踐踏對方未提交的成果,也踐踏不到你的。
  • 便宜到可以隨手就用。 因為 worktree 不複製任何歷史,為了一項 20 分鐘的任務就開一份,完全划算。你可不會為了這種事去 clone 整個儲存庫。
  • 就地合併。 所有 worktree 共用同一個儲存庫,所以整合一個代理完成的成果,就是對一條本地分支做一次本地合併。你檢閱的是一份 diff,而不是一整套 pull request 流水線。

Claude Code、Codex 及其他工具在 worktree 內都跑得好好的。摩擦來自那套繁文縟節:挑一個目錄、想一個分支名稱、git worktree add、把你的 .env 複製過去(被 gitignore 的檔案不會跟著來)、cd、啟動代理、貼上任務。然後事後還得把這一切清理乾淨。

Bromure Agentic Coding 免去了這套繁文縟節。

Bromure Agentic Coding 中的 worktree

給第一次接觸的人快速回顧一下:Bromure Agentic Coding 在 Apple 的 Virtualization.framework 上,於拋棄式 Ubuntu VM 內執行你的編碼代理。一個 workspace(工作區)就是一個 VM;它透過 virtiofs 掛載你的專案資料夾(不複製);工作區裡的每個分頁都是一個執行你想要之任何東西的終端機,通常是一個代理。VM 的邊界讓一個處於完全自主模式的代理,可以盡情執行 rm -rfcurl | bash,卻碰不到你的 Mac。

Worktree 增添了第二條隔離軸線。VM 隔離的是機器;worktree 隔離的是工作樹,於是好幾個代理可以共用一個工作區、共用一個儲存庫,卻不共用狀態。

一個工作區視窗,來源清單中開著一個儲存庫分頁,並可見一個終端機
一個工作區,來源清單中開著一個儲存庫分頁。

建立一份

從任何目錄為 git 儲存庫的分頁,你可以:

  • 在分頁上按右鍵,選擇 New worktree…,或
  • 按下 ⇧⌘GWorkspaces 選單裡也列著它,就在 Merge worktree ⇧⌘M 旁邊)。

即使 VM 正掌握著你的鍵盤,這些快捷鍵依然有效:客體視窗管理員會攔下這組組合鍵,再交還給應用程式。

開啟的分頁右鍵選單,顯示 New worktree 動作
分頁右鍵選單,帶有「New worktree…」。
開啟的 Workspaces 選單列,顯示 New worktree ⇧⌘G、Merge worktree ⇧⌘M 與 Discard worktree
Workspaces 選單列出相同的動作及其快捷鍵。

你會得到一個小對話框:一個任務名稱、一個工具選擇器(claudecodexgrok,預設為工作區所用的工具),以及一個選填的初始提示。

New worktree 對話框,帶有任務名稱、開啟的工具選擇器與填好的初始提示
填寫完成的 New worktree 對話框。

按下 Create,在 VM 內部,Bromure 便會:

  1. 從分頁當前的提交切出一條分支 wt/<slug>(任務名稱「Website refactoring」→ 分支 wt/website-refactoring),
  2. 把它簽出到 ~/.bromure/worktrees/<repo>/<slug> 之下,
  3. 複製進任何你的儲存庫在 .worktreeinclude 中列出的、被 gitignore 的檔案(下文詳述),
  4. 在該目錄開啟一個新分頁,並
  5. 啟動你所挑選的工具,若 VM 缺少該工具則先行安裝,並以你的初始提示為起點。

輸入任務名稱後幾秒鐘,就有一個代理在它自己的分支上為它工作了。

觀察整支艦隊

每份 worktree 都是工作區來源清單裡的一個分頁,標著一個分支圖示,並縮排在它源出的那個分頁之下。圖示顯示正在執行哪個代理,一個狀態圓點顯示它的狀態:工作時脈動橙色,完成時綠色,需要你介入時紅色。掃一眼側邊欄就能回答「誰卡住了?」,什麼都不必打開。

一份來源清單,儲存庫分頁下有數個 worktree 分頁、混合的狀態圓點,以及一份巢狀 worktree
數個並行的 worktree 分頁,帶有狀態圓點,其中一份為巢狀 worktree。

Worktree 可以巢狀。一個 worktree 分頁本身就是一份 git 簽出,所以 New worktree… 在那裡同樣管用。重構網站的代理可以衍生一個子代理,在 wt/website-refactoring 的子分支上嘗試一個高風險的變體,縮排再深一層。

合併回去

當一份 worktree 的狀態圓點轉綠,在它上面按右鍵並選擇 Merge…(或 ⇧⌘M)。因為 worktree 可以巢狀,對話框會問你要合併到哪裡:目的地彈出選單列出祖先鏈,從直接的父分支一路上到儲存庫的根簽出,凡是會跳過中間層的選項都會加上警告後綴。

一個 worktree 分頁上的右鍵選單,顯示 Merge 與 Discard worktree 動作
一個 worktree 分頁的右鍵選單,帶有 Merge… 與 Discard worktree。
Merge 對話框,目的地彈出選單已開啟,顯示祖先鏈
Merge 對話框,目的地彈出選單已開啟。

合併不會藏起任何東西。它在目的地簽出裡的一個新分頁中執行,所以你能看著它發生。只有 worktree 分支上已提交的成果會合併過去。有兩種結果:

  • 乾淨: 分頁印出 bromure: merged wt/website-refactoring into main. 並等待你按 Enter。
  • 衝突: 分頁印出 bromure: merge conflicts — starting claude to resolve…,並就地在有衝突的簽出裡啟動代理,指示它解決衝突、將其加入暫存、摘要它所做的事,並在你確認之後才提交。想親手解決、稍後又改變主意?合併分頁的右鍵選單一直保留著一個 Have the agent resolve conflicts 動作。
一個合併分頁印出 worktree 分支已合併進 main,並等待按 Enter
一次乾淨的合併:分支落地,分頁等待你按 Enter。
一個發生衝突後的合併分頁,代理正在摘要它的解決方案
一個有衝突的合併分頁,代理正在解決它。

合併之後 worktree 會原地保留。你可能還想再改一輪,或把同一條分支合併到鏈上更高處。當你徹底做完了,按右鍵 → Discard worktree。這是具破壞性的那一個,它也直說了:它會移除簽出、刪除 wt/ 分支,並警告你會失去任何尚未合併的提交。

Discard worktree 確認對話框,帶有刪除分支的警告
Discard worktree 的確認畫面。

那些讓它真正好用的細節

被 gitignore 的檔案會一起過來,只要你開口。 一份全新的 worktree 是一份乾淨的簽出,所以你的 .env、本地憑證與工具權杖都不在裡面,而一個連開發伺服器都起不了的代理沒什麼用處。在你的儲存庫根目錄放一個 .worktreeinclude 檔案(每行一個相對路徑,與 Claude Code 使用的慣例相同),Bromure 就會把那些檔案從主簽出複製進每一份新 worktree。

你未提交的變更依然屬於你。 Bromure 從分頁當前的提交切出 worktree,而非它的髒狀態。父分頁裡做到一半的成果不會進到代理的簽出,反之亦然。

Worktree 撐得過重開機。 分頁是短暫的,但簽出與分支長存於磁碟上,而且 Bromure 為每個儲存庫保有一份小小的登錄表。關掉工作區,一週後再啟動,你的 worktree 分頁會帶著相同的分支、相同的標籤重新出現,代理也重新啟動。

它在 SSH 之上也管用。 你可以遠端連到每一個工作區,而終端機 UI 具備完整的 worktree 對等能力:一個 🌿 Worktrees… 選單列出儲存庫裡的每一個 worktree 與合併分頁,你可以透過 SSH 用手機建立、合併、解決衝突與捨棄,方式和在 Mac 上一模一樣。

故障不會拖垮應用程式。 Worktree 操作在客體內部執行。若其中一個失敗(目錄消失了、不是 git 儲存庫、tmux 出了岔子),你會得到一筆日誌記錄與一張出現在工作區視窗上、不會阻擋操作的浮動面板,而不是一個把其他一切都凍住的強制對話框。

兩種隔離,一套工作流

Worktree 與 VM 解決的是同一問題的不同兩半。Worktree 隔離的是儲存庫狀態,讓並行的代理無法破壞彼此的成果。VM 隔離的是其餘一切,從檔案系統到網路再到憑證,讓你能放手讓那些代理無人看管地執行。Bromure Agentic Coding 把這兩者組合起來:輸入一個任務名稱、挑一個代理,就能得到一條獨立的分支、一份獨立的簽出,以及一台獨立的機器,全都藏在一個分頁後面。

整套流程只有三個按鍵那麼深:⇧⌘G 委派,完成時一個綠點,⇧⌘M 合併。其餘的就是 git,那種無趣而可靠的東西。


Worktree 需要 Bromure Agentic Coding 4.0.8 或更新版本。Bromure Agentic Coding 隨 Bromure 一同提供——在 bromure.io/download 下載。Worktree 支援也需要一個執行著當前 VM 內代理的工作區,所以較舊的工作區請重新啟動一次以取得它。