簡體中文 ▾ 主題 ▾ 最新版本 ▾ git-worktree 上次更新於 2.48.0

名稱

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 命令,可以將新的工作區與倉庫關聯起來,並附帶額外的元資料,以區分同一倉庫中的不同工作區。工作區連同其元資料被稱為“工作區”。

這個新的工作區被稱為“連結工作區”(linked worktree),與 git-init[1]git-clone[1] 準備的“主工作區”(main worktree)相對。一個倉庫有一個主工作區(如果它不是裸倉庫),以及零個或多個連結工作區。當您完成使用連結工作區後,請使用 git worktree remove 命令將其刪除。

最簡單的形式是,git worktree add <path> 會自動建立一個新分支,其名稱為 <path> 的最後一個元件,如果您打算處理一個新主題,這會很方便。例如,git worktree add ../hotfix 會建立新分支 hotfix 並將其檢出到路徑 ../hotfix。如果要在新的工作區中處理現有分支,請使用 git worktree add <path> <branch>。另一方面,如果您只是計劃進行一些實驗性更改或測試,而不想幹擾現有開發,那麼建立一個不與任何分支關聯的*一次性*工作區通常會很方便。例如,git worktree add -d <path> 會建立一個新的工作區,其 HEAD 處於分離狀態,指向與當前分支相同的提交。

如果一個工作區在沒有使用 git worktree remove 的情況下被刪除,那麼其相關的管理檔案(位於倉庫中,參見下文“詳情”)最終會被自動移除(參見 git-config[1] 中的 gc.worktreePruneExpire),或者您可以在主工作區或任何連結工作區中執行 git worktree prune 來清理任何過時的管理檔案。

如果連結工作區的工作目錄儲存在便攜裝置或網路共享上,並且不總是掛載的,您可以透過執行 git worktree lock 命令來防止其管理檔案被修剪,您可以選擇指定 --reason 來解釋鎖定該工作區的原因。

命令

add <path> [<commit-ish>]

<path> 建立一個工作區並檢出 <commit-ish> 到其中。新的工作區連結到當前倉庫,共享除每個工作區特有的檔案(如 HEADindex 等)之外的所有內容。為方便起見,<commit-ish> 可以是裸的“-”,這等同於 @{-1}

如果 <commit-ish> 是一個分支名稱(稱之為 <branch>)且未找到,並且沒有使用 -b-B--detach,但恰好在一個遠端倉庫(稱之為 <remote>)中存在一個同名跟蹤分支,則將其視為等同於

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

如果分支存在於多個遠端倉庫中,並且其中一個由 checkout.defaultRemote 配置變數指定,我們將使用該遠端倉庫進行消歧,即使 <branch> 在所有遠端倉庫中並非唯一。例如,將其設定為 checkout.defaultRemote=origin,以便在 <branch> 模稜兩可但存在於 origin 遠端倉庫時,總是從那裡檢出遠端分支。另請參閱 git-config[1] 中的 checkout.defaultRemote

如果省略 <commit-ish> 並且沒有使用 -b-B--detach,那麼為方便起見,新的工作區將與一個分支(稱之為 <branch>)關聯,該分支以 $(basename <path>) 命名。如果 <branch> 不存在,則會自動建立一個基於 HEAD 的新分支,如同給定了 -b <branch>。如果 <branch> 確實存在,並且沒有在其他地方被檢出,它將在新的工作區中被檢出,否則命令將拒絕建立該工作區(除非使用了 --force)。

如果省略 <commit-ish>,且沒有使用 --detach--orphan,並且沒有有效的本地分支(如果指定了 --guess-remote 則也沒有遠端分支),那麼為方便起見,新的工作區將與一個新的未出生(unborn)分支 <branch> 關聯(如果既沒有使用 -b 也沒有使用 -B,則以 $(basename <path>) 命名),如同向命令傳遞了 --orphan。如果倉庫有遠端且使用了 --guess-remote,但不存在任何遠端或本地分支,則命令將失敗併發出警告,提醒使用者首先從遠端倉庫抓取(fetch)(或透過使用 -f/--force 覆蓋)。

list

列出每個工作區的詳細資訊。主工作區首先列出,然後是每個連結工作區。輸出詳細資訊包括工作區是否為裸倉庫、當前檢出的版本、當前檢出的分支(如果沒有則是“分離的 HEAD”)、如果工作區被鎖定則顯示“locked”、如果工作區可以透過 prune 命令修剪則顯示“prunable”。

lock

如果一個工作區位於便攜裝置或網路共享上且並非總是掛載的,則將其鎖定以防止其管理檔案被自動修剪。這也能防止它被移動或刪除。您可以選擇使用 --reason 來指定鎖定的原因。

move

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

prune

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

remove

移除一個工作區。只有乾淨的工作區(沒有未跟蹤的檔案且跟蹤檔案沒有修改)才能被移除。不乾淨的工作區或包含子模組的工作區可以使用 --force 移除。主工作區不能被移除。

repair [<path>…​]

如果工作區管理檔案因外部因素而損壞或過時,則儘可能修復它們。

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

同樣,如果連結工作區的工作目錄在沒有使用 git worktree move 的情況下被移動,主工作區(或裸倉庫)將無法找到它。在最近移動的工作區內執行 repair 將重新建立連線。如果移動了多個連結工作區,從任何工作區執行 repair 並以每個工作區的新 <path> 作為引數,將重新建立與所有指定路徑的連線。

如果主工作區和連結工作區都已被手動移動或複製,那麼在主工作區中執行 repair 並指定每個連結工作區的新 <path>,將重新建立雙向的所有連線。

unlock

解鎖一個工作區,允許其被修剪、移動或刪除。

選項

-f
--force

預設情況下,當 <commit-ish> 是一個分支名稱且已被另一個工作區檢出,或者 <path> 已被分配給某個工作區但其目錄缺失(例如,如果 <path> 被手動刪除)時,add 會拒絕建立新的工作區。此選項會覆蓋這些安全保護措施。要新增一個缺失但被鎖定的工作區路徑,請指定兩次 --force

move 拒絕移動鎖定的工作區,除非指定兩次 --force。如果目標路徑已被分配給其他工作區但其目錄缺失(例如,如果 <new-path> 被手動刪除),則 --force 允許繼續移動;如果目標路徑被鎖定,則使用兩次 --force

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

-b <new-branch>
-B <new-branch>

使用 add 命令,建立一個名為 <new-branch> 的新分支,從 <commit-ish> 開始,並將 <new-branch> 檢出到新的工作區中。如果省略 <commit-ish>,則預設為 HEAD。預設情況下,如果新分支已存在,-b 會拒絕建立。-B 會覆蓋此安全措施,將 <new-branch> 重置為 <commit-ish>

-d
--detach

使用 add 命令,分離新工作區中的 HEAD。參見 git-checkout[1] 中的“分離的 HEAD”(DETACHED HEAD)。

--[no-]checkout

預設情況下,add 會檢出 <commit-ish>,但是,可以使用 --no-checkout 來抑制檢出,以便進行自定義,例如配置稀疏檢出(sparse-checkout)。參見 git-read-tree[1] 中的“稀疏檢出”(Sparse checkout)。

--[no-]guess-remote

使用 worktree add <path>,且不帶 <commit-ish> 時,如果恰好有一個遠端跟蹤分支的名稱與 <path> 的基本名稱匹配,則新分支將基於該遠端跟蹤分支建立,並將其標記為新分支的“上游”,而不是從 HEAD 建立新分支。

這也可以透過使用 worktree.guessRemote 配置選項設定為預設行為。

--[no-]relative-paths

使用相對路徑或絕對路徑(預設)連結工作區。此選項會覆蓋 worktree.useRelativePaths 配置選項,詳見 git-config[1]

使用 repair 時,如果存在絕對/相對路徑不匹配的情況,即使連結是正確的,連結檔案也會被更新。

--[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

list 命令指定 --porcelain 時,每行以 NUL 字元而不是換行符終止。這使得當工作區路徑包含換行符時,可以解析輸出。

-q
--quiet

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

-v
--verbose

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

使用 list 時,輸出關於工作區的額外資訊(詳見下文)。

--expire <time>

使用 prune 時,只使舊於 <time> 的未使用的工作區過期。

使用 list 時,如果缺失的工作區舊於 <time>,則將其標註為可修剪(prunable)。

--reason <string>

使用 lockadd --lock 時,解釋工作區被鎖定的原因。

<工作區>

工作區可以透過路徑識別,可以是相對路徑或絕對路徑。

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

引用

當使用多個工作區時,一些引用(refs)在所有工作區之間共享,但另一些則特定於單個工作區。一個例子是 HEAD,它在每個工作區中都不同。本節介紹共享規則以及如何從一個工作區訪問另一個工作區的引用。

通常,所有偽引用(pseudo refs)都是每個工作區特有的,所有以 refs/ 開頭的引用都是共享的。偽引用是像 HEAD 這樣直接位於 $GIT_DIR 下而不是 $GIT_DIR/refs 內部的引用。然而也有例外: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 不應被共享,除非您確定所有工作區都始終使用稀疏檢出(sparse checkout)。

詳見 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/worktrees/<id>/config.worktree 會在讀取 .git/config 之後讀取。

列表輸出格式

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

  • 如果工作區可以透過 git worktree prune 修剪,則顯示 prunable

$ 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

請注意,如果有附加資訊,註釋會移到下一行,否則它會留在工作區本身的同一行。

瓷器格式

瓷器格式中,每個屬性佔據一行。如果給定 -z,則行以 NUL 字元而不是換行符終止。屬性以標籤和值列出,由一個空格分隔。布林屬性(如 baredetached)僅列出標籤,並且僅當值為真時才存在。某些屬性(如 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

BUG

總的來說,多重檢出(multiple checkout)仍處於實驗階段,且對子模組的支援不完整。不建議對超級專案(superproject)進行多重檢出。

GIT

Git[1] 套件的一部分

scroll-to-top