我总是想知道Git如何存储目录,Git是否遵循Linux的哲学“任何东西都是FILE”,然后将目录视为要存储的文件?
答案 0 :(得分:4)
尽管AtnNn's answer在内部存储的工作方式上是正确的,但值得注意的是,Git 使用Git称为其 index 这些树对象。 >或登台区域或(现在很少)缓存。索引无法保存目录:它仅保存文件。索引中的文件仅具有长路径名,并带有斜杠,例如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
的条目,它将保存包含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 commit
和git 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不在索引中存储目录的事实意味着:
有一个子模块技巧可用于空目录:请参见this answer至How 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的“树对象”部分。