iThome

對開發人員而言,版本控制與原始碼編譯器與編譯器一樣,就像是吃飯與呼吸,是日常開發生活的一部分。

版本控制是管理一組檔案的許多版本的流程。它們通常是軟體系統的原始檔案(所以它通常被稱為原始碼控制),但是它也可以是簡單的文件樹改版,或任何其他你想要在檔案系統中儲存的東西。

這是一個非常簡單的工具。但是善用好的版本控制系統有許多好處:

● 它會提供中央協作中心,策劃開發人員一起工作的方法。

● 它會定義且公布最新狀況,除非程式碼被存到系統,否則它不會被整合。其他的工具會連結到這個更新摘要,例如,持續整合、版本工程,及程式碼審核系統。

● 它會維護專案的工作歷史記錄,將每一個特定版本內的具體內容建檔。它是程式碼的時光機器。

這有利於進行軟體考古,在檔案中追蹤改變,來找出組成特定功能的修改項目。它會記錄是誰修改檔案,及其原因。

● 它會集中備份你的工作成果。

● 它會提供一個安全網給開發人員。它會保留實驗、修改的空間,並且在修改的程式無法動作時將它們回復。

● 它會促進工作的節奏與韻律:讓你做一部分的工作、測試它,接著將它checkin。接著繼續做下一個部分的工作。

● 它可以讓許多開發流程同時在同樣的基礎程式上進行,不會受到干擾。

● 它具有可逆性:如果有錯的話,在專案歷史中的任何改變都可以被找出並逆轉。

使用它或失去它

這是一個令人印象深刻的清單。版本控制是開發流程的骨幹,如果沒有它,你就缺乏結構性的支援。

所以,首先,版本控制的黃金定律是:使用它。在任何專案的一開始就採用版本控制。沒有“如果",沒有“但是"。

大部分的現代VCS(version control systems,版本控制系統)都不需要耗費精力去設定,所以沒有什麼藉口可以讓你不使用版本控制。就算是最簡單的原型(這些有害的東西也是一樣,通常會日漸增長,進入產品系統)都在一開始就有它們自己的儲存庫與可追蹤的歷史記錄。

軟體在本質上是不安全的,磁碟上的原始碼就如同數位煙霧:只要揮揮手就可以將它們揮散。我已記不清過去幾年來,自己有多少次刪除不對的東西,或做出錯誤的修改,卻沒有檢查點可以回復。版本控制可解決這種問題。體面、輕量的VCS可引導小型、經常性的check-in,提供許多保護來避免你的愚蠢行為。

 

★重點﹕使用版本控制。它不是一個選項,或是最好可以擁有的工具。它是開發的骨幹。如果沒有它,你的工作會很危險。

 

選一個,隨便都行

這幾年來出現許多不同的VCS系統,從Unix的rcs指令開始(1980年代初),經歷集中式的CVS(在1990年代流行),它的現代化兄弟Subversion(2000年代當道),最後進入現代離散系統的世界,如Git與Mercurial(現在統治2010年代)。有些工具是商業性的,許多其他的都是開放原始碼的。它們的認證、價格、易用性、支援平台、成熟度、可擴充性及功能各有不同。

最重要的差異是運作的模式。已成為歷史的集中系統透過一個中央伺服器來管理存放所有被控制版本的檔案的存儲庫,並用它來掌控所有通訊。這是一個簡單的模式,但任何操作都需要存取伺服器。最近的VCS是離散模式,一種端對端的方式,讓每一台電腦都可以儲存它自己的存儲庫副本。這可產生更多令人印象深刻的工作流,並且讓你可以與存儲庫互動,就算你沒有連結網路。

如果你可以選擇的話,要用哪一種工具?

請優先使用現代、受支援且常規的系統。在最近,Subversion或許是預設的選擇,因為它的價格(免費)、支援平台的範圍(幾乎所有平台,包括你的烤麵包機),與容易使用。但是最近換Git坐上王位。離散式的版本控制系統變得更受歡迎,而且有很好的理由。它們提供更好的工作流程,而且非常實用。但是這種能力也需要代價:Git有個陡峭的學習曲線。

儲存正確的東西

我們建立非常多的檔案,而且很豐富。我們有原始檔案、設定檔、二進位資產、組建腳本、中間組建檔、物件檔、位元組碼、編譯後的可執行檔,還有很多。

要把哪些被儲存在版本控制底下?

針對我們的原始碼專案,有兩種不同的答案。它們並不是完全互相矛盾的。

答案一:儲存所有東西

你必須儲存每一個重新創造軟體需要的檔案。無論它是“二進位"或“原始"檔。將它加入版本控制。好的VCS可以用合理的方式來處理大型的二進位檔,因此你不需要擔心如何管理二進位檔。(而且如果你不在VCS下存放二進位檔,也要用其他方法在其他地方將它們封存並管理版本。)

一開始先適當地設置組建機器,以及使用正確的OS與編譯環境(組建工具、標準程式庫,等等,加上足夠的磁碟空間),因此一個簡單的check out操作就可以讓你取得一個良好的可組建原始碼。

這代表你的存儲庫必須有:

● 所有的原始程式檔

● 所有的文件

● 所有的組建檔(生成檔、IDE設定、指令碼)

● 所有組態檔

● 所有資產(圖形、聲音、安裝媒體、資源檔)

● 所有第三方支援的檔案(例如:你使用的程式庫,或其他公司的DLL)

答案二:盡可能地少存

你顯然必須儲存許多東西。但不要存入會造成混淆、膨脹,礙事、沒必要的東西。盡可能讓存儲檔案結構保持簡單。特別是:

● 不要儲存IDE設定檔或快取檔。避免check in預先編譯的標頭檔或動態建立的程式資訊、ctags檔、使用者預設檔,等等。

● 不要儲存生成的項目—如物件檔、程式庫檔,或應用程式。二進位檔是組建流程產生的結果,你不需要將它們check in。你甚至不需要將自動生成的原始檔案check in。

有時自動生成的檔案需要check in:如果它們特別難以生成,或需要花費長時間來建構的話。你必須非常謹慎地做出這個決定,不要在你的存儲庫內塞滿沒必要的垃圾。

● 不要儲存不屬於專案的東西,例如開發工具的安裝程式,或組建伺服器用的作業系統映像。

● 不要將測試或bug報告check in。它們應該在bug回報系統中管理。

● “有趣的"專案email不屬於存儲庫。如果它們含有實用的資訊,就應該放在更有結構的文件檔案裡面。

● 不要儲存個人設定項目,例如編輯器的色彩配置、IDE的畫面設定,或(尤其是)描述你的電腦上的組建檔案位置的設定。當你的設定會與別人的電腦衝突時,這特別令人討厭。

● 不要把你認為將來有一天可能會用到的東西存在儲存庫裡。記得:你可以在版本控制刪除認何東西,只要它不符合目前的最新狀況(這樣做是百分之百安全的,它還是會在封存區裡面)。不要保留可丟掉的數位包袱。

 

★重點﹕在版本控制下儲存所有組成軟體專案的東西。但是儲存得愈少愈好,不要加入任何沒必要的檔案。

 

儲存軟體版本

你應該將你所組建的軟體版本存在版本控制底下嗎?有些商店會將他們的釋出版放到存儲庫裡。這通常是一個獨立的“釋出"存放區,二進位檔並不是真的適合放在原始檔案旁邊。

考慮將它們放在另一個簡單的靜態目錄結構中。當你幫以後的人記錄較不動態的檔案結構時,版本控制不會給你更多幫助。在檔案伺服器上瀏覽這種封存對象會比較容易。

存儲配置

仔細地構思你的存放區的配置。確保目錄結構的清晰,讓它揭示程式的樣貌。在頂層加入實用的“read me"文件。

無情地避免重複行為。如同重複的程式會產生很多bug,在存放區儲存重複的檔案也是如此。

謹慎地管理第三方程式。把它與你自己的原始檔案分開。將它放在清楚標示的子目錄。這可幫助你追蹤第三方的修改,而不會與自己的檔案混淆。

確保你的存放區被設置為忽略不適當的檔案。大部分的系統都可以使用模式匹配規則來忽略某些檔案。這可幫助你不小心check in個人設定檔、衍生檔,等等。

善用版本控制

如果黃金規則是:“使用版本控制",那麼銀色規則就是:“善用版本控制"。完全瞭解版本控制系統如何動作,及它的最佳使用方式是很重要的。

多數的做法都是共通的。

微量提交

提交給存放區的修改,說明你對程式做的事情。考慮如何講這個故事,讓歷史記錄可以很清晰。

提交簡短、微量的修改。它們比較容易瞭解,而且容易檢查正確性。這就是少量且頻繁的check-in策略。

不要累積一個星期的工作,再一次check in。甚至一天都不行。這會產生一些問題:

● 因為修改範圍較大,你很難追蹤程式中修改的地方。

● 在你更新期間,程式碼存放區其餘的部分可能有大幅度的改變。你的新作品可能不再可以動作。

● 如果你周圍的世界改變,你更有可能會面臨衝突:你所改變的程式段落可能已被其他人修改過了,現在必須解決共同改變的問題。

微量提交是連貫且一致的,可用獨立的步驟來展示相關的修改。不要做含蓋一個以上的修改的check in。如果你發現你編寫的提交訊息是:修改內部結構並將按鈕改為綠色,很明顯你正在做兩件事。將它們分開,提交兩次。具體且經常發生的案例是,不要同時修改程式碼的排版與功能。

微量提交是完整的。不要將做一半的工作check in。每一次提交都必須是完整的步驟。

 

 

傳送正確的訊息

在每次提交時,提供良好的check-in訊息。一開始可以用一個簡短的摘要來說明改變了哪些,最好是一個明確的句子。接著寫你要修改的理由,如果它們是有價值的話。如果適當,加入bug的編號或其他支援資訊。

讓訊息清晰、簡潔,且明確,就跟良好的程式碼一樣。記得DRY原則:不要重複自己做過的事。沒有必要列出修改過的檔案:VCS已經為你做記錄了。

在第一個句子中,總結你所做的改變。當你瀏覽存放區歷史記錄時,它們會被掃描成個明確的提交清單。

以下是來自一個真實基礎程式的check-in訊息。你認為哪一些是好的或不好的?

● 修復#4507:公用程式視窗,由ACVS之後載入

● 增加一些好處…修復編輯模式範例的標籤無法動作的bug…

●  ? '`'(…是的,一個空字串,這是一個非常普遍的提交訊息)

● 修正一個異常行為

● 在尋找當機原因時,程式載入過程有些很嚇人的程式碼,將它記錄下來。知道嗎,有時在查看基礎程式時,我會覺得很無力。

● 說真的,有人看過這個東西嗎?

建立良好的提交

勤奮的程式員會很體貼,而且會做出適當的check-in。正如同你應該仔細地填寫提交訊息,提交的內容也應該如此。

● 不要破壞版本。在你check in任何程式之前,先用存放區中最新的版本的測試它。這可以確保你的新程式不會破壞組建版,並惹惱其他的開發人員。它所依賴的其他的元件可能在你寫程式時已被修改過了,造成你的新作品是錯誤的。簡單的流程是:修改,用存放區的最新版測試它是否可以組建,測試它是否可以動作,將它check in。一定要按此順序。當你急著衝出門趕公車時,快速checkin“應該可以動"的程式是非常具有誘惑力的。我可以告訴你:很少如你所願。

● 不要把某個檔案丟到垃圾筒或移除,除非你知道每一個人都對它受夠了。在跨平台多重組建系統的專案中,這一點特別重要。

● 不要讓編輯器爭奪行尾,這是在跨平台專案中另一個容易落入的陷阱。

分支:見樹不見林

分支是VCS重要的基礎工具。它們可讓你“分開"你的開發勞力,並同時進行不同的功能,不會讓這些開發中的程式干擾其他地方。一旦完成之後,每一個程式分支都可以合併回主線,與它們的父系同步分支。這是個非常強大的開發工具。

分支可以用來做個人工作(當成個人的開發訓練處,或做危險實驗的地方),幫助團隊合作(定義整合或測試區域),及版本管理。

透過分支可讓許多常見的工作變得更容易。請考慮在下列情況使用它們:

● 封裝原始程式樹的修訂。例如,每一個功能都可在它自己的分支開發。

● 探查開發工作:試驗你不確定可以動作的東西。不要冒險破壞主要的開發線:如果實驗成功的話,可接上分支,接著合併。你可以建立多個分支來測試同一個功能的不同實作方式,合併最成功的那一個(一種程式碼天擇的形式)。

● 跨越許多原始程式樹,並且需要花費一段時間完成的主要修改,需要許多QA測試,及許多個人的check in才能正確完成。使用分支來做這件事,可以避免其他的開發人員拖延好幾天,最後給了一個壞掉的程式樹。

● 個別的bug修復。開啟一個分支來修復bug,測試作品,當錯誤已被排除,將分支合併。

● 將揮發性的開發與穩定的釋出線路分開。例如,我們會使用釋出分支來“凍結"構成軟體釋出版本的程式碼。釋出版本的分支是在Please Release Me中描述的。

分支是種很棒的組織工具,正等待你來使用它。不要害怕它們。不要讓可被放到分支的程式干擾你的主要開發線路。

但是,請小心分支不一定是最適當的並行開發技術。有時避開多重、幾乎看不見、並行的開發工作(與隨之而來的週期性整合負擔),改由在程式開發的主要路線採用簡單的功能切換,是較佳的做法。

程式的家

版本控制下的存放區是你的程式的家。小心不要把它變成養老院。或太平間。

看了夠多的大型專案之後,你會發現到,所有複雜專案的原始程式都會習慣它的VCS。

當專案及其基礎結構成長的時候,就會發生這種事。隨著程式碼進入青春期,組建指令碼與釋出工具會與存放區緊密地整合在一起。例如,自動化的版本更新指令碼會驅動版本控制機器。它們會追隨某種檔案結構用法,因為VCS管理它們(例如:空目錄的存在,或是否可以建立符號連結)。

這些事情會塑造你與程式碼的共事方式,無論更好或更糟。

我們不會經常在VCS間遷移專案,因為我們很重視存放區的改版歷史記錄。遷移是可行的,但通常是會損失資訊且凌亂的過程。這是在專案的一開始就選擇適當的VCS的主要原因。

 

★重點﹕原始程式住在儲存它的VCS裡面。專案愈成熟,就愈依賴它的棲息地。

 

結論

版本控制是基本軟體開發工具之一。每一個程式員都應該知道如何善用VCS,如同你應該充分瞭解如何使用強大的原始程式編輯器。(摘錄整理自第20章)

 

成為卓越程式設計師的38項必修法則

Pete Goodliffe/著

賴屹民/譯

碁峯出版

售價:680元

 

作者簡介

Pete Goodliffe

一位程式員、軟體開發專欄作家、音樂家與作者。他從來不會待在軟體食物鏈同一個地方太久。Pete 正在編寫一個名為 "Becoming a Better Programmer" 的雜誌專欄,並為許多軟體開發書籍做出貢獻。他經常演說軟體開發主題。

熱門新聞

Advertisement