Git如何存储目录信息?;
我一直想知道Git是如何存储目录的,Git是否遵循Linux的理念“任何东西都是文件”,然后将目录视为要存储的文件?Git将目录存储为Git如何存储目录信息?;,git,Git,我一直想知道Git是如何存储目录的,Git是否遵循Linux的理念“任何东西都是文件”,然后将目录视为要存储的文件?Git将目录存储为树对象,其中包含目录中每个条目的模式、类型、哈希和条目名称。例如,在根目录下有文件和文件夹的Git存储库中: $ ls example.txt src/ $ git cat-file -p HEAD: 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 example.txt 040000 tree 8
树对象,其中包含目录中每个条目的模式、类型、哈希和条目名称。例如,在根目录下有文件和文件夹的Git存储库中:
$ ls
example.txt
src/
$ git cat-file -p HEAD:
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 example.txt
040000 tree 87a2294c8c0351121cefbaef16cbe88dd2b64b80 src
cat file
命令显示给定对象的漂亮(-p
)版本,头:
。额外的冒号表示分支的根目录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
中。我们可以使用revparse
查找其哈希:
$ 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 -
有关详细信息,请参阅的“树对象”部分。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: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
中。我们可以使用revparse
查找其哈希:
$ 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 -
有关更多信息,请参阅的“树对象”部分。尽管就内部存储的工作方式而言,Git是正确的,但值得注意的是,Git从Git调用其索引或暂存区域或(现在很少)缓存的内容构建这些树对象。索引不能保存目录:它只保存文件。索引中的文件只是具有带有斜杠的长路径名,例如path/to/file.txt
git write tree
命令读取索引并将其拆分:
- 它创建了一个树对象,其中包含一个blob对象的条目,保存在组件名
file.txt
下。此树对象创建后将获取哈希ID。让我们把这个哈希ID称为H2
- 它将创建另一个树对象,该对象将包含名为
to
的条目。to
的条目将存储哈希ID H2。(它可能包含更多条目:它将包含一个以path/to/
开头的路径)当git write tree
写出此树对象时,它将获得一个哈希ID;让我们把这个哈希ID称为H1
- 然后,它创建另一个树对象,该树对象将包含一个名为
path
的条目,该条目将存储散列ID H1。(和以前一样,它可能包含更多的条目,例如一个名为README.md
的条目,它将保存包含README.md
文件内容的blob的散列ID。)当git write tree
写出这个树对象时,它将获得一个散列ID,我们可以称之为H0
git write tree
命令将此散列ID H0报告到其标准输出
git commit tree
命令使用此哈希ID以及其他信息来创建提交对象。提交对象将H0作为其树
。因此,提交将引用树H0
要将提交读取到Git的索引中,Git read tree
注意到H0中有一个名为path
的子树,因此它读取该子树(散列H1),并发现有一个名为To
的条目。因此,它读取该子树并找到名为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就会创建它。既然有了一个路径
,Git也会根据需要制作路径/到
,既然路径/到
已经存在,Git就可以在路径/到
中创建一个名为file.txt
的文件
Git不在索引中存储目录这一事实意味着:
- 您无法存储目录的权限;1及
- 也没有合适的方法来存储空目录
有一个适用于空目录的子模块技巧:参见
1由于目前唯一允许的文件模式是100755
(可执行)和100644
(不可执行),因此没有存储组写入权限的位置。例如,在Git的早期,您可以将文件存储为mode100664
,因此它在当时更有意义。请注意,在Linux上,目录必须是可执行的才能使用,因此当树对象存储为mode40000
时,实际的磁盘inode具有mode040777&~umask
,wh