Git树哈希id生成

时间:2012-12-20 16:58:30

标签: git

首先初始化一个包含名为rose

的文件的repo
$: echo sweet > rose
$: git init
$: git add .
$: find .git/objects/ -type f
.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d
$: git commit -m "rose"
$: find .git/objects/ -type f -printf "%h%f %s\n"
.git/objects/05b217bb859794d08bb9e4f7f04cbda4b207fbe9 49
.git/objects/aa823728ea7d592acc69b36875a482cdf3fd5c8d 21
.git/objects/665d02ccbacdde1c0f2eecde01fbf47144ddd492 124

然后我想破坏blob并看看如何生成树对象的id

echo -e "tree 21\0100644 rose\0aa823728ea7d592acc69b36875a482cdf3fd5c8d"|sha1sum

打印的内容不是05b217bb859794d08bb9e4f7f04cbda4b207fbe9
我哪里错了?

3 个答案:

答案 0 :(得分:3)

树中的对象ID不以该格式存储。看看:

git cat-file tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | od -c

而是树数据是<mode> SP <filename> NUL <hash>的序列,其中<mode>是字符串格式,<hash>是20字节的SHA1。

答案 1 :(得分:2)

默认情况下,

echo会插入换行符,除非您指定-n(省略换行符)标记。

此外,blob ID不是以ASCII格式存储,而是以二进制值存储。这导致对象大小为32(而不是21)。

以下命令将为您提供正确的输出:

echo -en 'tree 32\x00100644 rose\x00\xaa\x82\x37\x28\xea\x7d\x59\x2a\xcc\x69\xb3\x68\x75\xa4\x82\xcd\xf3\xfd\x5c\x8d' | sha1sum

答案 2 :(得分:1)

树对象的格式如下:

tree SIZE\0ENTRIES

SIZE是树的大小 ENTRIES是一个序列,其中每个元素表示树引用的对象 每个对象条目的格式如下:

MODE NAME\0BSHA

MODE是:

  • 100644表示正常文件,
  • 100755表示可执行文件,
  • 120000表示符号链接,
  • 040000表示树对象。

NAME是目录或文件名 BSHA是对象ID的二进制表示。

关于OP的例子,让我们得到对上层树(master分支)的引用:

$ git write-tree
05b217bb859794d08bb9e4f7f04cbda4b207fbe9

虽然我将使用这个树,但以下内容适用于每棵树 前6个字符(05b217)就足够了。

人类可读格式的树内容由:

给出
$ git ls-tree 05b217
100644 blob aa823728ea7d592acc69b36875a482cdf3fd5c8d    rose

您可以将git ls-tree替换为git cat-file -p

二进制格式

相似
$ git cat-file tree 05b217
100644 rose ▒▒7(▒}Y*▒i▒hu▒▒▒▒▒\▒%

实际内容还包含初始字符串tree [content size]\0 要获得它,您可以使用2/38哈希格式解压缩存储.git文件夹中的树的文件:

$ openssl zlib -d -in .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9
tree 32 100644 rose ▒▒7(▒}Y*▒i▒hu▒▒▒▒▒\▒%

鉴于存储在树中并通过ls-tree可用的对象,可以生成使用awk脚本存储的(实际)内容:

$ git ls-tree 05b217 | 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 32 100644 rose ▒▒7(▒}Y*▒i▒hu▒▒▒▒▒\▒%

为了更好地理解输出,我使用转义序列生成它的一个版本:

$ git ls-tree 05b217 | 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 92 100644 rose \xaa\x82\x37\x28\xea\x7d\x59\x2a\xcc\x69\xb3\x68\x75\xa4\x82\xcd\xf3\xfd\x5c\x8d%     

将此输出与git ls-tree 05b217的先前输出进行比较。

我现在使用不同的方法来树形哈希生成

使用树的文件存储版本:

$ openssl zlib -d -in .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | shasum
05b217bb859794d08bb9e4f7f04cbda4b207fbe9 *-

使用我的awk生成的内容:

$ git ls-tree 05b217 | 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
05b217bb859794d08bb9e4f7f04cbda4b207fbe9 *-

最后是git mktree命令:

# git ls-tree 05b217 | git mktree    
05b217bb859794d08bb9e4f7f04cbda4b207fbe9    

获得的哈希值始终相同。