设置和配置
获取和创建项目
基本快照
分支与合并
共享和更新项目
检查和比较
打补丁
调试
电子邮件
外部系统
服务器管理
指南
管理
底层命令
- 2.53.0 → 2.54.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 向)合并或 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 进行的三向合并仅解决真正琐碎的情况,并将其他情况留在索引中,以便由更高级的命令实现不同的合并策略。此标志使命令在内部解决更多情况:
-
当一方删除了某路径而另一方保持该路径不变时。解决方法是删除该路径。
-
当双方都删除了某路径时。解决方法是删除该路径。
-
当双方以相同方式添加了某路径时。解决方法是添加该路径。
-
- --prefix=<prefix>
-
保留当前的索引内容,并将指定的 tree-ish 的内容读取到 <prefix> 目录之下。该命令将拒绝覆盖原始索引文件中已存在的条目。
- --index-output=<file>
-
不要将结果写入
$GIT_INDEX_FILE,而是写入指定的文件。命令运行时,原始索引文件会以通常的机制加锁。该文件必须允许从创建在常用索引文件旁的临时文件 rename(2) 过来;通常这意味着它必须与索引文件位于同一个文件系统上,且您拥有索引文件及其输出文件所在目录的写权限。 - --recurse-submodules
- --no-recurse-submodules
-
使用 --recurse-submodules 将通过递归调用 read-tree 来根据超级项目中记录的提交更新所有活动子模块的内容,同时将子模块的 HEAD 设置为在该提交处分离。
- --no-sparse-checkout
-
即使
core.sparseCheckout为真,也禁用稀疏检出支持。 - --empty
-
不将树对象读取到索引中,而是清空索引。
- -q
- --quiet
-
安静模式,抑制反馈消息。
- <tree-ish#>
-
要读取/合并的树对象的 ID。
合并 (MERGING)
如果指定了 -m,git read-tree 可以执行 3 种合并:如果只提供 1 个树,则进行单树合并;如果是 2 个树,则进行快进合并;如果提供 3 个或更多树,则进行 3 向合并。
单树合并
如果仅指定了 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 命令确保没有任何本地更改作为此次“合并”的结果而丢失。以下是“结转”规则,其中“I”表示索引,“干净”意味着索引和工作树一致,“存在”/“无”指的是特定提交中路径的存在情况。
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 向合并
每个“索引”条目都有两位“暂存”(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,即“orig”)以及您试图合并的两棵树(分别为 stage 2 和 3)。
当您使用已经填充的索引文件开始 3 向合并时,阶段 1、2 和 3 的顺序(因此也是三个 <tree-ish> 命令行参数的顺序)非常重要。以下是该算法的工作流程概述:
-
如果一个文件在所有三棵树中以相同格式存在,它将被 git read-tree 自动折叠为“已合并”状态。
-
一个在三棵树中存在任何差异的文件将保留为索引中的独立条目。如何移除非 0 阶段并插入合并后的版本,取决于“高级命令策略”。
-
索引文件保存并恢复所有这些信息,因此您可以增量合并。但只要它在阶段 1/2/3 中有条目(即“未合并条目”),您就无法写入结果。因此,合并算法现在变得非常简单:
-
按顺序遍历索引,忽略所有 stage 0 的条目,因为它们已经完成了。
-
如果您发现一个“stage1”,但没有匹配的“stage2”或“stage3”,则说明它已从两棵树中被删除(它仅存在于原始树中),您应该移除该条目。
-
如果您发现匹配的“stage2”和“stage3”树,请移除其中一个,并将另一个变为“stage0”条目。如果存在匹配的“stage1”条目,也请一并移除。.. 所有正常的琐碎规则 ..
-
您通常会使用带有提供的 git merge-one-file 的 git merge-index 来执行此最后一步。该脚本在合并每个路径时以及成功合并完成后更新工作树中的文件。
当您使用已经填充的索引文件开始 3 向合并时,假设它代表了您工作树中文件的状态,甚至允许工作树中有索引文件中未记录的更改。此外还假设该状态是“派生自”stage 2 树的。如果 3 向合并在原始索引文件中发现一个与 stage 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 会大声报错并失败,而不修改任何内容)。在这种情况下,您可以简单地继续完成您正在做的工作,当工作树准备好(即您完成了正在进行的工作)后,再次尝试合并。
稀疏检出 (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 才能使用稀疏检出支持。