Git
英文 ▾ 主題 ▾ 最新版本 ▾ git-worktree 最後更新於 2.43.1

名稱

git-worktree - 管理多個工作樹

概要

git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]
		   [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]
git worktree list [-v | --porcelain [-z]]
git worktree lock [--reason <string>] <worktree>
git worktree move <worktree> <new-path>
git worktree prune [-n] [-v] [--expire <expire>]
git worktree remove [-f] <worktree>
git worktree repair [<path>…​]
git worktree unlock <worktree>

描述

管理附加到同一個儲存庫的多個工作樹。

一個 git 儲存庫可以支援多個工作樹,讓您一次簽出多個分支。使用 git worktree add 時,新的工作樹會與儲存庫建立關聯,同時會有一些額外的中繼資料,用來區分該工作樹與同一個儲存庫中的其他工作樹。工作樹連同這些中繼資料,稱為「工作樹」。

這個新的工作樹稱為「連結工作樹」,與 git-init[1]git-clone[1] 準備的「主要工作樹」相對。一個儲存庫有一個主要工作樹(如果不是裸儲存庫),以及零或多個連結工作樹。當您完成連結工作樹時,請使用 git worktree remove 將其移除。

最簡單的形式是,git worktree add <路徑> 會自動建立一個新的分支,其名稱是 <路徑> 的最後一個組成部分,如果您打算處理一個新主題,這會很方便。例如,git worktree add ../hotfix 會建立新的 hotfix 分支,並在路徑 ../hotfix 簽出。若要在新的工作樹中使用現有的分支,請使用 git worktree add <路徑> <分支>。另一方面,如果您只是打算進行一些實驗性的變更或執行測試,而不干擾現有的開發,通常可以建立一個與任何分支無關的拋棄式工作樹。例如,git worktree add -d <路徑> 會建立一個新的工作樹,並在與目前分支相同的 commit 處分離 HEAD

如果刪除工作樹時未使用 git worktree remove,則其相關的管理檔案(位於儲存庫中,請參閱下方的「詳細資訊」)最終會自動移除(請參閱 git-config[1] 中的 gc.worktreePruneExpire),或者您可以在主要工作樹或任何連結工作樹中執行 git worktree prune,來清除任何過期的管理檔案。

如果連結工作樹的工作樹儲存在可攜式裝置或網路共用上,而這些裝置或網路共用並非永遠掛載,您可以發出 git worktree lock 命令,防止其管理檔案被修剪,還可以選擇性地指定 --reason 來解釋鎖定工作樹的原因。

命令

add <路徑> [<commit-ish>]

<路徑> 建立一個工作樹,並將 <commit-ish> 簽出到其中。新的工作樹會連結到目前的儲存庫,除了每個工作樹的檔案(例如 HEADindex 等)之外,會共用所有項目。為了方便起見,<commit-ish> 可以是單獨的「-」,這與 @{-1} 同義。

如果 <commit-ish> 是一個分支名稱(稱之為 <分支>)且未找到,並且未使用 -b-B--detach,但剛好在一個遠端(稱之為 <遠端>)中有一個追蹤分支與名稱相符,則視為等同於

$ git worktree add --track -b <branch> <path> <remote>/<branch>

如果分支存在於多個遠端中,而且其中一個遠端是使用 checkout.defaultRemote 組態變數命名的,則我們會使用該遠端來進行消除歧義,即使 <分支> 在所有遠端中並非唯一也是如此。將其設定為例如 checkout.defaultRemote=origin,以便在 <分支> 不明確但在 origin 遠端上存在時,永遠從該處簽出遠端分支。另請參閱 git-config[1] 中的 checkout.defaultRemote

如果省略 <commit-ish>,且未使用 -b-B--detach,則為了方便起見,新的工作樹會與一個以 $(basename <路徑>) 命名的分支(稱之為 <分支>)建立關聯。如果 <分支> 不存在,則會自動建立一個以 HEAD 為基礎的新分支,如同給定 -b <分支> 一樣。如果 <分支> 存在,則會在新的工作樹中簽出,如果未在其他任何地方簽出,否則命令會拒絕建立工作樹(除非使用 --force)。

如果省略 <commit-ish>,且未使用 --detach--orphan,且沒有有效的本機分支(如果指定 --guess-remote,則沒有遠端分支),則為了方便起見,新的工作樹會與一個名為 <分支> 的新未生分支建立關聯(如果未使用 -b-B,則在 $(basename <路徑>) 之後),如同將 --orphan 傳遞給命令一樣。如果儲存庫有一個遠端且使用 --guess-remote,但沒有遠端或本機分支存在,則命令會失敗,並顯示警告,提醒使用者先從其遠端擷取(或使用 -f/--force 來覆寫)。

list

列出每個工作樹的詳細資料。主要工作樹會先列出,接著是每個連結的工作樹。輸出的詳細資料包括工作樹是否為裸儲存庫、目前簽出的修訂版本、目前簽出的分支(如果沒有則為「分離的 HEAD」)、如果工作樹已鎖定則為「locked」,如果工作樹可以由 prune 命令修剪則為「prunable」。

lock

如果工作樹位於可攜式裝置或網路共用上,而這些裝置或網路共用並非永遠掛載,則鎖定工作樹,防止其管理檔案被自動修剪。這也會防止其被移動或刪除。選擇性地使用 --reason 指定鎖定的原因。

move

將工作樹移動到新的位置。請注意,無法使用此命令移動包含子模組的主要工作樹或連結工作樹。(但是,如果您手動移動主要工作樹,則 git worktree repair 命令可以重新建立與連結工作樹的連線。)

prune

修剪 $GIT_DIR/worktrees 中的工作樹資訊。

remove

移除工作樹。只有乾淨的工作樹(沒有未追蹤的檔案,且追蹤的檔案沒有修改)可以移除。可以使用 --force 移除不乾淨的工作樹或包含子模組的工作樹。無法移除主要工作樹。

repair [<路徑>…​]

盡可能修復工作樹管理檔案,如果這些檔案因外部因素而損毀或過期。

例如,如果移動主要工作樹(或裸儲存庫),連結工作樹將無法找到它。在主要工作樹中執行 repair 將重新建立從連結工作樹回到主要工作樹的連線。

同樣地,如果移動連結工作樹的工作樹時未使用 git worktree move,則主要工作樹(或裸儲存庫)將無法找到它。在最近移動的工作樹中執行 repair 將重新建立連線。如果移動多個連結工作樹,從任何工作樹執行 repair,並以每個樹狀結構的新 <路徑> 作為引數,將重新建立與所有指定路徑的連線。

如果主要工作樹和連結工作樹都已手動移動,則在主要工作樹中執行 repair 並指定每個連結工作樹的新 <路徑>,將重新建立兩個方向的所有連線。

unlock

解除鎖定工作樹,使其可以被修剪、移動或刪除。

選項

-f
--force

預設情況下,當 <commit-ish> 是分支名稱且已被另一個工作樹簽出,或 <路徑> 已指派給某些工作樹但遺失(例如,如果手動刪除 <路徑>),則 add 會拒絕建立新的工作樹。此選項會覆寫這些安全措施。若要新增遺失但已鎖定的工作樹路徑,請指定 --force 兩次。

除非指定 --force 兩次,否則 move 會拒絕移動鎖定的工作樹。如果目的地已指派給其他某些工作樹但遺失(例如,如果手動刪除 <新路徑>),則 --force 允許繼續移動;如果目的地已鎖定,則使用 --force 兩次。

除非使用 --force,否則 remove 會拒絕移除不乾淨的工作樹。若要移除鎖定的工作樹,請指定 --force 兩次。

-b <新分支>
-B <新分支>

使用 add,建立一個名為 <new-branch> 的新分支,起始於 <commit-ish>,並將 <new-branch> 檢出到新的工作目錄中。如果省略 <commit-ish>,則預設為 HEAD。預設情況下,如果新分支已經存在,-b 會拒絕建立新的分支。 -B 會覆寫這個安全機制,將 <new-branch> 重設為 <commit-ish>

-d
--detach

使用 add,在新工作目錄中分離 HEAD。請參閱 git-checkout[1] 中的「分離 HEAD」部分。

--[no-]checkout

預設情況下,add 會檢出 <commit-ish>,但是,可以使用 --no-checkout 來抑制檢出,以便進行自訂設定,例如設定稀疏檢出。請參閱 git-read-tree[1] 中的「稀疏檢出」部分。

--[no-]guess-remote

使用 worktree add <path>,如果沒有指定 <commit-ish>,則不會從 HEAD 建立新分支,而是如果恰好有一個遠端追蹤分支與 <path> 的基本名稱匹配,則會將新分支建立在該遠端追蹤分支的基礎上,並將該遠端追蹤分支標記為新分支的「上游」。

也可以透過使用 worktree.guessRemote 設定選項將此設定為預設行為。

--[no-]track

當建立新分支時,如果 <commit-ish> 是一個分支,則將其標記為新分支的「上游」。如果 <commit-ish> 是遠端追蹤分支,則此為預設行為。請參閱 git-branch[1] 中的 --track 以了解詳細資訊。

--lock

在建立後保持工作目錄鎖定。這等同於在 git worktree add 之後執行 git worktree lock,但不會出現競爭條件。

-n
--dry-run

使用 prune 時,不移除任何內容;僅報告將移除的內容。

--orphan

使用 add 時,使新的工作目錄和索引為空,並將工作目錄與名為 <new-branch> 的新未出生分支關聯。

--porcelain

使用 list 時,以易於腳本解析的格式輸出。此格式將在不同的 Git 版本中保持穩定,且不受使用者設定的影響。建議將此選項與 -z 結合使用。請參閱下方以了解詳細資訊。

-z

當使用 --porcelainlist 時,以 NUL 而非換行符號終止每一行。這使得當工作目錄路徑包含換行字元時,可以解析輸出。

-q
--quiet

使用 add 時,抑制回饋訊息。

-v
--verbose

使用 prune 時,報告所有移除操作。

使用 list 時,輸出有關工作目錄的其他資訊(請參閱下方)。

--expire <time>

使用 prune 時,僅過期未使用且早於 <time> 的工作目錄。

使用 list 時,如果遺失的工作目錄早於 <time>,則將其標記為可修剪。

--reason <string>

使用 lockadd --lock 時,說明為什麼鎖定工作目錄。

<worktree>

工作目錄可以通過路徑來識別,可以使用相對或絕對路徑。

如果工作目錄路徑中最後的路徑元件在工作目錄之間是唯一的,則可以使用它來識別工作目錄。例如,如果您只有兩個工作目錄,分別位於 /abc/def/ghi/abc/def/ggg,則 ghidef/ghi 就足以指向前一個工作目錄。

參考

當使用多個工作目錄時,某些參考會在所有工作目錄之間共享,但其他參考則是特定於個別工作目錄的。其中一個範例是 HEAD,每個工作目錄的 HEAD 都不同。此章節將說明共享規則以及如何從一個工作目錄存取另一個工作目錄的參考。

一般來說,所有虛擬參考都是每個工作目錄特有的,而所有以 refs/ 開頭的參考則是共享的。虛擬參考是指直接位於 $GIT_DIR 下(而不是在 $GIT_DIR/refs 內)的參考,例如 HEAD。但也有例外:refs/bisectrefs/worktreerefs/rewritten 內的參考不會共享。

每個工作目錄特有的參考仍然可以通過兩個特殊路徑 main-worktreeworktrees 從另一個工作目錄存取。前者可存取主要工作目錄的每個工作目錄特有的參考,而後者可存取所有連結的工作目錄。

例如,main-worktree/HEADmain-worktree/refs/bisect/good 解析出來的值分別與主要工作目錄的 HEADrefs/bisect/good 相同。同樣地,worktrees/foo/HEADworktrees/bar/refs/bisect/bad 分別與 $GIT_COMMON_DIR/worktrees/foo/HEAD$GIT_COMMON_DIR/worktrees/bar/refs/bisect/bad 相同。

要存取參考,最好不要直接查看 $GIT_DIR 內部。而是使用 git-rev-parse[1]git-update-ref[1] 等命令,這些命令可以正確處理參考。

設定檔

預設情況下,儲存庫的 config 檔案在所有工作目錄之間共享。如果通用設定檔中存在設定變數 core.barecore.worktree,並且 extensions.worktreeConfig 已停用,則它們將僅適用於主要工作目錄。

為了擁有特定於工作目錄的設定,您可以啟用 worktreeConfig 擴充功能,例如:

$ git config extensions.worktreeConfig true

在此模式下,特定設定會保留在 git rev-parse --git-path config.worktree 所指向的路徑中。您可以使用 git config --worktree 在此檔案中新增或更新設定。較舊的 Git 版本將拒絕存取具有此擴充功能的儲存庫。

請注意,在此檔案中,core.barecore.worktree 的例外情況已不再存在。如果它們存在於 $GIT_DIR/config 中,您必須將它們移至主要工作目錄的 config.worktree。您也可以利用此機會檢查並移動您不想與所有工作目錄共享的其他設定。

  • core.worktree 永遠不應該共享。

  • 如果值為 core.bare=true,則 core.bare 不應共享。

  • 除非您確定始終對所有工作目錄使用稀疏檢出,否則 core.sparseCheckout 不應共享。

請參閱 git-config[1] 中關於 extensions.worktreeConfig 的說明文件以了解更多詳細資訊。

詳細資訊

每個連結的工作目錄在儲存庫的 $GIT_DIR/worktrees 目錄中都有一個私有子目錄。私有子目錄的名稱通常是連結的工作目錄路徑的基本名稱,可能會附加一個數字使其唯一。例如,當 $GIT_DIR=/path/main/.git 時,命令 git worktree add /path/other/test-next next 會在 /path/other/test-next 中建立連結的工作目錄,並且也會建立 $GIT_DIR/worktrees/test-next 目錄(如果 test-next 已被使用,則為 $GIT_DIR/worktrees/test-next1)。

在連結的工作目錄中,$GIT_DIR 設定為指向此私有目錄(例如範例中的 /path/main/.git/worktrees/test-next),而 $GIT_COMMON_DIR 設定為指向回主要工作目錄的 $GIT_DIR(例如 /path/main/.git)。這些設定是在位於連結工作目錄頂層目錄的 .git 檔案中進行的。

通過 git rev-parse --git-path 的路徑解析使用 $GIT_DIR$GIT_COMMON_DIR,具體取決於路徑。例如,在連結的工作目錄中,git rev-parse --git-path HEAD 會返回 /path/main/.git/worktrees/test-next/HEAD(而不是 /path/other/test-next/.git/HEAD/path/main/.git/HEAD),而 git rev-parse --git-path refs/heads/master 會使用 $GIT_COMMON_DIR 並返回 /path/main/.git/refs/heads/master,因為參考在所有工作目錄之間共享,refs/bisectrefs/worktreerefs/rewritten 除外。

請參閱 gitrepository-layout[5] 以了解更多資訊。經驗法則是,當您需要直接存取 $GIT_DIR 內部的內容時,不要假設路徑是否屬於 $GIT_DIR$GIT_COMMON_DIR。使用 git rev-parse --git-path 來取得最終路徑。

如果您手動移動連結的工作目錄,則需要在條目的目錄中更新 gitdir 檔案。例如,如果將連結的工作目錄移動到 /newpath/test-next 且其 .git 檔案指向 /path/main/.git/worktrees/test-next,則請將 /path/main/.git/worktrees/test-next/gitdir 更新為參考 /newpath/test-next。更好的方法是,執行 git worktree repair 來自動重新建立連線。

為了防止 $GIT_DIR/worktrees 條目被修剪(在某些情況下很有用,例如當條目的工作目錄儲存在可攜式裝置上時),請使用 git worktree lock 命令,該命令會將名為 locked 的檔案新增到條目的目錄中。該檔案以純文字形式包含原因。例如,如果連結的工作目錄的 .git 檔案指向 /path/main/.git/worktrees/test-next,則名為 /path/main/.git/worktrees/test-next/locked 的檔案會阻止 test-next 條目被修剪。請參閱 gitrepository-layout[5] 以了解詳細資訊。

當啟用 extensions.worktreeConfig 時,會在讀取 .git/config 後讀取設定檔 .git/worktrees/<id>/config.worktree

列表輸出格式

worktree list 命令有兩種輸出格式。預設格式在一行中顯示詳細資訊,並以欄位顯示。例如

$ git worktree list
/path/to/bare-source            (bare)
/path/to/linked-worktree        abcd1234 [master]
/path/to/other-linked-worktree  1234abc  (detached HEAD)

該命令還根據每個工作目錄的狀態顯示註釋。這些註釋是:

  • locked,如果工作目錄被鎖定。

  • prunable,如果工作目錄可以透過 git worktree prune 修剪。

$ git worktree list
/path/to/linked-worktree    abcd1234 [master]
/path/to/locked-worktree    acbd5678 (brancha) locked
/path/to/prunable-worktree  5678abc  (detached HEAD) prunable

對於這些註釋,可能還會提供原因,並且可以使用詳細模式查看原因。然後註釋會移到下一行,縮進,後面接著其他資訊。

$ git worktree list --verbose
/path/to/linked-worktree              abcd1234 [master]
/path/to/locked-worktree-no-reason    abcd5678 (detached HEAD) locked
/path/to/locked-worktree-with-reason  1234abcd (brancha)
	locked: worktree path is mounted on a portable device
/path/to/prunable-worktree            5678abc1 (detached HEAD)
	prunable: gitdir file points to non-existent location

請注意,如果提供了其他資訊,則註釋會移到下一行,否則它會與工作目錄本身位於同一行。

Porcelain 格式

瓷器格式的每個屬性佔一行。如果給定 -z 選項,則行會以 NUL 而不是換行符結束。屬性會列出標籤和值,兩者之間以單一空格分隔。布林屬性(如 baredetached)僅列出標籤,並且僅在值為 true 時才存在。某些屬性(如 locked)可以僅列出標籤,也可以根據是否有原因而列出值。工作樹的第一個屬性始終是 worktree,空行表示記錄結束。例如:

$ git worktree list --porcelain
worktree /path/to/bare-source
bare

worktree /path/to/linked-worktree
HEAD abcd1234abcd1234abcd1234abcd1234abcd1234
branch refs/heads/master

worktree /path/to/other-linked-worktree
HEAD 1234abc1234abc1234abc1234abc1234abc1234a
detached

worktree /path/to/linked-worktree-locked-no-reason
HEAD 5678abc5678abc5678abc5678abc5678abc5678c
branch refs/heads/locked-no-reason
locked

worktree /path/to/linked-worktree-locked-with-reason
HEAD 3456def3456def3456def3456def3456def3456b
branch refs/heads/locked-with-reason
locked reason why is locked

worktree /path/to/linked-worktree-prunable
HEAD 1233def1234def1234def1234def1234def1234b
detached
prunable gitdir file points to non-existent location

除非使用 -z 選項,否則鎖定原因中的任何「不常見」字元(例如換行符)都會被跳脫,並且整個原因會像配置變數 core.quotePath 所解釋的那樣被引號括起來(請參閱 git-config[1])。例如:

$ git worktree list --porcelain
...
locked "reason\nwhy is locked"
...

範例

您正在進行重構工作,而您的老闆走進來要求您立即修復某個問題。您通常可能會使用 git-stash[1] 來暫時存放您的變更,但是,您的工作目錄處於非常混亂的狀態(包含新的、移動的和刪除的檔案,以及散落在各處的其他零碎內容),因此您不想冒任何干擾它的風險。相反地,您可以建立一個臨時連結的工作樹來進行緊急修復,完成後將其移除,然後繼續您先前的重構工作。

$ git worktree add -b emergency-fix ../temp master
$ pushd ../temp
# ... hack hack hack ...
$ git commit -a -m 'emergency fix for boss'
$ popd
$ git worktree remove ../temp

錯誤

一般來說,多重檢出的功能仍處於實驗階段,並且對子模組的支援並不完整。不建議對一個超級專案進行多次檢出。

GIT

屬於 git[1] 套件的一部分

scroll-to-top