退款功能還在 feature/order-refund 上開發到一半,畫面開著 dev server,資料庫接的是測試用的假資料,還有一堆檔案沒 commit。這時候 PM 敲你:main 上結帳頁的稅額算錯了,要你馬上看一下。
以前遇到這種事,標準流程是 git stash,切到 main,看完、修完,再切回來,git stash pop。三、五分鐘的事,做起來卻很煩:stash 有時候會跟你切過去那邊的檔案衝突,dev server 因為切分支重新載入一次,如果兩個分支的 package.json 不一樣,還得重新 npm install。看完 main 的問題回來,偶爾還會忘記自己 stash 過東西,隔天才想起來。
git worktree 解決的就是這件事:同一個 repo,可以同時有好幾個工作目錄,各自 checkout 不同分支,互不干擾。
沒有 worktree 之前:stash、切分支、再切回來
流程大概長這樣:
git stash # 手上的修改先收起來
git checkout main # 切過去看 main
# ...看完、可能還順手修了一版...
git checkout feature/order-refund # 切回來
git stash pop # 修改拿回來,祈禱不要衝突
這個流程本身沒有錯,git 也是設計成這樣用的。問題是它把「同時處理兩件事」硬擠成「輪流處理兩件事」,而且每次切換都有成本:
stash 可能衝突。 尤其你在 main 上也順手改了幾行,兩邊剛好動到同一個檔案。
開發環境要重新暖機。 dev server 的 file watcher 重新掃一次、有時候直接掛掉;node_modules 版本不同的話還要重灌。
狀態容易忘記。 stash 完去忙別的事,過一陣子回來已經不記得自己 stash 過什麼,
git stash list裡堆了好幾層。
worktree 不是取代這個流程,是讓你根本不用做這個流程——兩個分支各自待在各自的資料夾裡,你要哪個開哪個。
worktree 是什麼
一個 git repo 只有一份 .git,裡面存著所有的 commit、物件(object)和分支資訊。平常你 git checkout 切分支,動的其實是同一個工作目錄裡的檔案內容和 .git 裡那個唯一的 HEAD 指標。
git worktree add 做的事情是:在旁邊另外開一個資料夾,這個資料夾也是一份完整、可運作的工作目錄,checkout 到你指定的分支,但它跟原本的工作目錄共用同一份 .git,不是重新 clone 一份出來。
共用的是 commit 歷史、物件、分支這些「repo 本體」的資料;不共用的是工作目錄裡實際的檔案,包括每個資料夾自己的 node_modules、.env、IDE 設定這些沒進版控的東西。這點很直覺:你在 order-refund-hotfix/ 改了一個檔案並 commit,order-refund/ 那邊 git log 馬上看得到這筆 commit(因為 .git 是共用的),但 order-refund-hotfix/ 資料夾裡多裝的套件,order-refund/ 不會自動有。
實際操作
在既有的 repo 旁邊,另開一個工作目錄看 main:
git worktree add ../order-refund-hotfix main
這樣會在上層目錄建一個 order-refund-hotfix/ 資料夾,裡面已經 checkout 好 main,可以直接 cd 進去跑 dev server、跑測試,跟原本 feature/order-refund 那邊的工作目錄完全獨立。
如果是要開一個新分支來修 bug,順便建 worktree,一行搞定:
git worktree add -b hotfix/tax-calc ../order-refund-hotfix main
看完、改完之後,確認資料夾裡沒有殘留的未 commit 修改,就可以移除:
git worktree remove ../order-refund-hotfix
想確認目前 repo 底下開了哪些 worktree,可以:
git worktree list
會列出每個工作目錄的路徑、目前的 commit、checkout 到哪個分支。
幾個容易踩到的地方
同一個分支不能同時被兩個 worktree checkout。 如果 main 已經在別的 worktree 裡簽出了,你在另一個地方想再 checkout 一次 main,git 會直接拒絕,跳出 'main' is already checked out at '...'。要嘛開新分支,要嘛用 --detach 進入 detached HEAD 狀態(單純想看某個 commit、不打算在上面 commit 新東西的話,detached HEAD 完全夠用)。
node_modules、.env 不會跟著共用。 worktree 共用的只有 .git 裡的東西,工作目錄裡沒進版控的檔案,每個資料夾都要自己準備一份。如果專案的相依套件很肥、安裝要花好幾分鐘,這是使用 worktree 前要考慮的成本——不是每次臨時看一下 main 都划算開一個新 worktree,看情況決定。
手動刪資料夾不算清乾淨。 worktree 的中繼資訊實際上放在 .git/worktrees/ 底下,如果你直接把 order-refund-hotfix/ 資料夾丟到垃圾桶,而不是用 git worktree remove,.git/worktrees/ 裡會留下一筆指向不存在路徑的孤兒紀錄。之後可以用 git worktree prune 清掉這些殘留。
IDE 建議當成獨立專案開。 兩個 worktree 資料夾內容差很多的時候(例如一個在改前端、一個在追 bug),同一個 IDE 視窗開兩個 worktree 容易搞混現在到底在哪個分支上,習慣上我會另開一個視窗,一個資料夾一個視窗對應一個分支。
SourceTree 沒有這個功能,只能靠終端機
前一篇提到 rebase 在 SourceTree 裡可以整套用滑鼠做完,worktree 不行——目前的 SourceTree 沒有管理 worktree 的圖形介面,新增、移除都得自己在終端機(或 SourceTree 內建的 Terminal 按鈕)裡打指令。
這不是 SourceTree 的缺點,worktree 本來就是相對冷門、CLI 導向的功能,日常用到的頻率遠不如切分支、commit、rebase。知道這件事的好處是:遇到問題不用在選單裡到處找,直接開終端機就對了。
請 AI 幫忙操作 worktree 時,指令要多一層小心
跟 rebase 不一樣的地方在於,worktree 的操作會實際在你的檔案系統上新增、刪除資料夾——不是重寫 git 歷史那種「錯了還能救回來」的風險,是「資料夾真的被砍掉」的風險。請 AI 助理代勞時,這個分寸要講清楚:
請幫我在目前 repo 旁邊新增一個 worktree 來看 main 上的 bug。
規則:
1. 用 git worktree add 建立,路徑固定在 ../order-refund-hotfix,
不要用其他路徑,也不要覆蓋已存在的資料夾。
2. 建立前先執行 git worktree list,確認 main 目前沒有被其他
worktree 簽出;如果已經被簽出,停下來跟我說,不要自動改用
detached HEAD 或其他分支名稱。
3. 之後如果要移除,一律用 git worktree remove,不要用 rm -rf
直接刪資料夾。移除前先確認該工作目錄沒有未 commit 的修改,
有的話列出來讓我看,不要自動捨棄。
重點跟先前那篇一樣:把「路徑」「要不要覆蓋」「刪除方式」這幾個會動到實際檔案的地方寫死,AI 遇到模糊指令時,比較容易自己找一個看起來合理的路徑或做法補上,而資料夾這種東西,猜錯了不是 diff 一下就能看出來的。
worktree 跟 rebase,解決的是不同的摩擦
Rebase 處理的是「main 的紀錄乾不乾淨」;worktree 處理的是「手上這件事還沒做完,卻要同時處理另一件事」。兩個沒有誰取代誰的關係,但擺在一起用,日常開發會順很多:分支歷史靠 rebase 保持乾淨,臨時要看別的分支不用中斷手上的工作、也不用跟 stash 賭運氣。