lem*_*han 36
树对象的格式:
tree [content size]\0[Entries having references to other trees and blobs]
Run Code Online (Sandbox Code Playgroud)
每个条目的格式都引用了其他树和blob:
[mode] [file/folder name]\0[SHA-1 of referencing blob or tree]
Run Code Online (Sandbox Code Playgroud)
我写了一个缩小树对象的脚本.它输出如下:
tree 192\0
40000 octopus-admin\0 a84943494657751ce187be401d6bf59ef7a2583c
40000 octopus-deployment\0 14f589a30cf4bd0ce2d7103aa7186abe0167427f
40000 octopus-product\0 ec559319a263bc7b476e5f01dd2578f255d734fd
100644 pom.xml\0 97e5b6b292d248869780d7b0c65834bfb645e32a
40000 src\0 6e63db37acba41266493ba8fb68c76f83f1bc9dd
Run Code Online (Sandbox Code Playgroud)
数字1作为模式的第一个字符显示是对blob /文件的引用.上面的例子,pom.xml是一个blob,其他是树.
请注意,\0为了漂亮的打印,我添加了新的行和空格.通常所有内容都没有新行.我还将20个字节(即引用blob和树的SHA-1)转换为十六进制字符串以更好地可视化.
ant*_*nio 19
我试着通过一个测试回购来详细说明@lemiorhan的回答.
在空文件夹中创建测试项目:
$ echo ciao > file1
$ mkdir folder1
$ echo hello > folder1/file2
$ echo hola > folder1/file3
Run Code Online (Sandbox Code Playgroud)
那是:
$ find -type f
./file1
./folder1/file2
./folder1/file3
Run Code Online (Sandbox Code Playgroud)
创建本地Git仓库:
$ git init
$ git add .
$ git write-tree
0b6e66b04bc1448ca594f143a91ec458667f420e
Run Code Online (Sandbox Code Playgroud)
最后一个命令返回顶级树的哈希值.
要以人类可读格式打印树的内容,请使用:
$ git ls-tree 0b6e66
100644 blob 887ae9333d92a1d72400c210546e28baa1050e44 file1
040000 tree ab39965d17996be2116fe508faaf9269e903c85b folder1
Run Code Online (Sandbox Code Playgroud)
在这种情况下0b6e66,顶部树的前六个字符.你也可以这样做folder1.
要以原始格式获取相同的内容,请使用:
$ git cat-file tree 0b6e66
100644 file1 ?z?3=???$ ??Tn(???D40000 folder1 ?9?]?k??o???i???[%
Run Code Online (Sandbox Code Playgroud)
内容类似于以压缩格式存储为文件的内容,但它错过了初始字符串:
tree [content size]\0
Run Code Online (Sandbox Code Playgroud)
要获取实际内容,我们需要解压缩存储c1f4bf树对象的文件.我们想要的文件是 - 给出2/38路径格式 - :
.git/objects/0b/6e66b04bc1448ca594f143a91ec458667f420e
Run Code Online (Sandbox Code Playgroud)
此文件使用zlib压缩,因此我们使用以下内容获取其内容:
$ openssl zlib -d -in .git/objects/0b/6e66b04bc1448ca594f143a91ec458667f420e
tree 67 100644 file1 ?z?3=???$ ??Tn(???D40000 folder1 ?9?]?k??o???i???[%
Run Code Online (Sandbox Code Playgroud)
我们了解树的内容大小是67.
请注意,由于终端不是用于打印二进制文件,因此它可能会占用字符串的某些部分或显示其他奇怪的行为.在这种情况下,| od -c使用下一节中的手动解决方案管道上面的命令.
为了理解树生成过程,我们可以从人类可读的内容开始自己生成它,例如对于顶层树:
$ git ls-tree 0b6e66
100644 blob 887ae9333d92a1d72400c210546e28baa1050e44 file1
040000 tree ab39965d17996be2116fe508faaf9269e903c85b folder1
Run Code Online (Sandbox Code Playgroud)
每个对象ASCII SHA-1哈希都被转换并以二进制格式存储.如果你需要的只是ASCII哈希的二进制版本,你可以用:
$ echo -e "$(echo ASCIIHASH | sed -e 's/../\\x&/g')"
Run Code Online (Sandbox Code Playgroud)
所以blob 887ae9333d92a1d72400c210546e28baa1050e44转换为
$ echo -e "$(echo 887ae9333d92a1d72400c210546e28baa1050e44 | sed -e 's/../\\x&/g')"
?z?3=???$ ??Tn(???D
Run Code Online (Sandbox Code Playgroud)
如果我们想要创建整个树对象,这里是一个awk单行:
$ git ls-tree 0b6e66 | awk -b 'function bsha(asha)\
{patsplit(asha, x, /../); h=""; for(j in x) h=h sprintf("%c", strtonum("0x" x[j])); return(h)}\
{t=t sprintf("%d %s\0%s", $1, $4, bsha($3))} END {printf("tree %s\0%s", length(t), t)}'
tree 67 100644 file1 ?z?3=???$ ??Tn(???D40000 folder1 ?9?]?k??o???i???[%
Run Code Online (Sandbox Code Playgroud)
该函数bsha将SHA-1 ASCII哈希值转换为二进制文件.首先将树内容放入变量中t,然后计算其长度并在该END{...}部分中打印.
如上所述,控制台不太适合打印二进制文件,因此我们可能希望用\x##等效的格式替换它们:
$ git ls-tree 0b6e66 | awk -b 'function bsha(asha)\
{patsplit(asha, x, /../); h=""; for(j in x) h=h sprintf("%s", "\\x" x[j]); return(h)}\
{t=t sprintf("%d %s\0%s", $1, $4, bsha($3))} END {printf("tree %s\0%s", length(t), t)}'
tree 187 100644 file1 \x88\x7a\xe9\x33\x3d\x92\xa1\xd7\x24\x00\xc2\x10\x54\x6e\x28\xba\xa1\x05\x0e\x4440000 folder1 \xab\x39\x96\x5d\x17\x99\x6b\xe2\x11\x6f\xe5\x08\xfa\xaf\x92\x69\xe9\x03\xc8\x5b%
Run Code Online (Sandbox Code Playgroud)
输出应该是理解树内容结构的良好折衷.将上面的输出与常规树内容结构进行比较
tree [content size]\0[Object Entries]
Run Code Online (Sandbox Code Playgroud)
每个对象条目的位置如下:
[mode] [Object name]\0[SHA-1 in binary format]
Run Code Online (Sandbox Code Playgroud)
模式是UNIX文件系统模式的子集.有关详细信息,请参阅Git上的Tree Objects手册.
我们需要确保结果是一致的.为此,我们可以将awk生成树的校验和与Git存储树的校验和进行比较.
至于后者:
$ openssl zlib -d -in .git/objects/0b/6e66b04bc1448ca594f143a91ec458667f420e | shasum
0b6e66b04bc1448ca594f143a91ec458667f420e *-
Run Code Online (Sandbox Code Playgroud)
至于自制树:
$ git ls-tree 0b6e66 | awk -b 'function bsha(asha)\
{patsplit(asha, x, /../); h=""; for(j in x) h=h sprintf("%c", strtonum("0x" x[j])); return(h)}\
{t=t sprintf("%d %s\0%s", $1, $4, bsha($3))} END {printf("tree %s\0%s", length(t), t)}' | shasum
0b6e66b04bc1448ca594f143a91ec458667f420e *-
Run Code Online (Sandbox Code Playgroud)
校验和是一样的.
或多或少的官方方式是:
$ git ls-tree 0b6e66 | git mktree
0b6e66b04bc1448ca594f143a91ec458667f420e
Run Code Online (Sandbox Code Playgroud)
要手动计算它,我们需要将脚本生成树的内容传递给shasum命令.实际上我们已经完成了上述操作(比较生成和存储的内容).结果是:
0b6e66b04bc1448ca594f143a91ec458667f420e *-
Run Code Online (Sandbox Code Playgroud)
和是一样的 git mktree.
您可能会发现,对于您的仓库,您无法找到.git/objects/XX/XXX...存储Git对象的文件
.发生这种情况是因为一些或所有"松散"对象已被打包到一个或多个.git\objects\pack\*.pack文件中.
要解压缩仓库,首先将包文件从其原始位置移开,然后git-unpack对象.
$ mkdir .git/pcache
$ mv .git/objects/pack/*.pack .git/pcache/
$ git unpack-objects < .git/pcache/*.pack
Run Code Online (Sandbox Code Playgroud)
完成实验后重新包装:
$ git gc
Run Code Online (Sandbox Code Playgroud)
Gre*_*con 12
表示为类似BNF的模式,git树包含表单的数据
(?<tree> tree (?&SP) (?&decimal) \0 (?&entry)+ )
(?<entry> (?&octal) (?&SP) (?&strnull) (?&sha1bytes) )
(?<strnull> [^\0]+ \0)
(?<sha1bytes> (?s: .{20}))
(?<decimal> [0-9]+)
(?<octal> [0-7]+)
(?<SP> \x20)
Run Code Online (Sandbox Code Playgroud)
也就是说,git树以标题开头
tree0x20)在NUL(即字节0x00)终止符之后,树包含表单的一个或多个条目
然后,Git将树数据提供给zlib的 deflate以进行紧凑存储.
请记住,git blob是匿名的.Git树将名称与其他内容的SHA1哈希相关联,其他内容可能是blob,其他树等.
要演示,请考虑与git的v2.7.2标记关联的树,您可能希望在GitHub上浏览该树.
$ git rev-parse v2.7.2^{tree}
802b6758c0c27ae910f40e1b4862cb72a71eee9f
Run Code Online (Sandbox Code Playgroud)
下面的代码要求树对象采用"松散"格式.我不知道从packfile中提取单个原始对象的方法,所以我首先git unpack-objects将包装文件从我的克隆运行到新的存储库.请注意,这扩展了一个.git大约90 MB 的目录,大约1.8 GB.
更新:感谢max630展示如何解压缩单个对象.
#! /usr/bin/env perl
use strict;
use warnings;
use subs qw/ git_tree_contents_pattern read_raw_tree_object /;
use Compress::Zlib;
my $treeobj = read_raw_tree_object;
my $git_tree_contents = git_tree_contents_pattern;
die "$0: invalid tree" unless $treeobj =~ /^$git_tree_contents\z/;
die "$0: unexpected header" unless $treeobj =~ s/^(tree [0-9]+)\0//;
print $1, "\n";
# e.g., 100644 SP .gitattributes \0 sha1-bytes
while ($treeobj) {
# /s is important so . matches any byte!
if ($treeobj =~ s/^([0-7]+) (.+?)\0(.{20})//s) {
my($mode,$name,$bytes) = (oct($1),$2,$3);
printf "%06o %s %s\t%s\n",
$mode, ($mode == 040000 ? "tree" : "blob"),
unpack("H*", $bytes), $name;
}
else {
die "$0: unexpected tree entry";
}
}
sub git_tree_contents_pattern {
qr/
(?(DEFINE)
(?<tree> tree (?&SP) (?&decimal) \0 (?&entry)+ )
(?<entry> (?&octal) (?&SP) (?&strnull) (?&sha1bytes) )
(?<strnull> [^\0]+ \0)
(?<sha1bytes> (?s: .{20}))
(?<decimal> [0-9]+)
(?<octal> [0-7]+)
(?<SP> \x20)
)
(?&tree)
/x;
}
sub read_raw_tree_object {
# $ git rev-parse v2.7.2^{tree}
# 802b6758c0c27ae910f40e1b4862cb72a71eee9f
#
# NOTE: extracted using git unpack-objects
my $tree = ".git/objects/80/2b6758c0c27ae910f40e1b4862cb72a71eee9f";
open my $fh, "<", $tree or die "$0: open $tree: $!";
binmode $fh or die "$0: binmode: $!";
local $/;
my $treeobj = uncompress <$fh>;
die "$0: uncompress failed" unless defined $treeobj;
$treeobj
}
Run Code Online (Sandbox Code Playgroud)
看着我们的穷人git ls-tree在行动.输出相同,只是它输出tree标记和长度.
$ diff -u <(cd ~/src/git; git ls-tree 802b6758c0) <(../rawtree) --- /dev/fd/63 2016-03-09 14:41:37.011791393 -0600 +++ /dev/fd/62 2016-03-09 14:41:37.011791393 -0600 @@ -1,3 +1,4 @@ +tree 15530 100644 blob 5e98806c6cc246acef5f539ae191710a0c06ad3f .gitattributes 100644 blob 1c2f8321386f89ef8c03d11159c97a0f194c4423 .gitignore 100644 blob e5b4126bec557db55924b7b60ed70349626ea2c4 .mailmap
正如所建议的,Pro Git 很好地解释了结构。要显示漂亮的树打印,请使用:
git cat-file -p 4c975c5f5945564eae86d1e933192c4a9096bfe5
Run Code Online (Sandbox Code Playgroud)
要以原始但未压缩的形式显示同一棵树,请使用:
git cat-file tree 4c975c5f5945564eae86d1e933192c4a9096bfe5
Run Code Online (Sandbox Code Playgroud)
结构本质上是相同的,哈希值存储为二进制和以 null 结尾的文件名。