文章

Claude Code 多開在同一個儲存庫,怎麼擋住它把別的 session 的改動一起 commit

同一個儲存庫裡的幾個 agent session 共用同一份 git index。這篇講一個真的會拒絕的 pre-commit 檢查、另一半為什麼只能警告,以及 Clawdline 怎麼知道一條路徑是哪條工作線的。

假設你在同一個儲存庫開了兩個 Claude Code,一個修測試,一個改文件,而且你在 prompt 裡交代過:只 commit 自己改的檔案。結果改文件的那個打了 git commit,把修測試那個剛 stage 好的檔案也一起帶走了。

叫 agent 小心沒有用,因為那條規則只是畫面上一段字,session 一忙就會略過。要擋住這件事,得換成一個會拒絕的檢查。hook 本身只要幾行 shell,難的是它需要一個 git 沒有記的答案:這個 staged 的路徑,到底是誰的工作。

兩個視窗各自 git add,進的是同一份 index

git 一個工作目錄只有一份 index,而 index 就是一個檔案。兩個 session 共用一份 checkout,就是兩個都在寫這個檔案。它沒有主人,這邊 git add 和那邊 git add 進的是同一份暫存區。接著任何一邊打一個沒帶路徑的 git commit,就把裡面全部提交出去,兩邊都不會收到任何提醒。

這不是什麼特殊用法。你在同一個儲存庫開到第三、第四個 session,自然就是這個樣子。

session A:git add session B:git add 同一份 index git commit

兩個 session 各自 stage,進的是同一份 index,一個沒帶路徑的 commit 會把它整份拿走。

index 只有一份,而且沒有主人。它只記 stage 了什麼,不記是誰 stage 的。所以沒帶路徑的 commit 會把兩邊一起帶走,事後看 diff 也分不出哪一段是誰的。要擋住它,得有人在 commit 之前先把歸屬寫下來。

叫 agent 小心一點,我們自己一個星期破了三次

我們自己的儲存庫裡就有這條規則,寫得清清楚楚,結果一個星期內被打破三次。2026 年 8 月底,有兩次是一個沒帶路徑的 git commit,把另一個 session 已經 stage 好的檔案一起帶走。六天後更嚴重:一個 git reset --hard HEAD,把另一條工作線正在進行的合併整個毀掉,包括一個 MERGE_HEAD、六個手動解好的衝突,還有一個 2,265 行的新檔案。

三次都一樣:規則在、檢查也跑了、輸出也正確,然後它被略過了。

規則本身什麼都不缺。缺的是一個能執行它的東西;在那之前,指示就只是建議。

不靠任何服務的那一半,九行就能拒絕

先處理你自己就能做的那一半。如果 git 目錄裡有 MERGE_HEAD、CHERRY_PICK_HEAD、REVERT_HEAD 或 rebase 的狀態目錄,代表有一個操作還沒做完,你現在 git commit 就是在替它收尾。在共用的 checkout 裡,開始那個操作的不一定是你。檢查這件事只要九行,不需要任何基礎設施:

#!/bin/sh
# pre-commit——拒絕替一個「這個 session 可能沒有開始過」的操作收尾。
git_dir=$(git rev-parse --git-dir) || exit 0
for f in MERGE_HEAD CHERRY_PICK_HEAD REVERT_HEAD rebase-merge rebase-apply; do
    [ -e "$git_dir/$f" ] || continue
    echo "pre-commit: $f 存在——這次 commit 會替一個進行中的操作收尾。" >&2
    echo "            如果那確實是你的:git commit --no-verify" >&2
    exit 1
done

它只讀本機的 git 目錄,沒有任何依賴會掛掉。注意它用 git rev-parse --git-dir 找 git 目錄,沒有寫死 .git,因為在 linked worktree 裡,那些狀態檔不在 .git 底下。

訊息裡特地寫出 git commit --no-verify。守衛如果完全沒有通道可以繞過,大家就會乾脆把它拔掉,從此再也不用。--no-verify 跳得過 pre-commit,跳不過 post-commit,所以你可以在 post-commit 印一行,說這次 commit 沒有經過檢查。

「這個檔案是誰改的」,git 從來沒記

另一半,你在 git 裡面解決不了:這個 staged 路徑是不是別人的工作?git 記的是 index 裡有什麼,從來不記是誰放進去的。所以答案必須在 commit 之前就存在,由派工作出去的那一方先寫下來。

這正是 Clawdline 負責的部分。每個派出去的任務都要宣告它的寫入範圍(claims),也就是它可以改哪些儲存庫路徑;範圍是空的,就是明確的唯讀任務。我們的 pre-commit hook 會問本機的 App 目前有哪些任務還在跑、各自宣告了哪些路徑,只要一次 commit stage 到別條工作線的路徑,就拒絕它。

路徑屬於工作線,不屬於視窗。派出任務的那個 session,可以提交它派出去的任務宣告過的路徑,而且合併收尾的時候它非這樣不可。

這兩個檢查失敗的方向,是我們刻意設計成相反的。檢查進行中操作的那一個會拒絕,因為它什麼都不依賴。檢查路徑歸屬的那一個要去問一個不一定開著的服務,所以它只警告:列出它沒辦法背書的路徑,然後放行。

一個什麼都不依賴的檢查可以拒絕;一個要靠某個服務活著的檢查不可以拒絕,不然那個服務一停,你就跟著不能工作。

被擋下來時,先把別人的路徑拿出暫存區

被擋下來,處理方式是兩個指令:

git reset -- <對方的路徑>      # 只把它從暫存區拿掉,不碰它的內容
git commit -m "<訊息>"         # 然後提交你自己看過的 index

還有第三個指令看起來最對,其實是陷阱:git commit -- <你的路徑>。它提交的是這些路徑在工作目錄裡的內容,不是你 stage 起來的那幾段 hunk,所以另一個 session 還沒 stage 的改動,也可能被一起帶進去。

hook 只在 git commit 時跑,最嚴重那次它擋不到

這個守衛管得很窄,因為 pre-commit 只有在 git commit 的時候才會跑。拿我們自己那三次來對照:

操作 pre-commit 會跑嗎 我們自己那三次
git commit 會。只有這一個會呼叫它 8 月那兩次。擋得住
git commit --amend 會跑,但 HEAD 裡原本就有的東西直接過去,不受檢查 –
git reset --hard 不會 9 月那一次。擋不住
git stash 不會 –
沒有衝突的 git merge 不會。它直接寫下 commit,完全不呼叫 hook –

merge 那一列,很多人以為答案相反,我們在 git 2.38.1 上確認過。結果就是:這個守衛擋得住三次裡的兩次,擋不住最嚴重的那一次。reset --hard 永遠不會經過 pre-commit,而 reference-transaction 要等工作目錄和 index 都已經沒了才會觸發。

要不要乾脆每個 session 開一個 worktree?

git worktree add 會給 session 一份自己的工作目錄和自己的 index,上面這整類問題就不會發生。

但這是取捨,不是升級。新開的 checkout 沒有 build 快取,最開始幾分鐘都花在重新編譯原本就在磁碟上的東西。2026 年 8 月 28 日,我們在自己的一台機器上量了 138 個派出去的任務:跑在獨立 worktree 的,每秒用掉的 token 大約是 1.5 倍,中位數約 8,200 對上暖好的共用 checkout 的 5,400。只是一台機器、一個人的習慣,但方向很明確。

所以我們的經驗法則是:會動到很多檔案的長工作,放進自己的 worktree;短工作留在共用的 checkout,前面擋一個守衛。

兩種做法都顧不到的事

第一種是大家一起往裡面加內容的共用檔案。兩個 session 改的是不同行,git 不會警告,某一邊還沒 commit 的內容,就會跟著下一個提交這個檔案的人進去。我們自己的檔案在 2026 年 9 月 3 日就這樣出過事。提交這種檔案之前,先用 git diff --cached -- <檔案> 看每一段 hunk,因為 stat 只告訴你改了幾行,不會告訴你是誰的。

第二種是坐在那台機器前面、也在改同一批檔案的人。兩種做法都看不到這個人,但那個什麼都不依賴的檢查還是擋得住,因為它讀的是 git 目錄,不管你是誰。


回到開頭那兩個視窗。它們還是共用同一份 index,但裝了那九行 hook,其中一個再想替別人進行中的合併收尾,就會先被擋下來;要擋住它帶走別人 stage 好的檔案,還得有人在 commit 之前寫下哪條路徑是誰的。一個任務會宣告什麼、之後由誰負責,寫在工作流程指南;這些名詞的定義在 Stations 那一頁。今晚只帶一樣東西回你自己的儲存庫:那九行 hook。

常見問題

可以同時開兩個 Claude Code 在同一個 repo 裡工作嗎?
可以,只是開之前要知道一件事:它們共用同一份 git index。這個 session 的 git add 和另一個 session 的 git add 會進到同一份暫存區,一個沒帶路徑的 git commit 就把兩邊的東西一起提交。git 從來沒有「每個 session 一份暫存區」這回事,所以同一份 checkout 開兩個視窗就會碰到,不是什麼少見的用法。可行的做法有兩種:裝一個 pre-commit hook,遇到證明得出屬於另一條工作線的 commit 就拒絕;或是每個 session 各開一個 git worktree,讓大家沒有共用的 index 可搶。兩種都不做,遲早會親身遇到一次。
怎麼避免一個 AI agent 把另一個 agent 的改動 commit 進去?
把規則放到一個會說「不」的地方。寫在 prompt 裡的規則,偏偏會在 session 最忙的時候被略過。你自己就能做好的那一半只要九行 shell:一個 pre-commit hook,只要 git 目錄裡有 MERGE_HEAD、CHERRY_PICK_HEAD、REVERT_HEAD 或 rebase 的狀態目錄,就以非零值結束,因為這時候你的 commit 是在替一個你不一定有開始的操作收尾。你自己做不出來的是另一半:判斷某個 staged 路徑是誰的。git 只記 stage 了什麼,從來不記是誰 stage 的,所以這個答案得在 commit 之前,由派工作出去的那一方先寫下來。
每個 agent session 都要開一個 git worktree 嗎?
不一定。它是取捨,不是升級。每個 session 一個 worktree,各自有自己的 index 和工作目錄,上面這整類問題就不會發生;代價是新的 checkout 沒有 build 快取,也沒有共用那份 checkout 早就裝好的依賴,所以一開始的幾分鐘都花在重建原本就在磁碟上的東西。2026 年 8 月 28 日,我們在自己的機器上量了 138 個派出去的任務:跑在獨立 worktree 的,每秒用掉的 token 大約是跑在暖好的共用 checkout 的 1.5 倍。經驗法則是:會動到很多檔案的長工作放進自己的 worktree,短工作留在共用的 checkout,前面擋一個守衛。