終于有人把 Git 的數(shù)據(jù)模型講清楚了!
上一篇,我們講了 Git 的前世今生,神——Linus在 10 天內(nèi)就創(chuàng)造了 Git 的第一版,這一篇,我們來(lái)探究一下 Git 的數(shù)據(jù)模型。
由于公眾號(hào)的文章發(fā)布后不能修改,也沒辦法加個(gè)統(tǒng)一的目錄作為索引頁(yè),所以二哥就把《Java 程序員進(jìn)階之路》的系列文章開源到了 GitHub(點(diǎn)擊閱讀原文可以直接跳轉(zhuǎn)):
https://github.com/itwanger/toBeBetterJavaer
每天看著 star 數(shù)(目前已有 716 個(gè) star)的上漲我心里非常的開心,希望越來(lái)越多的 Java 愛好者能因?yàn)檫@個(gè)開源項(xiàng)目而受益,而越來(lái)越多人的 star,也會(huì)激勵(lì)我繼續(xù)更新下去~
盡管 Git 的接口有些難懂,但它底層的設(shè)計(jì)和思想?yún)s非常的優(yōu)雅。難懂的接口只能靠死記硬背,但優(yōu)雅的底層設(shè)計(jì)則非常容易理解。我們可以通過(guò)一種自底向上的方式來(lái)學(xué)習(xí) Git,先了解底層的數(shù)據(jù)模型,再學(xué)習(xí)它的接口。可以這么說(shuō),一旦搞懂了 Git 的數(shù)據(jù)模型,再學(xué)習(xí)它的接口并理解這些接口是如何操作數(shù)據(jù)模型的就非常容易了。
進(jìn)行版本控制的方法很多,Git 擁有一個(gè)精心設(shè)計(jì)的模型,這使其能夠支持版本控制所需的所有特性,比如維護(hù)歷史記錄、支持分支和團(tuán)隊(duì)協(xié)作。
快照
Git 將頂級(jí)目錄中的文件和文件夾稱作集合,并通過(guò)一系列快照來(lái)管理歷史記錄。在 Git 的術(shù)語(yǔ)中,文件被稱為 blob 對(duì)象(數(shù)據(jù)對(duì)象),也就是一組數(shù)據(jù)。目錄則被稱為 tree(樹),目錄中可以包含文件和子目錄。
?(tree)
|
+-?foo?(tree)
|??|
|??+?bar.txt?(blob,?contents?=?"hello?world")
|
+-?baz.txt?(blob,?contents?=?"git?is?wonderful")
頂層的樹(也就是 root) 包含了兩個(gè)元素,一個(gè)名為 foo 的子樹(包含了一個(gè) blob 對(duì)象“bar.txt”),和一個(gè) blob 對(duì)象“baz.txt”。
歷史記錄建模:關(guān)聯(lián)快照
版本控制系統(tǒng)是如何和快照進(jìn)行關(guān)聯(lián)的呢?線性歷史記錄是一種最簡(jiǎn)單的模型,它包含了一組按照時(shí)間順序線性排列的快照。不過(guò),出于種種原因,Git 沒有采用這種模型。
在 Git 中,歷史記錄是一個(gè)由快照組成的有向無(wú)環(huán)圖。“有向無(wú)環(huán)圖”,聽起來(lái)很高大上,但其實(shí)并不難理解。我們只需要知道這代表 Git 中的每個(gè)快照都有一系列的父輩,也就是之前的一系列快照。這些快照通常被稱為“commit”,看起來(lái)好像是下面這樣:
o?<--?o?<--?o?<--?o
????????????^??
?????????????\
??????????????---?o?<--?o
o 表示一次 commit,也就是一次快照。箭頭指向了當(dāng)前 commit 的父輩。在第三次 commit 之后,歷史記錄分叉成了兩條獨(dú)立的分支,這可能是因?yàn)橐瑫r(shí)開發(fā)兩個(gè)不同的特性,它們之間是相互獨(dú)立的。開發(fā)完成后,這些分支可能會(huì)被合并為一個(gè)新的 commit,這個(gè)新的 commit 會(huì)同時(shí)包含這些特性,看起來(lái)好像是下面這樣:
o?<--?o?<--?o?<--?o?<----?o
????????????^????????????/
?????????????\??????????v
??????????????---?o?<--?o
Git 中的 commit 是不可改變的。當(dāng)然了,這并不意味著不能被修改,只不過(guò)這種“修改”實(shí)際上是創(chuàng)建了一個(gè)全新的提交記錄。
數(shù)據(jù)模型及其偽代碼表示
以偽代碼的形式來(lái)學(xué)習(xí) Git 的數(shù)據(jù)模型,可能更加通俗易懂。
//?文件是一組數(shù)據(jù)
type?blob?=?array
//?一個(gè)包含了文件和子目錄的目錄
type?tree?=?map
//?每個(gè)?commit?都包含了一個(gè)父輩,元數(shù)據(jù)和頂層樹
type?commit?=?struct?{
????parent:?array?//?父輩
????author:?string?//?作者
????message:?string?//?信息
????snapshot:?tree?//?快照
}
對(duì)象和內(nèi)存尋址
Git 中的對(duì)象可以是 blob、tree 或者 commit:
type?object?=?blob?|?tree?|?commit
Git 在存儲(chǔ)數(shù)據(jù)的時(shí)候,所有的對(duì)象都會(huì)基于它們的安全散列算法進(jìn)行尋址。
objects?=?map
def?store(object):
????id?=?sha1(object)
????objects[id]?=?object
def?load(id):
????return?objects[id]
blob、tree 和 commit 一樣,都是對(duì)象。當(dāng)它們引用其他對(duì)象時(shí),并沒有真正在硬盤上保存這些對(duì)象,而是僅僅保存了它們的哈希值作為引用。
還記得之前的例子嗎?
?(tree)
|
+-?foo?(tree)
|??|
|??+?bar.txt?(blob,?contents?=?"hello?world")
|
+-?baz.txt?(blob,?contents?=?"git?is?wonderful")
root 引用的 foo 和 baz.txt 就像下面這樣:
100644?blob?4448adbf7ecd394f42ae135bbeed9676e894af85????baz.txt
040000?tree?c68d233a33c5c06e0340e4c224f0afca87c8ce87????foo
引用
所有的快照都可以通過(guò)它們的哈希值來(lái)標(biāo)記,但 40 位的十六進(jìn)制字符實(shí)在是太難記了,很不方便。針對(duì)這個(gè)問(wèn)題,Git 的解決辦法是給這些哈希值賦予一個(gè)可讀的名字,也就是引用(reference),引用是指向 commit 的指針,與對(duì)象不同,它是可變的,可以被更新,指向新的 commit。通常,master 引用通常會(huì)指向主分支的最新一次 commit。
references?=?map
def?update_reference(name,?id):
????references[name]?=?id
def?read_reference(name):
????return?references[name]
def?load_reference(name_or_id):
????if?name_or_id?in?references:
????????return?load(references[name_or_id])
????else:
????????return?load(name_or_id)
這樣,Git 就可以使用“master”這樣容易被記住的名稱來(lái)表示歷史記錄中特定的 commit,而不需要再使用一長(zhǎng)串的十六進(jìn)制字符了。
在 Git 中,當(dāng)前的位置有一個(gè)特殊的索引,它就是“HEAD”。
倉(cāng)庫(kù)
我們可以粗略地給出 Git 倉(cāng)庫(kù)的定義了:對(duì)象 和 引用。
在硬盤上,Git 僅存儲(chǔ)對(duì)象和引用,因?yàn)槠鋽?shù)據(jù)模型僅包含這些東西。所有的 git 命令都對(duì)應(yīng)著對(duì) commit 樹的操作。
參考資料:https://missing-semester-cn.github.io/2020/version-control/
這是《Java 程序員進(jìn)階之路》專欄的第 73 篇(記得點(diǎn)擊「閱讀原文」鏈接去點(diǎn)個(gè) star 哦)。該專欄風(fēng)趣幽默、通俗易懂,對(duì) Java 愛好者極度友好和舒適??,內(nèi)容包括但不限于 Java 基礎(chǔ)、Java 集合框架、Java IO、Java 并發(fā)編程、Java 虛擬機(jī)、Java 企業(yè)級(jí)開發(fā)(SSM、Spring Boot)等核心知識(shí)點(diǎn)。
點(diǎn)擊上方名片,發(fā)送消息「03」 就可以獲取最新版《Java 程序員進(jìn)階之路》PDF 版了,讓我們一起成為更好的 Java 工程師吧,沖!
