Git如何存储目录信息?

时间:2020-09-04 03:28:16

标签: git

我总是想知道Git如何存储目录,Git是否遵循Linux的哲学“任何东西都是FILE”,然后将目录视为要存储的文件?

2 个答案:

答案 0 :(得分:4)

尽管AtnNn's answer在内部存储的工作方式上是正确的,但值得注意的是,Git 使用Git称为其 index 这些树对象。 >或登台区域或(现在很少)缓存。索引无法保存目录:它仅保存文件。索引中的文件仅具有长路径名,并带有斜杠,例如path/to/file.txt

git write-tree命令读取索引并将其拆分:

  • 它将创建一个树对象,该树对象将包含一个 blob对象的条目,该条目以组件名称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的条目,它将保存包含README.md文件内容的Blob的哈希ID。)当git write-tree写出该树对象时,它将获得一个哈希ID,我们可以将其称为 H 0

git write-tree命令将此哈希ID H 0 报告到其标准输出。

git commit-tree命令使用此哈希ID以及其他信息来创建提交对象。提交对象的tree将为 H 0 。因此,提交将引用树 H 0

要读入 Git的索引,git read-tree注意在 H 0 中有一个名为path的子树。 ,因此它将读取该子树(哈希 H 1 ),并发现有一个名为to的条目为 H 2 。因此,它将读取该子子树,并找到名为file.txt的条目,该条目给出了文件的Blob哈希ID。然后,它将path/to/file.txt写入索引,存储blob对象的哈希ID。

虽然git commitgit checkout现在已经内置了所有这些步骤,但是您仍然可以在git write-tree之后再使用git commit-tree进行新的提交。您仍然可以使用git read-tree来读取Git索引中的树,然后使用git checkout-index将文件提取到工作区中。索引中没有目录名称!它只有文件名。签出代码将仅在需要时创建新目录:也就是说,如果Git需要创建一个名为path/to/file.txt的文件,但尚无path,则Git会创建它。现在有了path,如果需要,Git也会制作path/to,而现在path/to/存在,Git可以在{{1}内创建一个名为file.txt的文件。 }。

Git不在索引中存储目录的事实意味着:

  • 您无法存储目录权限; 1
  • 也没有正确的方法来存储 empty 目录。

有一个子模块技巧可用于空目录:请参见this answerHow can I add an empty directory to a Git repository?


1 由于今天允许的唯一 file 模式为path/to/(可执行)和100755(不可执行),因此没有地方可以使用仍然存储组写权限。在Git的早期,例如,您可以将文件存储为模式100644,所以这样做会更有意义。请注意,在Linux上,目录必须是可执行文件才能使用它们,因此,当树对象存储为模式100664时,实际的磁盘inode的模式为40000,其中040777 & ~umask是{ {1}}位。例如,参见https://docs.huihoo.com/doxygen/linux/kernel/3.7/include_2uapi_2linux_2stat_8h.html

答案 1 :(得分:3)

Git将目录存储为tree对象,其中包含目录的每个条目的条目的方式,类型,哈希和名称。例如,在Git存储库中,其根目录下有一个文件和一个文件夹:

$ ls
example.txt
src/

$ git cat-file -p HEAD:
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    example.txt
040000 tree 87a2294c8c0351121cefbaef16cbe88dd2b64b80    src

cat-file命令显示给定对象-p的漂亮(HEAD:)版本。多余的冒号指向分支的根目录。 HEAD:src将引用src子文件夹。

我们可以通过传递tree而不是-p来检查原始目录数据:

$ git cat-file tree HEAD: | hexdump  -C
00000000  31 30 30 36 34 34 20 65  78 61 6d 70 6c 65 2e 74  |100644 example.t|
00000010  78 74 00 e6 9d e2 9b b2  d1 d6 43 4b 8b 29 ae 77  |xt........CK.).w|
00000020  5a d8 c2 e4 8c 53 91 34  30 30 30 30 20 73 72 63  |Z....S.40000 src|
00000030  00 87 a2 29 4c 8c 03 51  12 1c ef ba ef 16 cb e8  |...)L..Q........|
00000040  8d d2 b6 4b 80                                    |...K.|

如果未打包git存储库,则此树对象将存储在.git/objects中。我们可以使用rev-parse来查找其哈希值:

$ git rev-parse HEAD:
cb8fd5fa2bf22ffa242d4e3fa520849551bbfa98

压缩后的内容与上面的数据相同,但带有一个小的前缀:

$ cat .git/objects/cb/8fd5fa2bf22ffa242d4e3fa520849551bbfa98 | zlib-flate -uncompress | hexdump -C
00000000  74 72 65 65 20 36 39 00  31 30 30 36 34 34 20 65  |tree 69.100644 e|
00000010  78 61 6d 70 6c 65 2e 74  78 74 00 e6 9d e2 9b b2  |xample.txt......|
00000020  d1 d6 43 4b 8b 29 ae 77  5a d8 c2 e4 8c 53 91 34  |..CK.).wZ....S.4|
00000030  30 30 30 30 20 73 72 63  00 87 a2 29 4c 8c 03 51  |0000 src...)L..Q|
00000040  12 1c ef ba ef 16 cb e8  8d d2 b6 4b 80           |...........K.|

我们可以确认哈希是正确的:

$ cat .git/objects/cb/8fd5fa2bf22ffa242d4e3fa520849551bbfa98 | zlib-flate -uncompress | sha1sum
cb8fd5fa2bf22ffa242d4e3fa520849551bbfa98  -

有关更多信息,请参见the documentation的“树对象”部分。