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是如何存储目录的,Git是否遵循Linux的理念“任何东西都是文件”,然后将目录视为要存储的文件?

Git将目录存储为
对象,其中包含目录中每个条目的模式、类型、哈希和条目名称。例如,在根目录下有文件和文件夹的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的早期,您可以将文件存储为mode
100664
,因此它在当时更有意义。请注意,在Linux上,目录必须是可执行的才能使用,因此当树对象存储为mode
40000
时,实际的磁盘inode具有mode
040777&~umask
,wh