Git內(nèi)部原理之Git對象存儲
在Git內(nèi)部原理之Git對象哈希中,講解了Git對象hash的原理,接下來的這篇文章講一講Git對象如何存儲。
原理
數(shù)據(jù)對象、樹對象和提交對象都是存儲在.git/objects目錄下,目錄的結(jié)構(gòu)如下:
- .git
 - |-- objects
 - |-- 01
 - | |-- 55eb4229851634a0f03eb265b69f5a2d56f341
 - |-- 1f
 - | |-- 7a7a472abf3dd9643fd615f6da379c4acb3e3a
 - |-- 83
 - |-- baae61804e65cc73a7201a7252750c76066a30
 
從上面的目錄結(jié)構(gòu)可以看出,Git對象的40位hash分為兩部分:頭兩位作為文件夾,后38位作為對象文件名。所以一個(gè)Git對象的存儲路徑規(guī)則為:
- .git/objects/hash[0, 2]/hash[2, 40]
 
這里就產(chǎn)生了一個(gè)疑問:為什么Git要這么設(shè)計(jì)目錄結(jié)構(gòu),而不直接用Git對象的40位hash作為文件名?原因是有兩點(diǎn):
- 有些文件系統(tǒng)對目錄下的文件數(shù)量有限制。例如,F(xiàn)AT32限制單目錄下的***文件數(shù)量是65535個(gè),如果使用U盤拷貝Git文件就可能出現(xiàn)問題。
 - 有些文件系統(tǒng)訪問文件是一個(gè)線性查找的過程,目錄下的文件越多,訪問越慢。
 
在Git內(nèi)部原理之Git對象哈希中,我們知道Git對象會在原內(nèi)容前加個(gè)一個(gè)頭部:
- store = header + content
 
Git對象在存儲前,會使用zlib的deflate算法進(jìn)行壓縮,即簡要描述為:
- zlib_store = zlib.deflate(store)
 
壓縮后的zlib_store按照Git對象的路徑規(guī)則存儲到.git/objects目錄下。
總結(jié)下Git對象存儲的算法步驟:
- 計(jì)算content長度,構(gòu)造header;
 - 將header添加到content前面,構(gòu)造Git對象;
 - 使用sha1算法計(jì)算Git對象的40位hash碼;
 - 使用zlib的deflate算法壓縮Git對象;
 - 將壓縮后的Git對象存儲到.git/objects/hash[0, 2]/hash[2, 40]路徑下;
 
Nodejs實(shí)現(xiàn)
接下來,我們使用Nodejs來實(shí)現(xiàn)git hash-object -w的功能,即計(jì)算Git對象的hash值并存儲到Git文件系統(tǒng)中:
- const fs = require('fs')
 - const crypto = require('crypto')
 - const zlib = require('zlib')
 - function gitHashObject(content, type) {
 - // 構(gòu)造header
 - const header = `${type} ${Buffer.from(content).length}\0`
 - // 構(gòu)造Git對象
 - const store = Buffer.concat([Buffer.from(header), Buffer.from(content)])
 - // 計(jì)算hash
 - const sha1 = crypto.createHash('sha1')
 - sha1.update(store)
 - const hash = sha1.digest('hex')
 - // 壓縮Git對象
 - const zlib_store = zlib.deflateSync(store)
 - // 存儲Git對象
 - fs.mkdirSync(`.git/objects/${hash.substring(0, 2)}`)
 - fs.writeFileSync(`.git/objects/${hash.substring(0, 2)}/${hash.substring(2, 40)}`, zlib_store)
 - console.log(hash)
 - }
 - // 調(diào)用入口
 - gitHashObject(process.argv[2], process.argv[3])
 
***,測試下能否正確存儲Git對象:
- $ node index.js 'hello, world' blob
 - 8c01d89ae06311834ee4b1fab2f0414d35f01102
 - $ git cat-file -p 8c01d89ae06311834ee4b1fab2f0414d35f01102
 - hello, world
 
由此可見,我們生成了一個(gè)合法的Git數(shù)據(jù)對象,證明算法是正確的。















 
 
 









 
 
 
 