作者:Susan Potter,翻譯:張吉
Git能夠讓不同的協(xié)作者通過(guò)一個(gè)點(diǎn)對(duì)點(diǎn)的倉(cāng)庫(kù)網(wǎng)絡(luò)對(duì)數(shù)據(jù)內(nèi)容(通常是代碼,當(dāng)然不僅限于代碼)進(jìn)行維護(hù)。它支持分布式的工作流程,能夠讓數(shù)據(jù)內(nèi)容臨時(shí)分離,并最終合并到一起。
本章將闡述Git的內(nèi)部實(shí)現(xiàn)是如何提供以上功能的,以及它和其他版本控制系統(tǒng)(VCS)的區(qū)別。
為了更好地理解Git的設(shè)計(jì)思想,我們有必要先了解一下Git項(xiàng)目的發(fā)源地——Linux內(nèi)核開發(fā)社區(qū)——所面臨的問(wèn)題。
Linux內(nèi)核開發(fā)與其他商業(yè)軟件項(xiàng)目有很大不同,因?yàn)樗拈_發(fā)者眾多,且每個(gè)開發(fā)者的參與程度和對(duì)Linux內(nèi)核代碼的理解有很大差異。多年以來(lái),內(nèi)核代碼一直都是以Tar壓縮文件以及補(bǔ)丁的形式維護(hù)的,而當(dāng)時(shí)的核心開發(fā)團(tuán)隊(duì)一直在尋找一個(gè)能夠滿足他們各方面需求的版本控制系統(tǒng)。
Git就是在這樣的背景下于2005年作為一款開源軟件誕生的。當(dāng)時(shí),Linux內(nèi)核代碼通過(guò)兩種版本控制系統(tǒng)進(jìn)行維護(hù),BitKeeper和CVS,分別由兩組核心開發(fā)團(tuán)隊(duì)使用。BitKeeper相較于當(dāng)時(shí)頗為流行的CVS,提供了一種不同的歷史展示方式。
當(dāng)BitKeeper的所有者BitMover決定收回Linux內(nèi)核開發(fā)人員的使用許可時(shí),Linux Torvalds緊急開啟了一個(gè)項(xiàng)目,也就是后來(lái)的Git。一開始,他通過(guò)編寫一組Shell腳本來(lái)幫助他將郵件中的補(bǔ)丁按順序應(yīng)用到代碼中。這組原始腳本能夠在代碼合并過(guò)程中迅速中斷,讓維護(hù)者能夠進(jìn)行人工干預(yù),修改代碼,然后繼續(xù)合并。
從項(xiàng)目開始之初,Torvalds就為Git制定了一個(gè)目標(biāo)——要和CVS的做法完全相反——同時(shí)還包含了以下三條設(shè)計(jì)目標(biāo):
這些設(shè)計(jì)目標(biāo)都被實(shí)現(xiàn)了,我會(huì)在下文中通過(guò)解析Git的各種做法來(lái)闡述,包括在內(nèi)容管理中使用有向無(wú)環(huán)圖(DAG),頭指針引用,對(duì)象模型,遠(yuǎn)程協(xié)議,以及Git如何追蹤合并樹。
雖然Git設(shè)計(jì)之初受到了很多BitKeeper的影響,但是兩者還是有根本上的區(qū)別的,如Git提供了更多分布式和本地開發(fā)流程,這點(diǎn)是BitKeeper做不到的。Monotone,2003年啟動(dòng)的一個(gè)開源分布式版本控制系統(tǒng),也對(duì)Git的早期開發(fā)產(chǎn)生了影響。
分布式版本控制系統(tǒng)在提供更靈活的工作流程的同時(shí),往往會(huì)增加它的復(fù)雜程度。分布式模型的獨(dú)特優(yōu)點(diǎn)有:
在Git項(xiàng)目的開發(fā)期間,誕生了其他三個(gè)開源分布式版本控制系統(tǒng)(其中Mercurial可以參見《開源軟件架構(gòu)》的第一卷)。這些分布式版本控制系統(tǒng)(dVCS)都提供了非常靈活的工作流程,這是先前的集中式版本控制系統(tǒng)做不到的。注意:Subversion有一款插件名為SVK,由不同的開發(fā)者維護(hù),提供了服務(wù)器之間的同步功能。
目前流行的dVCS包括Bazaar, Darcs, Fossil, Git, Mercurial, 以及Veracity。
現(xiàn)在讓我們回過(guò)頭來(lái)看看Git之外的其他版本控制系統(tǒng)是如何設(shè)計(jì)的。通過(guò)比較他們和Git之間的區(qū)別,可以幫助我們?nèi)ダ斫釭it在架構(gòu)設(shè)計(jì)中的選擇。
版本控制系統(tǒng)通常有三項(xiàng)核心功能(需求):
注意:第三項(xiàng)并不是所有版本控制系統(tǒng)的核心功能。
在VCS中保存內(nèi)容,最普遍的做法是保存增量的修改,或使用有向無(wú)環(huán)圖(DAG)。
增量修改可以反映出兩個(gè)版本之間的內(nèi)容差異,以及一些額外的信息。使用有向無(wú)環(huán)圖保存內(nèi)容則是將特定對(duì)象構(gòu)造成一種樹狀結(jié)構(gòu),作為某一次提交的快照保存下來(lái)(樹狀結(jié)構(gòu)中未發(fā)生變化的對(duì)象是可以重用的)。Git使用有向無(wú)環(huán)圖來(lái)保存內(nèi)容,它所使用的不同對(duì)象類型會(huì)在本文的“對(duì)象數(shù)據(jù)庫(kù)”一節(jié)中有所描述。
在保存歷史、記錄變化方面,大部分VCS使用以下方式之一:
Git使用的還是有向無(wú)環(huán)圖,這次則是用來(lái)保存歷史。每次提交包含了它父節(jié)點(diǎn)的元信息——Git中的一次提交可以擁有0個(gè)或多個(gè)父節(jié)點(diǎn)(理論上沒有個(gè)數(shù)限制)。例如,Git倉(cāng)庫(kù)的第一次提交就沒有父節(jié)點(diǎn),而一次三頭合并則有三個(gè)父節(jié)點(diǎn)。
Git和SVN線性提交的另一個(gè)重要區(qū)別是Git可以直接進(jìn)行分支的創(chuàng)建,并記錄下大部分合并歷史。
libgit2。一開始它并不流行,直到一個(gè)名叫Vincent Marti的學(xué)生在谷歌編程夏令營(yíng)中使用了它。從那以后,Vincent和Github持續(xù)對(duì)libgit2類庫(kù)貢獻(xiàn)代碼,并為其他語(yǔ)言編寫了相應(yīng)類庫(kù),包括Ruby,Python,PHP,.NET,Lua,Object-C等。
Shawn Pearce還開啟了一個(gè)名為JGit的BSD項(xiàng)目,使用純Java語(yǔ)言實(shí)現(xiàn),能夠?qū)it版本庫(kù)進(jìn)行基本的操作。該類庫(kù)現(xiàn)在由Eclipse基金會(huì)維護(hù),用于Eclipse IDE的Git插件中。
還有其他一些有趣的周邊項(xiàng)目,帶有實(shí)驗(yàn)性質(zhì),使用各類數(shù)據(jù)源來(lái)保存Git對(duì)象,如:
以上這些都是獨(dú)立于Git核心工具包之外的項(xiàng)目。
如你所見,我們可以用各種方式來(lái)使用Git,它的表現(xiàn)形式不再只有命令行這一種了,而是成為一種版本控制系統(tǒng)的協(xié)議。
在本文撰寫之時(shí),這些項(xiàng)目都還沒有發(fā)布穩(wěn)定版本,所以還是有很多工作要做,但整體看來(lái)未來(lái)是光明的。
在軟件設(shè)計(jì)中,任何一個(gè)決定都有正反兩面。作為一個(gè)在日常工作中大量使用Git,并且還為Git對(duì)象數(shù)據(jù)庫(kù)開發(fā)了周邊軟件的程序員,我覺得Git目前的組織方式非常棒。因此,下文提到的“經(jīng)驗(yàn)教訓(xùn)”更多的是來(lái)自其他開發(fā)者對(duì)于Git目前設(shè)計(jì)方式的不滿,主要?dú)w咎于Git核心開發(fā)者當(dāng)初做出的決定。
最常見的問(wèn)題在于Git相較于其他CVS不能很好地和IDE進(jìn)行整合,因?yàn)镚it是基于工具包設(shè)計(jì)的,整合起來(lái)會(huì)比較具有挑戰(zhàn)性。
早期Git的實(shí)現(xiàn)是采用shell腳本的方式,不能很好地跨平臺(tái),特別是對(duì)于Windows操作系統(tǒng)。雖然我相信Git開發(fā)者不會(huì)因?yàn)檫@個(gè)問(wèn)題而寢食難安,但這的確阻礙了Git在大型公司內(nèi)的推廣?,F(xiàn)在,有一個(gè)名為Git for Windows的項(xiàng)目由志愿者發(fā)起,及時(shí)地將最新的Git開發(fā)成果移植到Windows平臺(tái)上。
Git工具包的設(shè)計(jì)方式所帶來(lái)的另一個(gè)間接影響是,他的底層命令繁多,會(huì)讓初學(xué)者陷入困境,難以理解Git出錯(cuò)時(shí)拋出的異常信息,最后無(wú)可適從。這就使得Git在某些開發(fā)團(tuán)隊(duì)中的推廣受到阻礙。
即便如此,我仍然對(duì)Git核心項(xiàng)目以及其周邊項(xiàng)目的開發(fā)充滿信心。
更多建議: