Tree这个词在Git中(以及计算机领域)有点多义。
工作目录或工作树(或其他拼写变体)是指您进行工作的位置。在此处,文件具有正常的日常形式,并且可以读取和(如果操作系统允许)写入。 (在Unix系统上,如果您使用chmod -w
更改文件,您将无法编写它们。但那不是Git的错。)
在Git中,树对象是一种内部数据结构,记录目录树或子树。它包含每个文件或子目录的一个条目(对于子模块,则为该子模块的gitlink条目)。每个条目列出文件的可执行模式位,作为奇怪编码的yes或no标志,以及文件的名称和blob哈希ID。对于子树,条目列出目录的名称和子目录对象哈希ID。 Git随后可以递归地通过子树对象来查找更多文件和更多子树(如有需要)。每个文件条目都为Git内部blob对象提供哈希ID,该对象是文件数据的冻结(只读)压缩副本。
每次提交保存一个(1)内部Git树对象哈希ID。该树对象包含提交包含的快照--因此提交的快照实际上是这些树之一,其中包含文件和子树的条目。由于每个提交都有一个树,Git可以从提交说明符转换为树对象:
$ git rev-parse master
3c31a203fbeedb4d746889dc77cbafc395fc6e92
$ git rev-parse master^{tree}
5c4b695f5d5606976f5b72e1a901ed17db30a359
在这种情况下,由
master
标识的
commit是第一个丑陋的哈希值,但是此提交所使用的内部
tree对象用于保存文件的是第二个。
因此,
工作树(work-tree)包含具有实际数据的实际文件,Git
tree对象(tree object)允许Git找到某个提交对应的所有冻结文件,只要您提供相应的树对象。
git diff
命令需要比较两个项目。这两件事可以是两个单独的文件,也可以是两个文件树。无论它们是树对象还是工作树中充满文件,
git diff
都会:
- 比较每棵树中的文件名称。
- 如果文件名匹配,Git假定这些是“相同的文件”,并将比较文件内容。
- 如果它们没有匹配的名称,则可选择通过内容匹配来匹配文件。
- 如果其他所有方法都失败,则告诉您删除了某些文件并添加了某些文件。删除文件的内容全部删除;添加文件的内容全部是新的。
这仅是概述,因为
git diff
可以做更多的事情,但这些就是基础知识。
还有一个非常重要的细节:
git diff
会检查索引并将其视为树。 索引包含从某处复制的文件副本。 最初,
某处是您
git checkout
的任何提交。 但是,您可以使用
git add
命令从工作树中添加文件以将它们复制到索引中,替换在提交中存储的版本。 您可以从未提交过的工作树中使用
git add
命令添加文件,因此对索引来说是新的。 此外,您可以使用
git rm
命令,带有或不带有
--cached
选项,将文件从索引中删除。
由于Git将从运行
git commit
时索引中的内容构建一个
新提交,因此将索引内容与某些冻结树或工作树进行比较是一件非常有用的事情。
1实际的树形条目存储(模式,路径,哈希)三元组。 mode
是一个字符串:对于可执行文件,是 100755
;对于非可执行文件,是 100644
;对于子树,是 40000
;对于符号链接,是 120000
;对于 git 链接,是 160000
。这些最初是 Linux 的 stat
st_mode
字段,Git 允许使用例如 rw-rw-r--
所表示的 100664
,但那被证明是一个错误,因此普通的树只使用有限的子集中的一个。Git 仍然支持 100664
,因为可能还有一些 Git 存储库仍具有这样的条目,但除非你找到一个非常旧的存储库,否则不会找到任何 100664
。哈希始终是 blob 哈希值,除了 gitlink 条目,其中哈希是子模块中所需的提交哈希。