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