设置和配置
获取和创建项目
基本快照
分支与合并
共享和更新项目
检查和比较
打补丁
调试
电子邮件
外部系统
服务器管理
指南
管理
底层命令
- 2.43.1 → 2.50.1 无更改
-
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
标志将一个树合并到索引中,执行快进(即双向)合并,或三向合并。与 -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>
-
保留当前索引内容,并将指定树对象的目录内容读入 <prefix> 目录。该命令将拒绝覆盖原始索引文件中已存在的条目。
- --index-output=<file>
-
将结果写入指定的
<file>
中,而不是写入$GIT_INDEX_FILE
。在命令操作期间,原始索引文件会像往常一样被锁定。该文件必须允许从在常用索引文件旁边创建的临时文件通过 rename(2) 重命名;通常这意味着它需要与索引文件本身位于同一文件系统上,并且您需要对索引文件和索引输出文件所在的目录具有写入权限。 - --[no-]recurse-submodules
-
使用 --recurse-submodules 将根据超级项目中记录的提交,通过递归调用 read-tree 来更新所有活动子模块的内容,同时也将子模块的 HEAD 分离到该提交。
- --no-sparse-checkout
-
即使
core.sparseCheckout
为 true,也禁用稀疏检出支持。 - --empty
-
清空索引,而不是将树对象读入索引。
- -q
- --quiet
-
安静模式,抑制反馈消息。
- <tree-ish#>
-
要读取/合并的树对象的 ID。
合并
如果指定了 -m
,git read-tree 可以执行 3 种合并:如果只给出一个树,则进行单个树合并;使用两个树进行快进合并;或如果提供三个或更多树,则进行三向合并。
单树合并
如果只指定一个树,git read-tree 的操作就像用户没有指定 -m
一样,但如果原始索引中存在给定路径名的条目,并且路径内容与正在读取的树匹配,则使用索引中的 stat 信息。(换句话说,索引的 stat()s 优先于合并树的 stat()s)。
这意味着如果您执行 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”表示索引,“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 相同,路径的删除就会被保留。
三向合并
每个“索引”条目都有两个位的“阶段”状态。阶段 0 是正常状态,也是您在任何正常使用中唯一会看到的状态。
然而,当您使用三个树执行 git read-tree 时,“阶段”从 1 开始。
这意味着您可以执行
$ git read-tree -m <tree1> <tree2> <tree3>
最终您将得到一个索引,其中所有 <tree1>
条目位于“stage1”中,所有 <tree2>
条目位于“stage2”中,所有 <tree3>
条目位于“stage3”中。将另一个分支合并到当前分支时,我们使用共同祖先树作为 <tree1>
,当前分支头作为 <tree2>
,另一个分支头作为 <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,即“原始”),以及您试图合并的两个树(分别为阶段 2 和 3)。
当您开始对已填充的索引文件进行三向合并时,阶段 1、2 和 3 的顺序(因此也是三个 <tree-ish>
命令行参数的顺序)非常重要。算法的工作原理概述如下:
-
如果文件在所有三个树中都以相同的格式存在,它将由 git read-tree 自动折叠为“已合并”状态。
-
如果文件在三个树中有 任何 不同之处,它将作为单独的条目保留在索引中。如何删除非 0 阶段并插入合并版本,由“上层策略”决定。
-
索引文件保存并恢复所有这些信息,因此您可以增量合并,但只要它有阶段 1/2/3 的条目(即“未合并的条目”),您就不能写入结果。所以现在合并算法变得非常简单:
-
您按顺序遍历索引,并忽略所有阶段 0 的条目,因为它们已经完成。
-
如果您找到一个“stage1”,但没有匹配的“stage2”或“stage3”,您就知道它已从两个树中删除(它只存在于原始树中),并且您删除该条目。
-
如果您找到匹配的“stage2”和“stage3”树,您删除其中一个,并将另一个转换为“stage0”条目。如果存在匹配的“stage1”条目,也将其删除。..所有正常的简单规则..
-
您通常会使用 git merge-index 并提供 git merge-one-file 来完成最后一步。该脚本在合并每个路径时更新工作树中的文件,并在成功合并结束时更新。
当您使用已填充的索引文件启动三向合并时,假定它代表您工作树中文件的状态,并且您甚至可以拥有索引文件中未记录更改的文件。进一步假定此状态“派生”自阶段 2 树。如果它在原始索引文件中找到与阶段 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 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
设置为 on 才能获得稀疏检出支持。