簡體中文 ▾ 主題 ▾ 最新版本 ▾ git-read-tree 上次更新於 2.52.0

名稱

git-read-tree - 將樹資訊讀取到索引中

概要

git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>)
		[-u | -i]] [--index-output=<file>] [--no-sparse-checkout]
		(--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])

描述

<tree-ish> 指定的樹資訊讀取到索引中,但實際上並不會更新它“快取”的任何檔案。(參見:git-checkout-index[1]

可選地,它可以使用 -m 標誌將一棵樹合併到索引中,執行快進(即 2 路)合併,或 3 路合併。當與 -m 一起使用時,-u 標誌會同時根據合併結果更新工作樹中的檔案。

git read-tree 本身只執行簡單的合併。當 git read-tree 返回時,只有衝突的路徑會處於未合併狀態。

選項

-m

執行合併,而不僅僅是讀取。如果索引檔案中有未合併的條目,命令將拒絕執行,這表明您尚未完成之前開始的合併。

--reset

與 -m 相同,不同之處在於未合併的條目將被丟棄而不是失敗。當與 -u 一起使用時,會導致工作樹更改或未跟蹤檔案或目錄丟失的更新不會中止操作。

-u

成功合併後,使用合併結果更新工作樹中的檔案。

-i

通常,合併需要索引檔案以及工作樹中的檔案與當前 HEAD 提交保持最新,以避免丟失本地更改。此標誌停用與工作樹的檢查,旨在用於將與當前工作樹狀態不直接相關的樹的合併建立到臨時索引檔案中。

-n
--dry-run

檢查命令是否會出錯,而不實際更新索引或工作樹中的檔案。

-v

顯示檢出檔案的進度。

--trivial

git read-tree 的三路合併限制為僅在不需要檔案級別合併時發生,而不是解析簡單的合併情況並將衝突的檔案保留為未解決狀態。

--aggressive

通常,git read-tree 的三路合併會解析非常簡單的情況,並將其他情況保留為未解決狀態,以便 porcelain 可以實現不同的合併策略。此標誌使命令在內部解析更多情況

  • 當一方刪除一個路徑而另一方保持該路徑不變時。解析結果是刪除該路徑。

  • 當雙方都刪除一個路徑時。解析結果是刪除該路徑。

  • 當雙方都以相同方式新增一個路徑時。解析結果是新增該路徑。

--prefix=<prefix>

保留當前的索引內容,並在 <prefix> 指定的目錄下讀取指定的 tree-ish 的內容。該命令將拒絕覆蓋原始索引檔案中已存在的條目。

--index-output=<file>

不將結果寫入 $GIT_INDEX_FILE,而是將生成的索引寫入指定的檔案。在命令操作期間,原始索引檔案會像通常一樣被鎖定。該檔案必須允許從在索引檔案旁邊建立的臨時檔案中重新命名(rename(2))到該檔案;通常這意味著它需要與索引檔案位於同一檔案系統上,並且您需要對索引檔案和索引輸出檔案所在的目錄具有寫入許可權。

--recurse-submodules
--no-recurse-submodules

使用 --recurse-submodules 將透過遞迴呼叫 read-tree 來更新所有活動子模組的內容,使其與超級專案中記錄的提交一致,同時還將子模組的 HEAD 設定為分離狀態(detached)。

--no-sparse-checkout

即使 core.sparseCheckout 為 true,也停用稀疏檢出支援。

--empty

不將樹物件讀取到索引中,而是將其清空。

-q
--quiet

安靜模式,抑制反饋訊息。

<tree-ish#>

要讀取/合併的樹物件的 ID。

合併

如果指定了 -mgit read-tree 可以執行 3 種合併:如果只給了一個樹,則為單樹合併;如果給 2 個樹,則為快進合併;如果給 3 個或更多樹,則為 3 路合併。

單樹合併

如果只指定了一個樹,git read-tree 的行為就如同使用者未指定 -m,但如果原始索引中有給定路徑名的條目,並且該路徑的內容與正在讀取的樹匹配,則使用索引中的 stat 資訊。(換句話說,索引的 stat() 優先於合併樹的。)

這意味著,如果您執行 git read-tree -m <newtree>,然後執行 git checkout-index -f -u -agit checkout-index 只會檢出真正發生更改的內容。

這用於避免在 git diff-filesgit read-tree 之後執行時出現不必要的誤報。

兩樹合併

通常,這被呼叫為 git read-tree -m $H $M,其中 $H 是當前倉庫的 HEAD 提交,而 $M 是外部樹的 HEAD,它僅僅是領先於 $H(即,我們處於快進情況)。

當指定兩棵樹時,使用者告知 git read-tree 以下資訊:

  1. 當前索引和工作樹源自 $H,但使用者可能自 $H 以來對其進行了本地更改。

  2. 使用者希望快進到 $M。

在這種情況下,git read-tree -m $H $M 命令確保“合併”的結果不會丟失任何本地更改。以下是“向前傳遞”規則,其中“I”表示索引,“clean”表示索引和工作樹一致,“exists”/“nothing”表示路徑在指定提交中的存在情況:

	I                   H        M        Result
       -------------------------------------------------------
     0  nothing             nothing  nothing  (does not happen)
     1  nothing             nothing  exists   use M
     2  nothing             exists   nothing  remove path from index
     3  nothing             exists   exists,  use M if "initial checkout",
				     H == M   keep index otherwise
				     exists,  fail
				     H != M

        clean I==H  I==M
       ------------------
     4  yes   N/A   N/A     nothing  nothing  keep index
     5  no    N/A   N/A     nothing  nothing  keep index

     6  yes   N/A   yes     nothing  exists   keep index
     7  no    N/A   yes     nothing  exists   keep index
     8  yes   N/A   no      nothing  exists   fail
     9  no    N/A   no      nothing  exists   fail

     10 yes   yes   N/A     exists   nothing  remove path from index
     11 no    yes   N/A     exists   nothing  fail
     12 yes   no    N/A     exists   nothing  fail
     13 no    no    N/A     exists   nothing  fail

	clean (H==M)
       ------
     14 yes                 exists   exists   keep index
     15 no                  exists   exists   keep index

        clean I==H  I==M (H!=M)
       ------------------
     16 yes   no    no      exists   exists   fail
     17 no    no    no      exists   exists   fail
     18 yes   no    yes     exists   exists   keep index
     19 no    no    yes     exists   exists   keep index
     20 yes   yes   no      exists   exists   use M
     21 no    yes   no      exists   exists   fail

在所有“保留索引”的情況下,索引條目將保持與原始索引檔案相同。如果條目不是最新的,git read-tree 在使用 -u 標誌執行時會保持工作樹中的副本不變。

當此形式的 git read-tree 成功返回後,您可以透過執行 git diff-index --cached $M 來檢視您所做的“本地更改”中哪些已被向前傳遞。請注意,這不一定與此類兩樹合併之前的 git diff-index --cached $H 所產生的結果匹配。這是因為情況 18 和 19——如果您已經有了 $M 中的更改(例如,您可能透過電子郵件收到了補丁形式),git diff-index --cached $H 會在這次合併之前告知您更改,但它在兩樹合併後的 git diff-index --cached $M 輸出中將不會顯示。

情況 3 有點棘手,需要解釋。此規則的結果邏輯上應該是在使用者暫存了路徑的刪除,然後切換到新分支時刪除該路徑。然而,這會阻止初始檢出發生,因此規則被修改為僅在使用 M(新樹)時,當索引的內容為空時。否則,只要 $H 和 $M 相同,路徑的刪除就會保留。

3 路合併

每個“索引”條目都有兩個位的“階段”狀態。階段 0 是正常的,也是在任何正常使用中您都會看到的唯一狀態。

然而,當您使用三個樹進行 git read-tree 時,“階段”從 1 開始。

這意味著您可以執行

$ git read-tree -m <tree1> <tree2> <tree3>

然後您將得到一個索引,其中 <tree1> 的所有條目都處於“stage1”,<tree2> 的所有條目都處於“stage2”,<tree3> 的所有條目都處於“stage3”。當執行將另一個分支合併到當前分支時,我們將公共祖先樹作為 <tree1>,當前分支 HEAD 作為 <tree2>,另一個分支 HEAD 作為 <tree3>。

此外,git read-tree 具有特殊情況邏輯,如下:如果您看到一個檔案在以下狀態中與所有方面都匹配,它將“摺疊”回“stage0”

  • 階段 2 和 3 相同;選擇其中一個(沒有區別——我們的分支在階段 2 中做了相同的工作,他們的分支在階段 3 中也做了相同的工作)。

  • 階段 1 和階段 2 相同,而階段 3 不同;選擇階段 3(我們的分支在階段 2 中自階段 1 的祖先以來沒有做任何工作,而他們的分支在階段 3 中對其進行了操作)。

  • 階段 1 和階段 3 相同,而階段 2 不同;選擇階段 2(我們做了一些工作,而他們沒有)。

git write-tree 命令拒絕寫入無意義的樹,如果它看到一個不是階段 0 的單個條目,它將抱怨未合併的條目。

好的,這一切聽起來都像是一堆完全無意義的規則,但實際上這就是您想要快速合併的確切內容。不同的階段代表“結果樹”(階段 0,即“已合併”),原始樹(階段 1,即“orig”),以及您正在嘗試合併的兩棵樹(分別為階段 2 和 3)。

當您使用已經填充的索引檔案開始 3 路合併時,階段 1、2 和 3 的順序(因此命令列的三個 <tree-ish> 引數的順序)很重要。以下是演算法工作方式的概述:

  • 如果一個檔案以相同的格式出現在所有三棵樹中,git read-tree 將自動將其摺疊為“已合併”狀態。

  • 在三棵樹中存在任何細微差別的檔案將保留為索引中的獨立條目。由“porcelain policy”來確定如何刪除非 0 階段,以及插入合併版本。

  • 索引檔案會儲存並恢復所有這些資訊,因此您可以增量地進行合併,但只要它包含階段 1/2/3 的條目(即“未合併的條目”),您就無法寫入結果。因此,現在合併演算法變得非常簡單:

    • 您按順序遍歷索引,並忽略所有階段 0 的條目,因為它們已經處理完畢。

    • 如果您找到一個“stage1”,但沒有匹配的“stage2”或“stage3”,您就知道它已從兩棵樹中刪除(它只存在於原始樹中),並且您將刪除該條目。

    • 如果您找到匹配的“stage2”和“stage3”樹,您將刪除其中一個,並將另一個轉換為“stage0”條目。如果存在匹配的“stage1”條目,也將其刪除……所有正常的簡單規則……

您通常會使用 git merge-index 並提供 git merge-one-file 來執行此最後一步。指令碼在合併每個路徑時會更新工作樹中的檔案,並在成功合併結束時也這樣做。

當您使用已經填充的索引檔案開始 3 路合併時,假定它代表您工作樹中檔案的狀態,並且您甚至可以擁有未在索引檔案中記錄更改的檔案。進一步假設此狀態“派生”自階段 2 樹。3 路合併如果發現原始索引檔案中與階段 2 不匹配的條目,將拒絕執行。

這樣做是為了防止您丟失正在進行的工作,並將您的隨機更改混入不相關的合併提交中。為了說明,假設您從儲存庫中最後一次提交的狀態開始:

$ JC=`git rev-parse --verify "HEAD^0"`
$ git checkout-index -f -u -a $JC

您進行隨機編輯,而不執行 git update-index。然後您注意到“上游”樹的尖端自您從他那裡拉取以來已經前進:

$ git fetch git://.... linus
$ LT=`git rev-parse FETCH_HEAD`

您的工作樹仍然基於您的 HEAD ($JC),但自那時以來您進行了一些編輯。三路合併可確保您自 $JC 以來沒有新增或修改索引條目,如果沒有,則會進行正確的操作。因此,透過以下序列:

$ git read-tree -m -u `git merge-base $JC $LT` $JC $LT
$ git merge-index git-merge-one-file -a
$ echo "Merge with Linus" | \
  git commit-tree `git write-tree` -p $JC -p $LT

您將提交的是 $JC 和 $LT 之間的純合併,而不包含您正在進行的更改,並且您的工作樹將更新為合併結果。

但是,如果您在工作樹中有將被此合併覆蓋的本地更改,git read-tree 將拒絕執行,以防止您的更改丟失。

換句話說,無需擔心僅存在於工作樹中的內容。當您在專案未參與合併的部分有本地更改時,您的更改不會干擾合併,並且會保持不變。當它們確實干擾時,合併甚至不會開始(git read-tree 會大聲抱怨並在不修改任何內容的情況下失敗)。在這種情況下,您可以繼續進行您正在做的事情,當您的工作樹準備就緒時(即,您已完成正在進行的工作),再次嘗試合併。

稀疏檢出

注意:git-update-index[1]read-tree 中的 skip-worktree 功能早於 git-sparse-checkout[1] 的引入。鼓勵使用者優先使用 sparse-checkout 命令來處理與稀疏檢出/skip-worktree 相關的需求。但是,以下資訊可能對試圖理解 sparse-checkout 命令的非錐形模式中使用的模式風格的使用者有所幫助。

“稀疏檢出”允許稀疏地填充工作目錄。它使用 skip-worktree 位(參見 git-update-index[1])來告訴 Git 工作目錄中的檔案是否值得檢視。

git read-tree 和其他基於合併的命令(git mergegit checkout……)可以幫助維護 skip-worktree 點陣圖和工作目錄更新。$GIT_DIR/info/sparse-checkout 用於定義 skip-worktree 參考點陣圖。當 git read-tree 需要更新工作目錄時,它會根據此檔案重置索引中的 skip-worktree 位,該檔案使用的語法與 .gitignore 檔案相同。如果條目匹配此檔案中的模式,或者條目對應於工作樹中存在的檔案,則不會在該條目上設定 skip-worktree。否則,將設定 skip-worktree。

然後它比較新的 skip-worktree 值與之前的。如果 skip-worktree 從設定變為未設定,它將重新新增相應的檔案。如果它從未設定變為設定,則該檔案將被刪除。

雖然 $GIT_DIR/info/sparse-checkout 通常用於指定哪些檔案包含在內,但您也可以使用否定模式指定哪些檔案包含在內。例如,要刪除檔案 unwanted

/*
!unwanted

另一個棘手的問題是,當您不再想要稀疏檢出時,完全重新填充工作目錄。您不能僅僅停用“稀疏檢出”,因為 skip-worktree 位仍然在索引中,並且您的工作目錄仍然是稀疏填充的。您應該使用 $GIT_DIR/info/sparse-checkout 檔案內容重新填充工作目錄,如下所示:

/*

然後您可以停用稀疏檢出。git read-tree 和類似命令中的稀疏檢出支援預設是停用的。您需要將 core.sparseCheckout 設定為 true 才能啟用稀疏檢出支援。

GIT

Git[1] 套件的一部分