[英]Multiple working directories with Git?
我不確定這是否受 Git 支持,但理論上它似乎對我有用。
我的工作流程經常涉及我同時在多個分支中編輯文件。 換句話說,我經常想在一個分支中打開幾個文件,而我在另一個分支中編輯另一個文件的內容。
我對此的典型解決方案是進行兩次結帳,但很遺憾我不能在它們之間共享分支和引用。 我想要的是只有兩個工作目錄由相同的.git 文件夾管理。
我知道本地 git 克隆解決方案(默認設置是硬鏈接共享對象,以及 --shared 選項,它使用原始存儲庫設置備用 object 存儲),但這些解決方案只會減少磁盤空間使用,尤其是在 --shared 的情況下,似乎充滿了危險。
有沒有辦法使用一個.git 文件夾,並有兩個工作目錄支持它? 還是 Git 被硬編碼為在任何時候只檢出一個工作目錄?
Git 2.5 提議自 2015 年 7 月起替換contrib/workdir/git-new-workdir
: git 工作樹
請參閱Junio C Hamano ( gitster
)的提交 68a2e6a 。
contrib/workdir/git-new-workdir
的替代品,它不依賴於符號鏈接,並通過讓借用者和借用者相互了解來使對象和引用的共享更安全。
請參閱提交 799767cc9 (Git 2.5rc2)
這意味着您現在可以執行git worktree add <path> [<branch>]
創建
<path>
並將<branch>
簽入其中。 新的工作目錄鏈接到當前存儲庫,共享除工作目錄特定文件(如 HEAD、索引等)之外的git worktree
部分添加:git 存儲庫可以支持多個工作樹,允許您一次檢查多個分支。
使用git worktree add
,新的工作樹與存儲庫相關聯。這個新的工作樹被稱為“鏈接工作樹”,而不是由“
git init
”或“git clone
”准備的“主工作樹” 。
一個存儲庫有一個主要的工作樹(如果它不是一個裸存儲庫)和零個或多個鏈接的工作樹。
細節:
每個鏈接的工作樹在存儲庫的
$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
目錄(或$GIT_DIR/worktrees/test-next1
如果test-next
已經被采用)。在鏈接的工作樹中:
$GIT_DIR
設置為指向這個私有目錄(例如/path/main/.git/worktrees/test-next
在示例中)和$GIT_COMMON_DIR
設置為指向主工作樹的$GIT_DIR
(例如/path/main/.git
)。這些設置在位於鏈接工作樹頂部目錄的
.git
文件中進行。完成鏈接工作樹后,您只需將其刪除即可。
存儲庫中工作樹的管理文件最終將被自動刪除(請參閱gc.pruneworktreesexpire
配置中的git config
),或者您可以在主或任何鏈接的工作樹中運行git worktree prune
以清理任何陳舊的管理文件。
警告:還有一個git worktree
“BUGS”部分需要注意。
對子模塊的支持不完整。
不建議對一個超級項目進行多次檢查。
注意:使用 git 2.7rc1(2015 年 11 月),您可以列出您的工作樹。
參見提交BB9C03B , 提交92718B7 , 提交5193490 , 提交1CEB7F9 , 提交1CEB7F9 , 提交5193490 , 提交1CEB7F9 , 提交1CEB7F9 (2015年10月8日), 提案92718B7 , 提交5193490,1CE 1CEB7901CECTB79.SCEB79B79 (08) 1CE 1CEB7F9 ( 提交 1ceb7f9 (2015 年 10 月 8 日)、 提交 1ceb7f9 (2015 年 10 月 8 日)和提交 ac6c561 (2015 年 10 月 2 日),作者為Michael Rappazzo ( rappazzo
) 。
(由Junio C Hamano -- gitster
--在提交 a46dcfb中合並,2015 年 10 月 26 日)
worktree
:添加“list
”命令
'
git worktree list
' 遍歷工作樹列表,並輸出工作樹的詳細信息,包括工作樹的路徑、當前簽出的修訂和分支,以及工作樹是否是裸露的。$ git 工作樹列表 /path/to/bare-source (bare) /path/to/linked-worktree abcd1234 [master] /path/to/other-linked-worktree 1234abc (分離 HEAD)
還有瓷器格式選項可用。
瓷器格式的每個屬性都有一行。
- 屬性以 label 和由單個空格分隔的值列出。
- Boolean 屬性(如“裸”和“分離”)僅作為 label 列出,並且僅當且僅當值為 true 時才存在。
- 空行表示工作樹的結束
例如:
$ 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
注意:如果移動工作樹文件夾,則需要手動更新gitdir
文件。
請參閱Nguyễn Thai Ngọc Duy ( pclouds
)的提交618244e (2016 年 1 月 22 日)和提交 d4cddd6 (2016 年 1 月 18 日)。
幫助者: Eric Sunshine ( sunshineco
) 。
(由Junio C Hamano -- gitster
--在提交 d0a1cbc中合並,2016 年 2 月 10 日)
git 2.8(2016 年 3 月)中的新文檔將包括:
如果移動鏈接的工作樹,則需要更新條目目錄中的 '
gitdir
' 文件。
例如,如果鏈接的工作樹移動到/newpath/test-next
並且其.git
文件指向/path/main/.git/worktrees/test-next
,則更新/path/main/.git/worktrees/test-next/gitdir
改為引用/newpath/test-next
。
刪除分支時要小心:在 git 2.9(2016 年 6 月)之前,您可以刪除另一個工作樹中正在使用的分支。
當使用“
git worktree
”功能時,“git branch -d
”允許刪除在另一個工作樹中簽出的分支。
請參閱Kazuki Yamaguchi rhenium
rhenium ) 的提交 f292244 (2016 年 3 月 29 日)。
幫助者: Eric Sunshine ( sunshineco
) 。
(由Junio C Hamano -- gitster
--在提交 4fca4e3中合並,2016 年 4 月 13 日)
branch -d
:拒絕刪除當前已簽出的分支
當當前工作樹檢出一個分支時,禁止刪除該分支。
但是,當分支僅由其他工作樹簽出時,刪除錯誤會成功。
使用find_shared_symref()
檢查分支是否正在使用,而不僅僅是與當前工作樹的 HEAD 進行比較。
同樣,在 git 2.9(2016 年 6 月)之前,重命名在另一個工作樹中簽出的分支不會調整所述另一個工作樹中的符號 HEAD。
請參閱Kazuki Yamaguchi rhenium
rhenium ) 的提交18eb3a9 (2016 年 4 月 8 日)和提交 70999e9 、 提交 2233066 (2016 年 3 月 27 日)。
(由Junio C Hamano -- gitster
--在提交 741a694中合並,2016 年 4 月 18 日)
branch -m
:更新所有每個工作樹的 HEAD
重命名分支時,當前只更新當前工作樹的 HEAD,但必須更新所有指向舊分支的工作樹的 HEAD。
這是當前的行為,/path/to/wt 的 HEAD 沒有更新:
% git worktree list /path/to 2c3c5f2 [master] /path/to/wt 2c3c5f2 [oldname] % git branch -m master master2 % git worktree list /path/to 2c3c5f2 [master2] /path/to/wt 2c3c5f2 [oldname] % git branch -m oldname newname % git worktree list /path/to 2c3c5f2 [master2] /path/to/wt 0000000 [oldname]
此補丁通過在重命名分支時更新所有相關的工作樹 HEAD 來解決此問題。
git 2.10 (Q3 2016) 正式支持鎖定機制
See commit 080739b , commit 6d30862 , commit 58142c0 , commit 346ef53 , commit 346ef53 , commit 58142c0 , commit 346ef53 , commit 346ef53 (13 Jun 2016), and commit 984ad9e , commit 6835314 (03 Jun 2016) by Nguyễn Thái Ngọc Duy ( pclouds
) .
推薦人: Eric Sunshine ( sunshineco
) 。
(由Junio C Hamano -- gitster
--在提交 2c608e0中合並,2016 年 7 月 28 日)
git worktree lock [--reason <string>] <worktree>
git worktree unlock <worktree>
如果鏈接的工作樹存儲在不總是掛載的便攜式設備或網絡共享上,您可以通過發出
git worktree lock
命令來防止其管理文件被修剪,可選擇指定--reason
來解釋工作樹被鎖定的原因.
<worktree>
:如果工作樹路徑中的最后一個路徑組件在工作樹中是唯一的,則可以用來識別工作樹。
例如,如果您只需要在“/abc/def/ghi
”和“/abc/def/ggg
”處工作樹,那么“ghi
”或“def/ghi
”就足以指向前一個工作樹。
Git 2.13(2017 年第二季度)在Nguyễn Thái Ngọc Duy ( pclouds
)的提交 507e6e9 (2017 年 4 月 12 日)中添加lock
選項。
推薦人:大衛·泰勒 ( dt
) 。
幫助者: Jeff King ( peff
) 。
(由Junio C Hamano -- gitster
--在提交 e311597中合並,2017 年 4 月 26 日)
允許在創建工作樹后立即鎖定它。
這有助於防止“git worktree add; git worktree lock
”和“git worktree prune
”之間的競爭。
所以git worktree add' --lock
相當於 git git worktree add
之后的 git git worktree lock
,但沒有競爭條件。
Git 2.17+(2018 年第二季度)添加git worktree move
/ git worktree remove
:請參閱此答案。
Git 2.19(2018 年第三季度)添加“ --quiet
”選項以使“ git worktree add
”不那么冗長。
請參閱Elia Pinto ( devzero2000
)的提交 371979c (2018 年 8 月 15 日)。
幫助者:Martin Ågren martin.agren@gmail.com、 Duy Nguyen ( pclouds
)和Eric Sunshine ( sunshineco
) 。
(由Junio C Hamano -- gitster
--在提交 a988ce9中合並,2018 年 8 月 27 日)
worktree
:添加--quiet
選項
與其他
git
命令一樣,將“--quiet
”選項添加到git worktree
。
'add
' 是唯一受它影響的命令,因為除了 'list
' 之外的所有其他命令當前默認是靜默的。
請注意,“ git worktree add
”用於執行“使用 stat 然后mkdir
查找可用名稱”,這很容易出現競爭。
Git 2.22(2019 年第二季度)已通過使用mkdir
並在循環中對EEXIST
做出反應來解決此問題。
請參閱Michal Suchanek ( hramrach
)的提交 7af01f2 (2019 年 2 月 20 日)。
(由Junio C Hamano -- gitster
--在提交 20fe798中合並,2019 年 4 月 9 日)
worktree
:修復worktree add
種族
Git 運行 stat 循環以查找可用的工作樹名稱,然后對找到的名稱執行
mkdir
。
將其轉為mkdir
循環以避免再次調用 worktree add 找到相同的空閑名稱並首先創建目錄。
Git 2.22(2019 年第二季度)修復了判斷 Git 存儲庫是否有工作樹保護“ git branch -D
”的邏輯。
對於具有不尋常名稱的存儲庫,此邏輯的實現被破壞了,不幸的是,如今這已成為子模塊的規范。
請參閱Jonathan Tan ( jhowtan
)的提交 f3534c9 (2019 年 4 月 19 日)。
(由Junio C Hamano -- gitster
--在提交 ec2642a中合並,2019 年 5 月 8 日)
工作
is_bare
worktree
當“
git branch -D <name>
”運行時,Git 通常首先檢查該分支當前是否已簽出。
但是,如果該存儲庫的 Git 目錄不在“<repo>/.git
”中,則不會執行此檢查,如果該存儲庫是一個子模塊,其 Z0BCC70105AD279503E31FE7B3F4/module 存儲為“super/.git/modules/<repo>
.git7B6”super/.git/modules/<repo>
”,例如。
這會導致分支被刪除,即使它已被簽出。這是因為
get_main_worktree()
僅在工作樹的路徑未以“/.git
”結尾時才使用啟發式方法在工作worktree.c
設置is_bare
,否則不會裸露。
這個is_bare
代碼是在92718b7 (“worktree
: add details to the worktree struct”, 2015-10-08, Git v2.7.0-rc0)中引入的,遵循pre-core.bare
啟發式。這個補丁做了兩件事:
- 教
get_main_worktree()
改為使用is_bare_repository()
,在7d1864c中引入(“介紹 is_bare_repository() 和 core.bare 配置變量”,2007-01-07,Git v1.5.0-rc1)並在e90fdc3中更新(“清理工作” -樹處理”,2007-08-01,Git v1.5.3-rc4)。
這解決了上面描述的“git branch -D <name>
”問題。
然而...- 如果存儲庫具有
core.bare=1
但“git
”命令正在從其輔助工作樹之一運行,is_bare_repository()
返回 false (這很好,因為有可用的工作樹)。
並且,當主工作樹是裸的時將其視為非裸會導致問題:例如,無法從主工作樹的 HEAD 引用的輔助工作樹中刪除分支,即使該主工作樹是裸的。為了避免這種情況,在設置
is_bare
core.bare
如果core.bare=1
,信任它,否則,使用is_bare_repository()
。
借助 Git 2.29(2020 年第四季度),“工作樹” worktree
可以更好地確定工作樹路徑。
請參閱Eric Sunshine ( sunshineco
) 的commit 918d8ff 、 commit 1c4854e 、 commit 246756f 、 commit 62573a5 (2020 年 7 月 31 日)。
(由Junio C Hamano -- gitster
--在提交 197253e中合並,2020 年 8 月 10 日)
worktree
:丟棄虛假和不必要的路徑修改簽字人:Eric Sunshine
.git/worktrees/<id>/gitdir
的內容必須是“/path/to/worktree/.git
”形式的路徑。
任何其他內容都表明“gitdir
”文件已損壞。要確定工作樹本身的路徑,只需去掉“
/.git
”后綴,這確實是從一開始就確定工作樹路徑的方式。但是, 5193490442 (“
worktree
:添加 function 以獲取工作樹詳細信息”,2015-10-08,Git v2.7.0-rc0 - 以一種神秘的方式合並在第 7 批中)擴展了路徑操作。
如果它無法從路徑中刪除“/.git
”,則它將當前工作目錄報告為鏈接工作樹的路徑:if (,strbuf_strip_suffix(&worktree_path. "/;git")) { strbuf_reset(&worktree_path), strbuf_add_absolute_path(&worktree_path. ";"), strbuf_strip_suffix(&worktree_path. "/;"); }
這個邏輯顯然是假的; 它永遠不可能是普遍正確的行為。 它在5193490442中憑空出現,既沒有解釋也沒有測試來說明它是可取的情況。
引入此邏輯可能是為了以某種方式處理損壞的“
gitdir
”文件,以便它返回某種有意義的值,但返回當前工作目錄沒有幫助。 事實上,這是非常具有誤導性的(除了在當前目錄是“gitdir
”條目已損壞的工作樹的一種特定情況下)。
此外,向用戶報告損壞的值,而不是撒謊並完全隱藏它,更有幫助,因為它可能有助於診斷問題。因此,放棄這個虛假的路徑修改並將邏輯恢復為僅剝離“
/.git
”的原始行為。
git
發行版附帶一個名為git-new-workdir
的貢獻腳本。 您可以按如下方式使用它:
git-new-workdir project-dir new-workdir branch
其中 project-dir 是包含.git
存儲庫的目錄的名稱。 此腳本創建另一個.git
目錄,其中包含許多指向原始目錄的符號鏈接,除了無法共享的文件(如當前分支),允許您在兩個不同的分支中工作。
這聽起來有點脆弱,但這是一個選擇。
我遇到了這個問題,希望能找到我在這里找不到的解決方案。 所以現在我確實找到了我需要的東西,我決定在這里發布給其他人。
警告:如果您需要同時編輯多個分支,例如 OP 狀態,這可能不是一個好的解決方案。 這是為了同時簽出多個您不打算編輯的分支。 (由一個.git 文件夾支持的多個工作目錄。)
自從我第一次提出這個問題以來,我學到了一些東西:
什么是“裸倉庫”。 它本質上是.git
目錄的內容,而不是位於工作樹中。
您可以使用git
選項--git-dir=
在命令行上指定您正在使用的存儲庫的位置( .git
目錄的位置)
您可以使用--work-tree=
指定工作副本的位置這一事實
什么是“鏡像回購”。
最后一點是一個非常重要的區別。 我實際上並不想處理repo,我只需要同時簽出不同分支和/或標簽的副本。 實際上,我需要保證分支最終不會與我的遠程分支不同。 所以鏡子對我來說是完美的。
所以對於我的用例,我得到了我需要的東西:
git clone --mirror <remoteurl> <localgitdir> # Where localgitdir doesn't exist yet
mkdir firstcopy
mkdir secondcopy
git --git-dir=<localgitdir> --work-tree=firstcopy checkout -f branch1
git --git-dir=<localgitdir> --work-tree=secondcopy checkout -f branch2
關於這一點的最大警告是兩個副本沒有單獨的 HEAD。 所以在上述之后,運行git --git-dir=<localgitdir> --work-tree=firstcopy status
將顯示從 branch2 到 branch1 的所有差異作為未提交的更改 - 因為 HEAD 指向 branch2。 (這就是為什么我使用-f
選項來checkout
,因為我實際上根本不打算在本地進行任何更改。只要我使用-f
選項,我就可以檢查任何工作樹的任何標記或分支。 )
對於我在同一台計算機上共存多個結帳而無需編輯它們的用例,這非常有效。 我不知道是否有任何方法可以在沒有腳本的情況下為多個工作樹設置多個 HEAD,例如其他答案中所涵蓋的腳本,但我希望無論如何這對其他人有幫助。
我能想到的唯一解決方案是克隆兩個目錄並將它們添加為彼此的遠程存儲庫。 然后,您可以繼續將內容從已更改的一個拉到另一個,而無需實際將任何內容推送到遠程存儲庫。
我假設您想要有兩個工作目錄而不是遠程的兩個克隆,因為您不想將一些分支推送到遠程。 否則,你的遙控器的兩個克隆就可以正常工作 - 你只需要做一些推拉來保持所有三個同步。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.