設定與組態
取得與建立專案
基本快照
分支與合併
分享與更新專案
檢查與比較
修補
除錯
電子郵件
外部系統
伺服器管理
指南
- gitattributes
- 命令列介面規範
- Git 日常使用
- 常見問題 (FAQ)
- 詞彙表
- 掛鉤 (Hooks)
- gitignore
- gitmodules
- 修訂版本 (Revisions)
- 子模組
- 教學
- 工作流程
- 所有指南...
管理
底層命令 (Plumbing Commands)
- 2.53.0 → 2.55.0 無變更
-
2.52.0
2025-11-17
- 2.43.1 → 2.51.2 無變更
-
2.43.0
2023-11-20
- 2.40.1 → 2.42.4 無變更
-
2.40.0
2023-03-12
- 2.39.1 → 2.39.5 無變更
-
2.39.0
2022-12-12
- 2.37.1 → 2.38.5 無變更
-
2.37.0
2022-06-27
- 2.36.1 → 2.36.6 無變更
-
2.36.0
2022-04-18
- 2.34.1 → 2.35.8 無變更
-
2.34.0
2021-11-15
- 2.27.1 → 2.33.8 無變更
-
2.27.0
2020-06-01
- 2.25.1 → 2.26.3 無變更
-
2.25.0
2020-01-13
- 2.22.1 → 2.24.4 無變更
-
2.22.0
2019-06-07
- 2.18.1 → 2.21.4 無變更
-
2.18.0
2018-06-21
- 2.17.0 → 2.17.6 無變更
-
2.16.6
2019-12-06
- 2.15.4 無變更
-
2.14.6
2019-12-06
-
2.13.7
2018-05-22
- 2.2.3 → 2.12.5 無變更
-
2.1.4
2014-12-17
-
2.0.5
2014-12-17
概要
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-way)合併,或是 3-way 合併。當與 -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 進行的三方合併僅解決非常簡單的情況,並將其他情況留在索引中,以便更上層的工具(porcelains)實作不同的合併策略。此旗標使指令在內部解決更多情況:
-
當一方移除了某個路徑,而另一方未修改該路徑時。解析方式是移除該路徑。
-
當雙方都移除了某個路徑時。解析方式是移除該路徑。
-
當雙方都以相同方式添加了某個路徑時。解析方式是添加該路徑。
-
- --prefix=<prefix>
-
保留當前索引內容,並將名為 tree-ish 的內容讀取至 <prefix> 目錄下。該指令將拒絕覆寫原始索引檔案中已經存在的項目。
- --index-output=<file>
-
不將結果寫入
$GIT_INDEX_FILE,而是將生成的索引寫入指定的檔案中。當指令執行時,原始索引檔案會使用與平常相同的機制鎖定。該檔案必須允許從在通常索引檔案旁建立的臨時檔案透過 rename(2) 進行替換;通常這意味著它必須與索引檔案位於同一個檔案系統上,且您必須擁有索引檔案和索引輸出檔案所在目錄的寫入權限。 - --recurse-submodules
- --no-recurse-submodules
-
使用 --recurse-submodules 將透過遞迴呼叫 read-tree,根據超級專案(superproject)中記錄的提交來更新所有有效子模組的內容,並將子模組的 HEAD 設定為在該提交處分離(detached)。
- --no-sparse-checkout
-
即使
core.sparseCheckout為真,也停用稀疏檢出支援。 - --empty
-
不讀取樹狀物件到索引中,而是將其清空。
- -q
- --quiet
-
安靜模式,抑制回饋訊息。
- <tree-ish#>
-
要讀取/合併的樹狀物件 ID。
合併 (MERGING)
如果指定了 -m,git read-tree 可以執行 3 種合併:如果只給定 1 個樹狀物件,則進行單樹合併;如果提供 2 個樹狀物件,則進行快轉合併;如果提供 3 個或更多樹狀物件,則進行 3-way 合併。
單樹合併
如果只指定了 1 個樹狀物件,git read-tree 的運作方式就好像使用者沒有指定 -m 一樣,不同之處在於:如果原始索引對於給定的路徑名稱有條目,且路徑內容與正在讀取的樹狀物件相符,則會使用索引中的 stat 資訊。(換句話說,索引的 stat() 優先於合併後的樹狀物件)。
這意味著如果您執行 git read-tree -m <newtree> 後面接著 git checkout-index -f -u -a,git checkout-index 只會檢出真正變更過的內容。
這用於在 git read-tree 之後執行 git diff-files 時,避免不必要的誤報。
雙樹合併
通常,這是透過 git read-tree -m $H $M 來調用的,其中 $H 是當前儲存庫的 HEAD 提交,$M 是外部樹狀物件的 HEAD,它簡單地領先於 $H(即我們處於快轉情況)。
當指定兩個樹狀物件時,使用者告訴 git read-tree 以下事項:
-
當前的索引和工作樹源自 $H,但使用者自 $H 以來可能在其中有本機變更。
-
使用者希望快轉到 $M。
在這種情況下,git read-tree -m $H $M 指令確保不會因為這次「合併」而遺失任何本機變更。以下是「向前攜帶」(carry forward)規則,「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-Way 合併
每個「索引」條目都有兩個位元的「階段」(stage)狀態。stage 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」:
-
stage 2 和 3 相同;取其中之一(沒什麼差別 - 我們在 stage 2 的分支和他們在 stage 3 的分支完成了相同的工作)
-
stage 1 和 stage 2 相同而 stage 3 不同;取 stage 3(我們在 stage 2 的分支自 stage 1 的祖先以來未做任何事,而他們在 stage 3 的分支對其進行了修改)
-
stage 1 和 stage 3 相同而 stage 2 不同;取 stage 2(我們做了修改而他們什麼都沒做)
git write-tree 指令拒絕寫入無意義的樹狀物件,如果它看到任何非 stage 0 的單一條目,它會報告關於未合併條目的錯誤。
好的,這聽起來像是一堆完全無意義的規則集合,但這實際上正是您為了進行快速合併所需要的。不同的階段分別代表「結果樹」(stage 0,又稱「已合併」)、原始樹(stage 1,又稱「原始」)以及您正試圖合併的兩個樹(分別為 stage 2 和 3)。
當您使用已經填充好的索引檔案開始 3-way 合併時,階段 1、2 和 3 的順序(因此也是三個 <tree-ish> 命令列參數的順序)至關重要。以下是該演算法工作原理的概述:
-
如果一個檔案在所有三個樹中以相同格式存在,它將由 git read-tree 自動坍縮為「已合併」狀態。
-
在三個樹中具有任何差異的檔案將作為獨立條目保留在索引中。由「上層工具策略」(porcelain policy)來決定如何移除非 0 階段,並插入合併後的版本。
-
索引檔案會儲存並恢復所有這些資訊,因此您可以逐步合併內容,但只要它在階段 1/2/3 中還有條目(即「未合併條目」),您就無法寫入結果。因此,現在合併演算法變得非常簡單:
-
您按順序遍歷索引,忽略所有 stage 0 的條目,因為它們已經完成了。
-
如果您發現一個「stage1」,但沒有匹配的「stage2」或「stage3」,您知道它已從兩個樹中移除(它僅存在於原始樹中),然後您移除該條目。
-
如果您發現匹配的「stage2」和「stage3」樹,您移除其中之一,並將另一個轉換為「stage0」條目。如果存在匹配的「stage1」條目也一併移除。.. 所有常規簡單規則 ..
-
您通常會使用 git merge-index 並配合提供的 git merge-one-file 來執行此最後步驟。該腳本會在合併每個路徑時更新工作樹中的檔案,並在成功合併結束時執行。
當您使用已經填充的索引檔案開始 3-way 合併時,假設它代表您工作樹中檔案的狀態,並且您甚至可以擁有索引檔案中未記錄變更的檔案。進一步假設此狀態是「源自」stage 2 樹。如果 3-way 合併在原始索引檔案中發現不符合 stage 2 的條目,它將拒絕執行。
這樣做是為了防止您遺失正在進行中的變更,並將您的隨意變更混入無關的合併提交中。為了說明,假設您從儲存庫中最後一次提交的內容開始:
$ JC=`git rev-parse --verify "HEAD^0"` $ git checkout-index -f -u -a $JC
您進行了隨意編輯,而沒有執行 git update-index。然後您注意到您的「上游」樹的尖端自您從他那裡拉取(pull)以來已經前進了:
$ 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 會大聲抱怨並失敗,而不修改任何內容)。在這種情況下,您可以簡單地繼續做您正在做的事情,當您的工作樹準備好時(即您已經完成了手頭的工作),再嘗試進行合併。
稀疏檢出 (SPARSE CHECKOUT)
注意:git-update-index[1] 和 read-tree 中的 skip-worktree 功能早於 git-sparse-checkout[1] 的引入。建議使用者優先使用 sparse-checkout 指令,以滿足與稀疏檢出/skip-worktree 相關的需求。然而,下方的資訊對於試圖理解 sparse-checkout 指令非 cone 模式所使用的模式風格的使用者可能會有幫助。
「稀疏檢出」允許稀疏地填充工作目錄。它使用 skip-worktree 位元(參閱 git-update-index[1])來告訴 Git 工作目錄中的檔案是否值得關注。
git read-tree 和其他基於合併的指令(git merge, git 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 才能擁有稀疏檢出支援。
GIT
git[1] 套件的一部分