中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

Git 內(nèi)部原理之 Git 對象哈希

2018-07-02    來源:importnew

容器云強勢上線!快速搭建集群,上萬Linux鏡像隨意使用

在上一篇文章中,將了數(shù)據(jù)對象、樹對象和提交對象三種Git對象,每種對象會計算出一個hash值。那么,Git是如何計算出Git對象的hash值?本文的內(nèi)容就是來解答這個問題。

Git對象的hash方法

Git中的數(shù)據(jù)對象、樹對象和提交對象的hash方法原理是一樣的,可以描述為:

header = "<type> " + content.length + "\0"
hash = sha1(header + content)

上面公式表示,Git在計算對象hash時,首先會在對象頭部添加一個header。這個header由3部分組成:第一部分表示對象的類型,可以取值blob、treecommit以分別表示數(shù)據(jù)對象、樹對象、提交對象;第二部分是數(shù)據(jù)的字節(jié)長度;第三部分是一個空字節(jié),用來將headercontent分隔開。將header添加到content頭部之后,使用sha1算法計算出一個40位的hash值。

在手動計算Git對象的hash時,有兩點需要注意:
1.header中第二部分關(guān)于數(shù)據(jù)長度的計算,一定是字節(jié)的長度而不是字符串的長度
2.header + content的操作并不是字符串級別的拼接,而是二進制級別的拼接。

各種Git對象的hash方法相同,不同的在于:
1.頭部類型不同,數(shù)據(jù)對象是blob,樹對象是tree,提交對象是commit
2.數(shù)據(jù)內(nèi)容不同,數(shù)據(jù)對象的內(nèi)容可以是任意內(nèi)容,而樹對象和提交對象的內(nèi)容有固定的格式。

接下來分別講數(shù)據(jù)對象、樹對象和提交對象的具體的hash方法。

數(shù)據(jù)對象

數(shù)據(jù)對象的格式如下:

blob <content length><NULL><content>

從上一篇文章中我們知道,使用git hash-object可以計算出一個40位的hash值,例如:

$ echo -n "what is up, doc?" | git hash-object --stdin
bd9dbf5aae1a3862dd1526723246b20206e5fc37

注意,上面在echo后面使用了-n選項,用來阻止自動在字符串末尾添加換行符,否則會導(dǎo)致實際傳給git hash-objectwhat is up, doc?\n,而不是我們直觀認為的what is up, doc?

為驗證前面提到的Git對象hash方法,我們使用openssl sha1來手動計算what is up, doc?的hash值:

$ echo -n "blob 16\0what is up, doc?" | openssl sha1
bd9dbf5aae1a3862dd1526723246b20206e5fc37

可以發(fā)現(xiàn),手動計算出的hash值與git hash-object計算出來的一模一樣。

在Git對象hash方法的注意事項中,提到header中第二部分關(guān)于數(shù)據(jù)長度的計算,一定是字節(jié)的長度而不是字符串的長度。由于what is up, doc?只有英文字符,在UTF8中恰好字符的長度和字節(jié)的長度都等于16,很容易將這個長度誤解為字符的長度。假設(shè)我們以中文來試驗:

$ echo -n "中文" | git hash-object --stdin
efbb13322ba66f682e179ebff5eeb1bd6ef83972
$ echo -n "blob 2\0中文" | openssl sha1
d1dc2c3eed26b05289bddb857713b60b8c23ed29

我們可以看到,git hash-objectopenssl sha1計算出來的hash值根本不一樣。這是因為中文兩個字符作為UTF格式存儲后的字符長度不是2,具體是多少呢?可以使用wc來計算:

$ echo -n "中文" | wc -c
       6

中文字符串的字節(jié)長度是6,重新手動計算發(fā)現(xiàn)得出的hash值就能對應(yīng)上了:

$ echo -n "blob 6\0中文" | openssl sha1
efbb13322ba66f682e179ebff5eeb1bd6ef83972

樹對象

樹對象的內(nèi)容格式如下:

tree <content length><NUL><file mode> <filename><NUL><item sha>...

需要注意的是,<item sha>部分是二進制形式的sha1碼,而不是十六進制形式的sha1碼。

我們從上一篇文章摘出一個樹對象做實驗,其內(nèi)容如下:

$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
100644 blob 83baae61804e65cc73a7201a7252750c76066a30  test.txt

我們首先使用xxd83baae61804e65cc73a7201a7252750c76066a30轉(zhuǎn)換成為二進制形式,并將結(jié)果保存為sha1.txt以方便后面做追加操作:

$ echo -n "83baae61804e65cc73a7201a7252750c76066a30" | xxd -r -p > sha1.txt
$ cat tree-items.txt
???a?Ne?s? rRu
              vj0%

接下來構(gòu)造content部分,并保存至文件content.txt

$ echo -n "100644 test.txt\0" | cat - sha1.txt > content.txt
$ cat content.txt
100644 test.txt???a?Ne?s? rRu
                             vj0%

計算content的長度:

$ cat content.txt | wc -c
      36

那么最終該樹對象的內(nèi)容為:

$ echo -n "tree 36\0" | cat - content.txt
tree 36100644 test.txt???a?Ne?s? rRu
                                    vj0%

最后使用openssl sha1計算hash值,可以發(fā)現(xiàn)和實驗的hash值是一樣的:

$ echo -n "tree 36\0" | cat - content.txt | openssl sha1
d8329fc1cc938780ffdd9f94e0d364e0ea74f579

提交對象

提交對象的格式如下:

commit <content length><NUL>tree <tree sha>
parent <parent sha>
[parent <parent sha> if several parents from merges]
author <author name> <author e-mail> <timestamp> <timezone>
committer <author name> <author e-mail> <timestamp> <timezone>
<commit message>

我們從上一篇文章摘出一個提交對象做實驗,其內(nèi)容如下:

$ echo 'first commit' | git commit-tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
db1d6f137952f2b24e3c85724ebd7528587a067a
$ git cat-file -p db1d6f137952f2b24e3c85724ebd7528587a067a
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author jingsam <jing-sam@qq.com> 1528022503 +0800
committer jingsam <jing-sam@qq.com> 1528022503 +0800
first commit

這里需要注意的是,由于echo 'first commit'沒有添加-n選項,因此實際的提交信息是first commit\n。使用wc計算出提交內(nèi)容的字節(jié)數(shù):

$ echo -n "tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author jingsam <jing-sam@qq.com> 1528022503 +0800
committer jingsam <jing-sam@qq.com> 1528022503 +0800
first commit\n" | wc -c
     163

那么,這個提交對象的header就是commit 163\0,手動把頭部添加到提交內(nèi)容中:

commit 163\0tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author jingsam <jing-sam@qq.com> 1528022503 +0800
committer jingsam <jing-sam@qq.com> 1528022503 +0800
first commit\n

使用openssl sha1計算這個上面內(nèi)容的hash值:

$ echo -n "commit 163\0tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author jingsam <jing-sam@qq.com> 1528022503 +0800
committer jingsam <jing-sam@qq.com> 1528022503 +0800
first commit\n" | openssl sha1
db1d6f137952f2b24e3c85724ebd7528587a067a

可以看見,與實驗的hash值是一樣的。

總結(jié)

這篇文章詳細地分析了Git中的數(shù)據(jù)對象、樹對象和提交對象的hash方法,可以發(fā)現(xiàn)原理是非常簡單的。數(shù)據(jù)對象和提交對象打印出來的內(nèi)容與存儲內(nèi)容組織是一模一樣的,可以很直觀的理解。對于樹對象,其打印出來的內(nèi)容和實際存儲是有區(qū)別的,增加了一些實現(xiàn)上的難度。例如,使用二進制形式的hash值而不是直觀的十六進制形式,我現(xiàn)在還沒有從已有資料中搜到這么設(shè)計的理由,這個問題留待以后解決。

標簽: ssl

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請與原作者聯(lián)系。

上一篇:RocketMQ 源碼學(xué)習(xí) 2 : Namesrv

下一篇:使用 lynis 進行 linux 漏洞掃描