在團隊開發的時候,使用版本控制系統來管理團隊共同開發的原始碼,以及其他開發過中的產物,是目前已經很普遍的情況。即使是個人、沒有牽扯到多人共同開發的情況,也有很多獨立的開發者,在開發時使用版本控制系統,來管理自己的原始碼。版本控制系統在今天,已經是軟體開發中不可或缺的重要元素。

如果沒有版本控制系統
或許很多人開始開發軟體的時候,都沒有使用版本控制系統。所以,一旦遇上了很多人共同開發時,一來沒有一個集中的儲存處,使得每個開發成員人人手上都有一份「各自版本」的原始碼。

由於不同的成員都會對原始碼做修改,這使得每個人的修改要進行合併、進而得到一份容納所有成員修改又不互相衝突的版本,就成了一個存在困難的事情。回顧過去,有利用共用的網路檔案系統來共同存放集體產出的原始碼的,甚至還有用電子郵件反覆不斷交換彼此所更新的原始碼檔案的。

這種管理共有原始碼的模式,想必不少人都經歷過,也都明白其中的苦痛。因為缺乏集中的儲放場所,而眾人又各自進行發展,等到要合併各自的修改時,不是你不小心蓋掉我寫的,就是我不小心蓋掉你寫的。再不然就是不知從何合併起,要花費很多的時間心力,小心地逐一比對,才能完成合併的動作。

除了整合眾人的修改會構成問題之外,在開發的過程中,我們的原始碼也是不斷演化。這中間每一次的演化、進展可能是為了增加某個小功能,也可能是為了修正某個問題,基本上都有其演化的歷程。但是,使用最原始的檔案交換、合併、整合的方式來處理原始碼檔案,我們就不能夠追溯每個檔案每次被修改的時間點、修改者究竟是誰、修改了什麼內容、以及究竟是基於什麼目的來做修改。

當我們需要檢查某個特定版本下的問題,或是砍掉重練倒回到某個特定版本重新開始時,更沒有辦法回溯取得特定時間點下的原始碼版本。我們手上永遠只有一份,那就是最新的版本,很難有什麼回溯的可能。

在缺乏版本控制系統的情況下(尤其是學生時期),透過一些慘痛的開發經驗,讓我們意識到版本控制系統存在的必要性。至今,絕大多數的開發團隊也都是在版本控制系統的協助下,來進行原始碼版本的管理。

版本控制系統的通用概念

我記得,自己最早使用版本控制系統的經驗,是從RCS(Revision Control System)開始。RCS最早的釋出是在1982年,距今已經30年,可見人們早就意識到軟體版本控制系統對於軟體開發的重要性。自RCS之後,有更多更先進的版本控制系統被發展,例如CVS(Concurrent Versions System)就是基於RCS而開發出來的。不過即便如此,版本控制及管理的觀念,其實並沒有太多的不同。即使現今有許多的版本控制系統存在,像是SVN、Git,其觀念都是相通的,只是實作觀念的工具及方法不同。如果明白版本控制系統的一般性觀念,使用不同的系統實作,其差別並不會太大。

因此,對於版本控制系統的了解,應該要首重於版本控制上的基本觀念,以及如何對應到真實開發生活中的流程。每一種流程都有它背後的意義,以及為什麼要這麼做。

舉例來說,版本控制系統允許我們針對原始碼做分支(branch),不同的版本控制系統當然有不同的指令或操作方式來做分支,但為什麼要做分支、以及做完分支之後的效果,則是無論使用什麼版本控制系統都是大致相同的。

我們在運用版本控制系統前,應該首重的是自己是否了解背後的觀念,以及每一種操作所對應的流程及意義,之後再研究如何使用具體的系統及工具來對應。

在一開始,讓我們先來談談版本控制系統的基本觀念,接著再延伸討論版本控制系統的各種操作,應當如何對應到一般開發生活中的諸般流程。

簡單來說,版本控制系統都有以下的幾個共通的觀念。首先是「檔案庫(repository)」。

檔案庫指的是,版本控制系統儲存受管制檔案所有版本的地方,包括了歷史上所有的版本,以及目前最新的版本。以現在大多數人使用的系統,檔案庫幾乎都是放到特定的伺服器上、甚至是雲端環境之上。不過,早期如RCS之類的檔案庫,都是在你執行指令的本機之上。

而「工作複本(Working Copy)」,指的便是從檔案庫中取得一份複本,複製於本機端供開發者進行開發活動。

在我們開發的過程中,可能會設定若干個里程碑,當這些里程碑到達的條件滿足時,我們會希望特別的標示目前的工作產出(例如所有的原始碼或文件),共同構成了一個穩定的版本,此時,我們就會說這是一個基準線(baseline)。

對版本控制系統的基本操作

那麼我們會對版本控制系統進行那些操作呢?一般來說,在初次建立檔案庫時,你可能會採取「匯入(import)」的操作,將本地端的成組檔案,直接匯入至檔案庫中。而初次建立工作複本時,則會利用「取出(check out)」的操作,將檔案庫中的最新內容取出,並且放置到本機的檔案系統中。

當然,開發者的工作就是針對自己本機端的工作複本進行修改,因此會對一個或多個的檔案進行了「更動(change)」。每次發生了更動,該檔案的「版次(revision)」就會增加。

當你完成了修改動作之後,你就必須將修改的部份「提交(commit)」回檔案庫中。每次提交時,提交者必須撰寫「提交日誌(Commit Log)」來描述這次所提交的內容做了什麼樣的更動,甚至是基於什麼樣的原因必須做此更動。若是其他開發成員也對檔案庫中的其他檔案進行了更動,並且提交了,那麼你就可以進行「更新(update)」的動作,來取得他們所做的更動。

倘若,很不幸的,在你提交某個檔案之前,已經有其他的成員針對該檔案進行了更動,那麼你就必須先更新他(或他們)所更動後的結果。由於他們對該檔案做了一些修改,而你也做了一些修改,所以,必須要整合雙邊所做的修改,此時,版本控制系統就會嘗試進行「合併(Merge)」。這個合併的動作是基於文字的變化來做的,因此,可能會發生自動判斷上的錯誤(很多奇怪的臭蟲反而因此而產生了),也有可能因為雙邊的修改發生了「衝突(Conflict)」,使得版本控制系統無法順利的進行合併。此時,版本控制系統會提示你、並且在系統中標示該檔案發生了衝突的情況,必須由你自行介入來「解決衝突(Resolve Conflict)」。

通常,版本控制系統會在檔案中發生衝突的地方加上特定的註記字元,使得檔案無法順利被編譯或是被運用。這迫使你在將該檔案提交至系統前,必須自力解決衝突。

有時候,檔案的變化歷程,並不見得是單線進行,有時候會基於一些目的,在某個版次之後進行了「分支(Branch)」的動作,而分支最終還有可能再併入主幹之中。最後,當我們要釋出某一個版本,或是基於特殊目的,要將檔案庫中所有檔案所在的版次,進行特殊的標註時,我們會下「標記(tag)」。

以上,便是版本控制系統一般性的觀念及操作。在下一回中,我們會開始談這些觀念和操作與日常開發生活及流程的關係。

 

專欄作者

熱門新聞

Advertisement