Git 全景課 / 第二部分 分支與歷史 · M8

M8 並行工作:stash 與 worktree

手上的工作做到一半,突然要處理另一件急事。你可以把半成品暫時收進抽屜(stash),也可以多開一張書桌(worktree)。本單元把兩者都練熟,並解答本課第一條學生問題:分支和 worktree 到底有甚麼分別。
約 60 分鐘沙盒 5 個任務 · 小測 4 題先修:M4、M5FAQ-001
學完本單元,你能夠:
  1. 用 git stash 收起、取回、管理未提交的修改;
  2. 用 git worktree 同時在幾條分支上工作,並正確地收拾;
  3. 解釋「同一條分支同一時間只能在一個 worktree」的原因;
  4. 按情況在 stash、分支、worktree 之間選擇。

1做到一半,要先處理別的事 Interrupted work

你正在改報告的兩節,還沒改完,也不想把半成品提交。這時導師要你立即修正另一處的錯誤。M4 說過,有未提交的修改時切換分支,Git 可能會拒絕;即使能切換,半成品也會跟着你到另一條分支,混進錯誤的 commit。

有三種做法:

  1. 先提交半成品(之後用 M6 的 reset --soft 或 M7 的互動式 rebase 整理);
  2. stash:把修改暫時收進抽屜,處理完再拿出來;
  3. worktree:不動現在這張書桌,另開一張書桌處理急事。

2stash:把半成品收進抽屜 Stashing

git stash                  # 收起:工作目錄和暫存區回到 HEAD 的樣子
git stash push -m "改到一半的第二節"   # 收起時寫一句說明
git stash list             # 列出抽屜裏的東西:stash@{0} 是最新的
git stash pop              # 拿出最新一份,並從抽屜移除
git stash apply stash@{1}  # 拿出指定的一份,但留在抽屜
git stash drop stash@{1}   # 丟掉指定的一份

抽屜是一個堆疊:最後收進去的,最先被 pop 出來。拿出來時,Git 用三方合併把修改套回目前的檔案;如果期間同一處(或相鄰處)被改過,就會出現衝突標記 <<<<<<< Updated upstream … >>>>>>> Stashed changes,處理方法和 M5 相同,而那一份 stash 會保留在抽屜,直到你確認後 drop。

stash 其實也是 commit git stash 建立兩個特殊的 commit:一個記下暫存區(index on …),一個記下工作目錄(WIP on …,它的第二個 parent 就是前者),再由 refs/stash 指着。所以 stash 和分支一樣存在倉庫裏,所有 worktree 共用同一個抽屜。沙盒算出的 stash 編號與真實 Git 相同。
兩個常見的坑
  • 未追蹤的新檔案預設不會被收起。要連它們一起收,用 git stash -u。
  • 抽屜會被遺忘。stash 沒有分支名,幾星期後你不會記得 stash@{3} 是甚麼。收起時寫說明(-m);收起超過一天的工作,通常應該改為提交到一條分支。

3worktree:多開一張書桌 Worktrees

FAQ-001 的比喻:倉庫是一本書,分支是書籤,worktree 是一張攤開這本書的書桌。git worktree add 在磁碟上多建一個資料夾,裏面攤開另一條分支,而歷史仍然只有一份。

一份 .git,三張書桌 上方是共用的 .git,內有所有物件、所有分支和 stash。下方三個虛線框是三個 worktree:~/project 攤開 main,~/app-feature 攤開 feature,~/hotfix 攤開 hotfix。每個 worktree 有自己的 HEAD、工作目錄和暫存區。 ~/project/.git — 共用 所有物件(commit、tree、blob)· 所有分支(refs/heads/*)· 標籤 · stash(refs/stash) 另有 worktrees/ 資料夾,記錄每張附加書桌各自的 HEAD 和暫存區 ~/project HEAD › main 主 worktree: .git 是資料夾 ~/app-feature HEAD › feature 附加 worktree: .git 是一個檔案,指回上方 ~/hotfix HEAD › hotfix 各自的 HEAD、 工作目錄、暫存區
圖 1. 虛線框=worktree(書桌),深色標籤=分支。在任何一張書桌提交,其他書桌立即看得到那個 commit(物件共用);但各自的未提交修改互不影響。
git worktree add ../app-feature feature     # 為現有分支開一張書桌
git worktree add -b hotfix ../hotfix         # 同時建立新分支 hotfix
git worktree list                            # 列出所有書桌
cd ../hotfix                                 # 到那張書桌工作(就是普通的資料夾)
git worktree remove ../hotfix                # 收拾書桌(分支本身保留)
git worktree prune                           # 清理已被手動刪除的書桌留下的記錄

一條分支,同一時間只能在一張書桌

如果兩張書桌都攤開 main,你在其中一張提交,main 會往前走;另一張書桌的檔案和暫存區卻還停在舊版本,它下一次提交就會悄悄蓋掉剛才的改動。所以 Git 直接禁止:

fatal: 'feature' is already used by worktree at '~/app-feature'

同樣地,被某張書桌攤開的分支也不能刪除。要在另一張書桌查看同一個版本,可以用 git worktree add --detach <路徑> main(detached HEAD,M4)。

收拾書桌要用命令,不要直接刪資料夾 直接刪除附加 worktree 的資料夾,.git/worktrees/ 裏的記錄仍在,那條分支也會一直被當作「使用中」。用 git worktree remove;如果已經刪了,執行 git worktree prune。另外,每張書桌都是一份完整的工作檔案,用完就收拾,免得佔用大量磁碟空間。

4沙盒:抽屜與書桌 Sandbox: stash and worktrees

互動 抽屜與書桌

「目前位置」下方列出所有書桌,按「cd 過去」或輸入 cd ../hotfix 切換。圖上的虛線標籤是其他書桌所攤開的位置。按「改寫」修改檔案(不提交就是未提交的修改)。

應用線:

5stash、分支、worktree 怎樣選 Choosing

stash分支+提交worktree
本質抽屜:暫存的修改(特殊 commit)書籤:歷史裏的一個位置書桌:磁碟上的另一個資料夾
適合幾分鐘到幾小時的打斷任何要保留的工作要同時進行、或要長時間並存的工作
同時看兩個版本不行不行(一個資料夾一次只展示一條分支)可以,兩個資料夾並排
成本幾乎為零幾乎為零一份完整的工作檔案
風險容易被遺忘半成品混入歷史(可之後整理)忘記收拾、佔用磁碟

worktree 與 clone 有何不同?clone 是另一個完整的倉庫(另一份 .git),兩邊的 commit 要靠 push/pull 才能互通。worktree 共用同一份 .git:在任何一張書桌提交,其他書桌立即看得到,不必同步。

常見誤解
  • 「開一個 worktree 就是複製了整個倉庫。」不是。歷史只有一份;複製的只是那條分支的工作檔案(FAQ-001)。
  • 「stash 只存在我現在這個資料夾。」不是。refs/stash 在共用的 .git 裏,任何一張書桌都能 git stash list、pop。
  • 「git worktree remove 會刪掉分支。」不會。它只收拾書桌,分支和 commit 都保留。

6worktree 與 AI 協作 Worktrees and AI agents

讓幾個 AI agent(或幾位同學)同時修改同一個倉庫時,最常見的做法就是一個 agent 一張書桌:每個 agent 在自己的 worktree、自己的分支上工作,互不干擾;完成後各自開 PR,經審查合併回 main。例如 Claude 的桌面程式為每個工作階段自動開一個 worktree(資料夾在 .claude/worktrees/ 之下),這門課的教材就是這樣寫成的。worktree 的「一條分支一張書桌」規則,正好防止兩個 agent 同時改寫同一條分支。M13 會完整討論 AI 時代的協作規則。

7命令速查 Cheat sheet

命令作用
git stash / git stash push -m "說明"收起未提交的修改
git stash -u連未追蹤的檔案一起收起
git stash list列出所有 stash
git stash pop / apply取回最新一份(pop 會移除,apply 保留)
git stash drop stash@{n}丟掉指定的一份
git worktree add <路徑> <分支>為現有分支開書桌
git worktree add -b <新分支> <路徑>建立新分支並開書桌
git worktree list列出所有書桌
git worktree remove <路徑>收拾書桌(有未提交修改時要 --force)
git worktree prune清理被手動刪除的書桌的記錄

8小測 Quiz

1. git stash 之後,工作目錄和暫存區會變成:
2. main 已經在 ~/project 攤開。你在 ~/app-feature 執行 git switch main,結果是:
3. 你在 ~/hotfix 提交了一個 commit。回到 ~/project 後:
4. 要同時開着兩個版本的網站各自執行、互相比較,最合適的是:

本單元重點 Summary

延伸閱讀