虽然AtnNn 的答案在内部存储如何工作方面是正确的,但值得注意的是,Git从 Git 称之为索引或暂存区域或(现在很少)缓存的东西构建这些树对象。索引不能保存目录:它只保存文件。索引中的文件只是具有嵌入斜杠的长路径名,例如.path/to/file.txt
该git write-tree命令读取索引并将其拆分:
file.txt。该树对象一旦创建就会获得一个哈希ID。我们将此哈希 ID 称为H 2。to。的条目to将存储哈希ID H 2。(它可能包含更多条目:它将为每个以 开头的路径包含一个条目path/to/。)当git write-tree写出此树对象时,它将获得一个哈希 ID;我们将此哈希 ID 称为H 1。path,该条目将存储哈希 ID H 1。(和以前一样,它可能包含更多条目,例如名为的条目README.md将保存包含文件内容的 blob 的哈希 ID README.md。)当git write-tree写出此树对象时,它将获得一个哈希 ID,我们可以将其称为H 0。该git write-tree命令将此哈希 ID H 0报告到其标准输出。
该git commit-tree命令使用此哈希 ID 以及其他信息来创建提交对象。提交对象将以H 0作为其tree。因此提交将引用树H 0。
要将提交读入Git 的索引,请注意H 0内部git read-tree有一个名为 H 0 的子树,因此它读取该子树(哈希H 1)并发现有一个名为H 2的条目。因此,它读取该子子树并找到名为给出文件的 blob 哈希 ID 的条目。然后,它写入索引,存储 blob 对象的哈希 ID。pathtofile.txtpath/to/file.txt
虽然git commit现在git checkout已经内置了所有这些步骤,但您仍然可以使用git write-tree后面的来git commit-tree进行新的提交。您仍然可以使用git read-tree将树读入 Git 的索引,然后使用git checkout-index将文件提取到工作区。索引中没有目录名称!它只有文件名。签出代码只会在需要时创建新目录:也就是说,如果 Git 需要创建一个名为 的文件path/to/file.txt但目前还没有path,Git 就会创建它。现在有了 .git path,如果需要的话,Git 也会创建path/to一个 .git ,现在它已经存在了,Git 可以创建一个名为inside 的path/to/文件。file.txtpath/to/
Git 不在索引中存储目录这一事实意味着:
有一个适用于空目录的子模块技巧:请参阅“如何将空目录添加到 Git 存储库?”的答案。
1由于目前唯一允许的文件模式是100755(可执行)和100644(不可执行),因此无论如何都没有地方存储组写入权限。例如,在 Git 的早期,您可以将文件存储为模式100664,因此那时它更有意义。请注意,在 Linux 上,目录必须是可执行文件才能使用它们,因此虽然树对象存储为 mode 40000,但实际的磁盘 inode 具有 mode 040777 & ~umask,其中040000是S_IFDIR位。例如,参见https://docs.huihoo.com/doxygen/linux/kernel/3.7/include_2uapi_2linux_2stat_8h.html