免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項超值服

開通VIP
版本管理三國志 (CVS, Subversion, git)

作者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉(zhuǎn)載,也請保留這段聲明。謝謝!

 

最近有一則和git有關(guān)的新聞很火:

12306的搶票插件拖垮了GitHub (GitHub基于git)

git是一款版本控制軟件(VCS,Version Control System)。VCS通常用于管理開發(fā)過程中的源代碼文件。VCS是軟件開發(fā)的好幫手。當(dāng)軟件本身在發(fā)布時獲取大量關(guān)注時,VCS躲在幕后默默管理和記錄軟件的開發(fā)和發(fā)布進(jìn)程。git頗有戲劇性的借春運(yùn)搶票火了一把,也讓許多人好奇什么是git,什么是VCS。我復(fù)習(xí)了一下VCS的歷史,忽然有些讀三國時的你方唱罷我登場的感覺,就想寫一個VCS版本的三國志。

現(xiàn)在最常見的VCS軟件(同時也是開源的VCS軟件)有CVS, Subversion和git。CVS曾經(jīng)雄霸一時,至今還管理著大量的開發(fā)項目。Subversion青出于藍(lán),對CVS進(jìn)行改進(jìn),大有取而代之的勢頭。git另辟蹊徑,依仗Linux的名號,并借GitHub的推廣攻城略地。VCS領(lǐng)域激烈的爭斗正反映了軟件開發(fā)項目的紅火勢頭。

 

斬白蛇而起

早期(1970年到1980年代)的軟件開發(fā)大部分是愉快的個人創(chuàng)作。比如UNIX下的sed是L. E. McMahon寫的,Python的第一個編譯器是Guido寫的,Linux最初的內(nèi)核是Linus寫的 (好吧,awk是個例外,它的名字是三位作者的首字母,但也只是三個人)。這些程序員可以用手工的方式進(jìn)行備份,并以注釋或者新建文本文件來記錄變動。

正如現(xiàn)在普通用戶常做的,當(dāng)時的程序員常用cp備份:

$cp dev.c dev.bak

更有條理一些的程序員會加上一個時間標(biāo)記,比如:

$cp dev.c dev.bak.19890908

程序員很可能會用vi創(chuàng)建一個LOG文件來做日志:

1989-09-08 02:00:00Old input method is stupidAdd command-line input function

在一個版本發(fā)布的時候,程序員可能做一個tar歸檔,將所有的文件歸為同一個.tar文件。

$tar -cf project_v1.0.tar project

上面的工具構(gòu)成了一套人工VCS。上面的這套組合也非常符合UNIX的模塊化理念:讓每個應(yīng)用專注于一個小的功能,使用者根據(jù)需要,將這些功能連接起來。你還可以寫一個shell腳本,將上面的功能都寫在里面。當(dāng)需要的時候,調(diào)用該腳本就可以了。

(這樣一個shell腳本并不復(fù)雜,而且挺有用的,可以作為學(xué)習(xí)shell編程的小練習(xí))

 

再說一下早期的合作開發(fā)模式。如在Python簡史中看到的,Guido通過電子郵件接收補(bǔ)丁(patch),并將補(bǔ)丁應(yīng)用到原來的代碼文件。實際上,一個補(bǔ)丁(patch)的主要功能是描述兩個文件的改變(change, or file delta)。 假設(shè)我們有兩個文件a.c和b.c內(nèi)容分別為:
a.c (有bug的代碼)

int sum(int a, int b){  int c;  c = a + 1;  return c;} 

b.c (修正后的代碼)

int sum(int a, int b){  int c;  c = a + b;  return c;}

 

在UNIX系統(tǒng)下,運(yùn)行

$diff a b > iss01.patch

iss01.patch就是一個補(bǔ)丁文件,它看起來如下:

4c4<   c = a + 1;--->   c = a + b;

這個補(bǔ)丁表示,更改原文件第四行的c = a + 1;,改為c = a + b;,更改后的這一行位于新的文件的第四行。

 

使用patch命令將iss01.patch應(yīng)用到a.c上,相當(dāng)于將 b.c-a.c 的改變作用在a上,a.c將和b.c有一樣的內(nèi)容:

$patch a.c < iss01.patch

當(dāng)我發(fā)現(xiàn)a.c的代碼有錯誤時,可以將我修改后的b.c與原來的a.c做diff獲得補(bǔ)丁文件,并將補(bǔ)丁發(fā)給Guido,并告訴他該補(bǔ)丁是為了修正a.c代碼中的加法錯誤。Guido確認(rèn)之后,就可以使用patch應(yīng)用該補(bǔ)丁了。在后面我們將看到,這種diff-patch的工作方式被VCS不同程度的采用。

 

東漢末年

早在70年代末80年代初,VCS的概念已經(jīng)存在,比如UNIX平臺的RCS (Revision Control System)。RCS是由Walter F. Tichy使用C開發(fā)。RCS對文件進(jìn)行集中式管理,主要目的是避免多人合作情況下可能出現(xiàn)的沖突。如果多用戶同時寫入同一個文件,其寫入結(jié)果可能相互混合和覆蓋,從而造成結(jié)果的混亂。你可以將文件交給RCS管理。RCS允許多個用戶同時讀取文件,但只允許一個用戶鎖定(locking)并寫入文件 (類似于多線程的mutex)。這樣,當(dāng)一個程序員登出(check-out,見RCS的co命令)某個文件并對文件進(jìn)行修改的時候。只有在這個程序完成修改,并登入(check-in,見RCS的ci命令)文件時,其他程序員才能登出文件?;旧蟁CS用戶所需要的,就是co和ci兩個命令。在co和ci之間,用戶可以對原文件進(jìn)行許多改變(change, or file delta)。一旦重新登入文件,這些改變將保存到RCS系統(tǒng)中。通過check-in將改變永久化的過程叫做提交(commit)。

RCS互斥寫入

RCS的互斥寫入機(jī)制避免了多人同時修改同一個文件的可能,但代價是程序員長時間的等待,給團(tuán)隊合作帶來不便。如果某個程序員登出了某個文件,而忘記登入,那他就要面對隊友的怒火了。(從這個角度上來說,RCS造成的問題甚至大于它所解決的問題……)

文件每次commit都會創(chuàng)造一個新的版本(revision)。RCS給每個文件創(chuàng)建了一個追蹤文檔來記錄版本的歷史。這個文檔的名字通常是原文件名加后綴,v (比如main.c的追蹤文檔為main.c,v)。追蹤文檔中包括:最新版本的文件內(nèi)容,每次check-in的發(fā)生時間和用戶,每次check-in發(fā)生的改變。在最新文檔內(nèi)容的基礎(chǔ)上,減去歷史上發(fā)生的改變,就可以恢復(fù)到之前的歷史版本。這樣,RCS就實現(xiàn)了備份歷史和記錄改變的功能。

 

RCS歷史版本追蹤

 

相對與后來的版本管理軟件,RCS純粹線性的開發(fā)方式非常不利于團(tuán)隊合作。但RCS為多用戶寫入沖突提供了一種有效的解決方案。RCS的版本管理功能逐漸被其他軟件(比如CVS)取代,但時至今日,它依然是常用的系統(tǒng)管理工具。RCS就像是東漢王室,飄搖多年而不倒。

 

挾天子,令諸侯

1986年,Dick Grune寫了一系列的shell腳本用于版本管理,并最終以這些腳本為基礎(chǔ),構(gòu)成了CVS (Concurrent Versions System)。CVS后來用C語言重寫。CVS是開源軟件。在當(dāng)時,Stallman剛剛舉起GNU的大旗,掀起開源允許的序幕。CVS被包含在GNU的軟件包中,并因此得到廣泛的推廣,最終擊敗諸多商業(yè)版本的VCS,呈一統(tǒng)天下之勢。

CVS繼承了RCS的集中管理的理念。在CVS管理下的文件構(gòu)成一個庫(repository)。與RCS的鎖定文件模式不同,CVS采用復(fù)制-修改-合并(copy-modify-merge)的模式,來實現(xiàn)多線開發(fā)。CVS引進(jìn)了分支(branch)的概念。多個用戶可以從主干(也就是中心庫)創(chuàng)建分支。分支是主干文件在本地復(fù)制的副本。用戶對本地副本進(jìn)行修改。用戶可以在分支提交(commit)多次修改。用戶在分支的工作結(jié)束之后,需要將分支合并到主干中,以便讓其他人看到自己的改動。所謂的合并,就是CVS將分支上發(fā)生的變化應(yīng)用到主干的原文件上。比如下面的過程中,我們從r1.1分支出rb1.1.2.*,并最終合并回主干,構(gòu)成r1.2

 copy-modify-merge

 

CVS與RCS類似,使用,v文件記錄改變,以便追蹤歷史。在合并的過程中,CVS將兩個change應(yīng)用于r1.1,就得到了r1.2:

r1.2 = r1.1 + change(rb1.1.2.2 - rb1.1.2.1) + change(rb1.1.2.1-r1.1)

上面的兩個改變都記錄在,v文件中,所以很容易提取。

 

在多用戶情況下,可以創(chuàng)建多個分支進(jìn)行開發(fā),比如:

在這樣的多分支合并的情況下,有可能出現(xiàn)沖突(colliding)。比如上圖中,第一次合并和第二次合并都對r1.1文件的同一行進(jìn)行了修改,那么r1.3將不知道如何去修改這一行 (第二次合并比圖示的要更復(fù)雜一些,分支需要先將主干拉到本地,合并過之后傳回主干,但這一細(xì)節(jié)并不影響我們這里的討論)。CVS要求沖突發(fā)生時的用戶手動解決沖突。用戶可以調(diào)用編輯器,對文件發(fā)生合并沖突的地方進(jìn)行修改,以決定最終版本(r1.3)的內(nèi)容。

 

CVS管理下的每個文件都有一系列獨立的版本號(比如上面的r1.1,r1.2,r1.3)。但每個項目中往往包含有許多文件。CVS用標(biāo)簽(tag)來記錄一個集合,這個集合中的元素是一對(文件名:版本號)。比如我們的項目中有三個文件(file1, file2, file3),我們創(chuàng)建一個v1.0的標(biāo)簽:

tag v1.0 (file1:r1.3) (file2:r1.1) (file3:r1.5)

v1.0的tag中包括了r1.3版本的文件file1,r1.1版本的file2…… 一個項目在發(fā)布(release)的時候,往往要發(fā)布多個文件。標(biāo)簽可以用來記錄該次發(fā)布的時候,是哪些版本的文件被發(fā)布。

 

CVS應(yīng)用在許多重要的開源項目上。在90年代和00年代初,CVS在開源世界幾乎不二選擇 (RCS也是開源的,但正如我們已經(jīng)提到的,RCS無法與CVS媲美)。CVS就像是官渡之戰(zhàn)后的曹魏,挾開源運(yùn)動,號令天下。時至今天,盡管CVS已經(jīng)長達(dá)數(shù)年沒有發(fā)布新版本,我們依然可以在許多項目中看到CVS的身影。

 

青出于藍(lán)

正如曹操的統(tǒng)治富有爭議一樣(比如非漢祚,以臣欺君等等),CVS也有許多常常被人詬病的地方,比如下面幾條:

  • 合并不是原子操作(atomic operation):如果有兩個用戶同時合并,那么合并結(jié)果將是某種錯亂的混合體。如果合并的過程中取消合并,不能撤銷已經(jīng)應(yīng)用的改變。
  • 文件的附加信息沒有被追蹤:一旦納入CVS的管理,文件的附加信息(比如上次讀取時間)就被固定了。CVS不追蹤它所管理文件的附加信息的變化。
  • 主要用于管理ASCII文件:不能方便的管理Binary文件和Unicode文件
  • 分支與合并需要耗費(fèi)大量的時間:CVS的分支和合并非常昂貴。分支需要復(fù)制,合并需要計算所有的改變并應(yīng)用到主干。因此,CVS鼓勵盡早合并分支。

CVS還有其它一些富有爭議的地方。隨著時間,人們對CVS的一些問題越來越感到不滿 (而且程序員喜歡新鮮的東西),Subversion應(yīng)運(yùn)而生。Subversion的開發(fā)者Karl Fogel和Jim Blandy是長期的CVS用戶。贊助開發(fā)的CollabNet, Inc.希望他們寫一個CVS的替代VCS。這個VCS應(yīng)該有類似于CVS的工作方式,但對CVS的缺陷進(jìn)行改進(jìn),并提供一些CVS缺失的功能。這就好像劉備從曹營拉出來單干的劉備一樣。

總體上說,Subversion在許多方面沿襲CVS,也是集中管理庫,通過記錄改變來追蹤歷史,允許分支和合并,但并不鼓勵過多分支。Subversion在一些方面得到改善。Subversion的合并是原子操作。它可以追蹤文件的附加信息,并能夠同樣的管理Binary和Unicode文件。但CVS和Subversion又有許多不同:

  • 與CVS的,v文件存儲模式不同,Subversion采用關(guān)系型數(shù)據(jù)庫來存儲改變集。VCS相關(guān)數(shù)據(jù)變得不透明。
  • CVS中的版本是針對某個文件的,CVS中每次commit生成一個文件的新版本。Subversion中的版本是針對整個文件系統(tǒng)(包含多個文件以及文件組織方式),每次commit生成一個整個項目文件系統(tǒng)樹的新版本。

Subversion依賴類似于硬連接(hard link)的方式來提高效率,避免過多的復(fù)制文件本身。Subversion不會從庫下載整個主干到本地,而只是下載主干的最新版本。

 

在Subversion剛剛誕生的時候,來自CVS用戶的抱怨不斷。他們覺得在Subversion中有太多的改動,有些改動甚至是相對于CVS的倒退。比如CVS中的tag,在Subversion中被改為直接復(fù)制版本的文件系統(tǒng)樹到一個特殊的文件夾。然而,隨著時間的推移,Subversion逐漸推廣 (Subversion已經(jīng)是Apache中自帶的一個模塊了,Subversion應(yīng)用于GCC、SourceForge,新浪APP Engine等項目),并依然有活躍的開發(fā),而CVS則逐漸沉寂。事實上,許多UNIX的參考書的新版本中,都縮減甚至刪除了CVS的內(nèi)容。

 

別開生面

CVS和Subversion有很多不同的地方。但如果將這兩者和git比較,那么git看起來就像孫權(quán)的碧眼,有一些怪異。

git的作者是Linus Torvald。對,就是寫Linux Kernel的那個Linus Torvald。Linus在貢獻(xiàn)了最初的Linux Kernel源代碼之后,一直領(lǐng)導(dǎo)著Linux Kernel的開發(fā)。Linus Torvald本人相當(dāng)厭惡CVS(以及Subversion)。然而,操作系統(tǒng)內(nèi)核是復(fù)雜而龐大的代碼“怪獸” (2012年的Linux Kernel有1500萬行代碼,Windows的代碼不公開,估計遠(yuǎn)遠(yuǎn)超過這一數(shù)目)。Linux內(nèi)核小組最初使用.tar文件來管理內(nèi)核代碼,但這遠(yuǎn)遠(yuǎn)無法匹配Linux內(nèi)核代碼的增長速度。Linus轉(zhuǎn)而使用BitKeeper作為開發(fā)的VCS工具。BitKeeper是一款分布式的VCS工具,它可以快速的進(jìn)行分支和合并。然而由于使用證書方面的爭議(BitKeeper是閉源軟件,但給Linux內(nèi)核開發(fā)人員發(fā)放免費(fèi)的使用證書),Linus最終決定寫一款開源的分布式VCS軟件:git。

git在英文中比喻一個愚蠢或者不愉快的人(a stupid or unpleasant person)。Linus說這個比喻是在說自己:

I'm an egotistical bastard, and I name all my projects after myself. First "Linux", now "git".

(這里,Linus似乎并不是在貶低自己,見Linus和Eric S. Raymond的爭論: The curse of the gifted)

 

對于一個開發(fā)項目,git會保存blob, tree, commit和tag四種對象。

  • 文件被保存為blob對象。
  • 文件夾被保存為tree對象。tree對象保存有指向文件或者其他tree對象指針。

上面兩個對象類似于一個UNIX的文件系統(tǒng),構(gòu)成了一個文件系統(tǒng)樹。

  • 一個commit對象代表了某次提交,它保存有修改人,修改時間和附加信息,并指向一個文件樹。這一點與Subversion類似,即每次提交為一個文件系統(tǒng)樹。
  • 一個tag對象包含有tag的名字,并指向一個commit對象。

虛線下面的對象構(gòu)成了一個文件系統(tǒng)樹。在git中,一次commit實際上就是一次對文件系統(tǒng)樹的快照(snapshot)。

 

每個對象的內(nèi)容的checksum校驗(checksum校驗可參閱IP頭部的checksum)都經(jīng)過SHA1算法的HASH轉(zhuǎn)換。每個對象都對應(yīng)一個40個字符的HASH值。每個對象對應(yīng)一個HASH值。兩個內(nèi)容不同的對象不會有相同的HASH值(SHA1有可能發(fā)生碰撞,但概率非常非常非常低)。這樣,git可以隨時識別各個對象。通過HASH值,我們可以知道這個對象是否發(fā)生改變。

比如一個文件LOG,它包含一下內(nèi)容:

aaa

這個文件的HASH碼為72943a16fb2c8f38f9dde202b7a70ccc19c52f34

如果我們修改這個文件,成為

aaa

bbb

這個文件的HASH碼變成dbee0265d31298531773537e6e37e4fd1ee71d62

所以,git只需看對象的HASH碼,就可以知道該對象是否發(fā)生改變。

 

在整個開發(fā)過程中,可能會有許多次提交(commit)。每次commit的時候,git并不總是復(fù)制所有的對象。git會檢驗所有對象的HASH值。如果該對象的HASH值已經(jīng)存在,說明該對象已經(jīng)保存過,并且沒有發(fā)生改變,所以git只需要調(diào)整新建tree或者commit中的指針,讓它們指向已經(jīng)保存過的對象就可以了。git在commit的時候,只會新建HASH值發(fā)生改變的對象。如下圖所示,我們創(chuàng)建新的commit的時候,只需要新建一個commit對象,一個tree對象和一個blob對象就足夠了,而不需要新建整個文件系統(tǒng)樹。

 

可以看到,與CVS,Subversion保存改變(file delta)的方式形成對照,git保存的不是改變,而是此時的文件本身。由于不需要遵循改變路徑來計算歷史版本,所以git可以快速的查閱歷史版本。git可以直接提取兩個commit所保存的文件系統(tǒng)樹,并迅速的算出兩個commit之間的改變。

 

同樣由于上面的數(shù)據(jù)結(jié)構(gòu),git可以很方便的創(chuàng)建分支(branch)。實際上,git的一個分支是一個指向某個commit的指針。合并時,git檢查兩個分支所指的兩個commit,并找到它們共同的祖先commit。git會分別計算每個commit與祖先發(fā)生的改變,然后將兩個改變合并(同樣,針對同一行的兩個改變可能發(fā)生沖突,需要手工解決沖突)。整個過程中,不需要復(fù)制和遵循路徑計算總的改變,所以效率提高很多。

比如下面的圖1中有兩個分支,一個master和一個develop。我們先沿著develop分支工作,并進(jìn)行了兩次提交(比如修正bug1),而master分支保持不變。隨后沿著master分支,進(jìn)行了兩次提交(比如增加輸入功能),develop保持不變。在最終進(jìn)行圖4中的合并時,我們只需要將C4-C2和C6-C2的兩個改變合并,并作用在C2上,就可以得到合并后的C7。合并之后,兩個分支都指向C7。我們此時可以刪除不需要的分支develop。

由于git創(chuàng)建、合并和刪除分支的成本極為低廉,所以git鼓勵根據(jù)需要創(chuàng)建多個分支。實際上,如果分支位于不同的站點(site),屬于不同的開發(fā)者,那么就構(gòu)成了分布式的多分支開發(fā)模式。每個開發(fā)者都在本地復(fù)制有自己的庫,并可以基于本地庫創(chuàng)建多個本地分支工作。開發(fā)者可以在需要的時候,選取某個本地分支與遠(yuǎn)程分支合并。git可以方便的建立一個分布式的小型開發(fā)團(tuán)隊。比如我和朋友兩人各有一個庫,各自開發(fā),并相互拉對方的庫到本地庫合并(如果上面master,develop代表了兩個屬于不同用戶的分支,就代表了這一情況)。當(dāng)然,git也允許集中式的公共倉庫存在,或者多層的公共倉庫,每個倉庫享有不同的優(yōu)先級。git的優(yōu)勢不在于引進(jìn)了某種開發(fā)模式,而是給了你設(shè)計開發(fā)模式的自由。

正如東吳門閥合作的政治模式,git非集中式的開發(fā)模式讓git成為了后起之秀。生子當(dāng)如孫仲謀,生子當(dāng)如Git Torvald。

(需要注意的是,GitHub盡管以git為核心,但并不是Linus創(chuàng)建的。事實上,Linus不接收來自GitHub的Pull Request。Linus本人將此歸罪于GitHub糟糕的Web UI。但有些搞笑的是,正是GitHub的Web頁面讓許多新手熟悉并開始使用git。好吧,Linus大嬸是在鞭策GitHub。)

 

總結(jié)

和三國志不同,VCS的三國還沒有決出最終勝負(fù)?;蛟SSubversion會繼續(xù)在一些重要項目上發(fā)揮作用,或許git會最終一統(tǒng)江山,或許CVS可以有新的發(fā)展并最終逆襲;又或許,一款新的VCS將取代所有的前輩。VCS激烈的競爭對于程序員來說是好事。一款優(yōu)秀的VCS可以提高了我們管理項目的能力,降低我們犯錯所可能支付的代價。隨著開發(fā)項目越來越龐大和復(fù)雜,這一能力變得越來越不可缺少。花一點時間學(xué)習(xí)VCS,并習(xí)慣在工作中使用VCS,將會有意想不到的回報。

(我平時只用git,經(jīng)驗有限,如果有錯漏,謝謝你的指正)

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
InfoQ: ThoughtWorks開發(fā)人員最喜愛的分布式版本控制系統(tǒng)
『互聯(lián)網(wǎng)架構(gòu)』軟件架構(gòu)
開源點評:源代碼版本控制系統(tǒng)介紹
程序員必備技能之 Git 的體系結(jié)構(gòu)與歷史
Linux 的版本控制
版本管理器的發(fā)展史 - CSDN.NET - CSDN軟件研發(fā)頻道
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服