2.1 Repository
你可能已經留意到我們忽略了些什麽;我們說過,“版本控制系統是...存放...的地方,”但我們從來沒有說過這些東東究竟都放在哪裏。實際上,它們全都儲存在
Repository中。
在大多數的版本控制系統中,Repository 是項目文件所有主版本的存儲中心。有些版本控制系統使用數據庫做爲 Repository,有些使用普通的文件,還有些是兩者兼有。總之,很明顯的,Repository 是版本控制策略的核心。你必須把它設置在一臺安全可靠的機器上。毫無疑問地,這玩意必須進行常規的備份。
在過去,Repository 以及它所有的用戶都不得不共同一臺機器(或者至少是共用著一個文件系統)。結果限制多多;
不同的網絡鏈接
版本控制系統的作者有時對“連網”有著不同的定義。有時它的意思是通過共享的網絡驅動器存取 Repository 裏的文件(就像 Windows 的共享或者 NFS 掛載)。有時它又代表著一個客戶端可以通過網絡與服務器端的 Repository 互動的 client-server 架構。兩者行得通(盡管如果支撐文件共享的裝置不支持鎖定機制的話前者難以進行正確設計)。無論如何,你還是可以找到一些關於部署和安全設置的資料,它們可以幫助你決定所需的系統。
如果版本控制系統需要連接到共享的驅動器,而你又需要在內部網絡以外連接它,你得先確認你的組織是不是允許你這麽幹。虛擬私有網絡(VPN)包允許你這麽做,但並不是所有公司都在跑 VPN。
CVS 使用 client-server 模式來進行遠程鏈接。
這就很難讓開發人員在不同的地方或是使用不同的機器、操作系統進行工作。結果就是,現在絕大多數的版本控制系統都支持網絡作業;作爲開發人員,你可以使用網絡連接 Repository ,有了 Repository,客戶端版本控制工具與服務器端的作用都是一樣的。巨爽。現在無論開發人員在哪裏;只要他們能夠通過網絡連接到 Repository,他們就可以安全的進行工作;你完全可以不讓煩人的對手看到你那珍貴的源代碼。Andy 和我就常常在路上通過 Internet 存取我們的源代碼。
然而這又引出了一個有趣的問題。如果沒有網絡可以連接到 Repository,而你又需要進行開發時該怎麽辦?很簡單,“看情況。”有些版本控制系統是僅爲連接到 Repository 的應用而設計的;也就是說你必須一直在線,如果不先連接到中央 Repository,你就不能對源碼進行改動。其它系統則寬鬆得多。
CVS 系統就屬於後者,也是本書使用的系統。我們可以在 35,000 英尺的高空用筆記本電腦進行編輯,回到酒店房間時再進行同步。在線/離線是選擇版本控制系統時一項很重要的標准,無論選擇什麽産品都要先確認它是否支持你的工作方式。
2.2 我們應該儲存什麽?
你的項目中的所有東西都儲存在 Repository 裏,但是,我們所說的這些東西究竟是什麽呢?
顯然你需要程序的源文件來建立項目:
Java,或者是
C#、
VB,又或者是任何你正在使用的語言。事實上,有些人認爲這裏所指的“源碼”是版本控制裏一項非常重要的組件,所以他們將版本控制系統稱爲“源代碼控制系統”。
源碼固然重要,但許多人都忘了還有其它的東西需要儲存在版本控制下。例如:假設你是一個
Java 程序員,你可能使用
Ant 來編譯你的源代碼。
Ant 通過一個腳本(通常是
build.xml)來控制編譯。這個腳本是構建過程中的一部分,沒有它你就不能建立程序。所以,它也應該被儲存在版本控制系統內。
類似的,有很多項目使用
Metadata 來進行它們的配置。這些
Metadata 也應當儲存在 Repository 裏。同理於各種用於創建發佈版本光盤的腳本、QA 使用的測試數據等。
事實上,有個簡單的測試可以決定文件是否應該儲存於 Repository 中。只需要問下自己“如果我們沒有一個最新版本的 xx,那麽我們還可以建立和交付我們的程序嗎?”如果答案是 no,那麽 xx 就應該放在 Repository 裏。
Joe 問到...
生成的文件要怎樣?
如果我們需要儲存建立項目所需的所有東西,那麽這是不是也意味著我們也要儲存所有生成的文件呢?例如,我們可能需要運行
JavaDoc 來爲我們的源碼樹生成 API 文檔。那麽這份文檔是否也應該儲存在版本控制系統的 Repository 裏呢?
非常簡單,no。如果一份生成的文件可以從其它文件中重建,那麽存儲它就是重複勞動。爲什麽這樣不好?我們並不是擔心浪費磁盤空間,而是因爲不想讓它脫離了應有的步驟。假設我們儲存了源碼和文檔,而當我們更改源碼後,這份文檔也就失效了。如果我們忘記更新然後提交了,那麽在 Repository 裏就有了一份讓人誤解的文檔。所以在這種情況下,我們只保留原始資料:源代碼。同理於所有生成的文件。
可實際上,有些文件是很難重建的。譬如說,生成文件的工具可能只有一個 single license,而文件卻是所有開發人員都需要的,又或者是這個文件需要幾個小時來生成。在這種情況下,把它們放在 Repository 就比較合理了。有 license 的開發人員可以創建這個文件,或者是用某台比較好的機器生成這費時的文件。這樣可以被提交到 Repository 裏,這樣其它的開發人員就可以直接使用了。
除了創建軟件所需的所有文件外,所有非代碼的項目文件也應當存儲在版本控制系統內(任何將會用得上的東西),包括項目文檔(內部和外部的都要)、其它也可能包含在內的如重要郵件的文本,會議記錄,網上找到的信息-總之是所有對項目有幫助的東西。
2.3 Workspace 和操控文件
Repository 存儲著項目的所有文件,但如果我們需要爲應用加入一些很棒的新功能的話它並不能給予很多的幫助;我們需要可以存取這些文件的地方,也就是我們本地的
workspace。
Workspace 是一份屬於我們工作部分的所有文件的本地副本,它從 Repository 獲得。對於中小型項目來說,
workspace 可能只是項目中所有文件的一份副本。而對於大型項目,你可安排開發人員在項目代碼的一個子集下工作,這樣構建的時候可以節省他們的時間也有助於分離出系統的子系統。
Workspace 有時也稱爲
working directory 或是代碼的
working copy。
爲了初始化
workspace,我們需要從 Repository 中導出相關的文件。不同的版本控制系統對這個過程有著不同的名稱,但最常用(也是
CVS 所使用的)的名稱是
checking out。當從 Repository
check out 時,你的
workspace 就得到了一份本地副本(即使 Repository 就存儲在你工作的電腦內,在使用文件之前你仍然需要將它們
check out 出來;Repository 應當被看做是黑盒。)。
Check out 的過程確保你得到的是最新的文件,這些文件的目錄結構與 Repository 一一對應。
工作的時候,你會在本地的
workspace 對項目的代碼進行改動,不時的將改動保存回 Repository。這個過程稱爲
committing;你在把所做的改動
committing(注:提交)回 Repository。
當然了,你在改動代碼的時候,組內的其它成員也是,他們同樣也在提交改動。然而這些改動並不會影響你本地的
workspace;它不會因爲別人將改動提交就突然改變了。你反而要通知版本控制系統更新你本地的
workspace。在更新的時候,你將會從 Repository 得到最新的文件集。而當你的同事進行更新時,他們也會得到你最新的改動。(可讓人困惑的是,有些人從 Repository 導出最新的改動時也用 "check out" 來表示更新。但因爲這是一個通用的習慣用語,我們將在本書裏使用更新一詞來表示。)CVS 的交互見下圖(Figure 2.1):
當然,這裏有一個潛在的問題:如果你和一個同事要同時對同一個源文件進行修改時會發生什麽事情呢?這取決於你所使用的版本控制系統,但它們都有辦法來處理這種情況。詳細的我們將會在 2.9 Locking Options 裏談到。
2.4 项目,模块和文件
迄今爲止,我們已經談了存儲文檔,但還沒有談到它們是怎樣組織起來的。
大多數的版本控制系統在底層使用單獨的文件進行處理(有一些類 IDE 的系統使用函數來進行版本控制,但這些系統極爲罕見)。項目中的每個文件都按名存儲於 Repository 中;如果你添加了一個叫 Panel.java 的文件到 Repository,那麽其它的組員就可以將 Panel.java 導入到他們自己的
workspace 中。
然而,這樣做是非常低級的。一個典型的項目可能有成百或是上千的文件,而一個公司也許有成打的項目。幸運的是,絕大多數的版本控制系統允許你對 Repository 進行結構化處理。在最上層,它們通常會將你的工作劃分爲項目。而每個項目中你所面對的是一組組的模塊(或是子模塊)。舉個例子,可能你正在爲
Orinoco(一個基於網絡的圖書訂購應用程序)。所有構建應用所需的文件可能都存儲在 Repository 中名爲
Orinoco 的項目下。如果需要,你可以把它們全都導入到你本地的磁盤上。
Orinoco 項目自身可能被劃分成大量獨立的模塊。例如,可能有一組人在作信用卡處理而另一組則在做訂購模塊。可以的話,信用卡子項目的人並不需要項目的所有源代碼;他們的代碼應當被清晰的劃分開來。事實上,他們
check out 時只是想要看到項目中他們在工作的那部分。
CVS 允許 Repository 管理者將項目拆分成模塊。一個模塊是一組可以按名導出的文件(通常包含一個或多個文件系統目錄樹)。模塊可以是層次結構,但卻不是必須的;同樣的文件或文件集可以出現在許多不同的模塊內。模塊甚至可以讓你在項目之間共享代碼(只需將文件放到一個共享的模塊中然後讓其它組按名引用即可)。
模塊爲你帶來 Repository 的許多不同的視圖,允許組員僅面對他們所需要的文件。我們會在第九章談到它。
2.5 版本从何而来?
本書是關於版本控制系統的,但至今爲止我們所談到是在 Repository 中存取文件。版本從何而來呢?
其實版本控制系統的 Repository 是相當聰明的。它不仅是照管着当前文件的副本,而是保存着每个
check in 进来的版本。如果你导出一个文件,编辑,然后提交,Repository 将会保留着原来的版本和包含你的改動的版本(實際上,大多數的版本控制系統保存的是文件間差異的版本而不是每個修正的完整版本)。大多數的系統使用一種簡單的編號方式爲文件版本進行命名。在 CVS 中,文件的第一個版本被賦值爲修訂版本號 1.1。如果有一個改動過的版本被提交,這個改動的版本就是 1.2。下一個改動是 1.3,以此類推。(稍後我們會談到更複雜的編號方式)。與這些修訂版本關聯的是文件被提交的日期和時間,連同一項可選的來自開發人員爲改動進行描述的注釋。
這個存儲修訂版本的系統功能異常的強大,使用它,版本控制系統可以做到:
- 取回文件的指定修訂版本。
- 導出系統的所有源代碼,如同它在兩個月前出現的那樣。
- 告別你某個特定文件 1.3 和 1.5 版本間的改動。
你也可以使用這個修訂版本系統來取消錯誤。如果你在將近週末的時候才發現自己已經走進了一條死胡同,你可以收回所做的所有改動,將代碼恢複到週一早上的狀態。
修訂版本的編號有許多種的方式。有些版本控制系統將單一的修訂版本號用在所有文件上,有些則給每個文件一個唯一的修訂版本號碼序列。
CVS 採用的是後者。舉個例子,我們從 Repository 中導出三個文件,得到下列的版本號碼:
File1.java 1.10
File2.java 1.7
File3.java 1.9
編輯 File1.java 和 File3.java,但不去碰 File2.java。如果我們將這些改動提交會 Repository,系統將會對那些我們修改過的文件的修訂版本號碼進行增值:
File1.java 1.11
File2.java 1.7
File3.java 1.10
這意味著你不能靠單獨的文件版本號碼來了解事情,譬如項目發佈版本(例如,
Orinoco 的 1.3a 版本)。因爲這點常常給剛開始使用
CVS 的團隊帶來災難,我們重複一下吧。
CVS 賦於文件的獨立的修訂版本號碼不應該被用作外部的版本號碼。作爲替代,版本控制系統爲你提供了
tags(或者它們的等價物)。
2.6 Tags
所有的這些修訂版本號碼都很棒,但比起像 1.47 這樣的號碼,人們似乎對如 "PreRelease2" 這樣的名稱更感冒一些。由不同文件組成的軟件的某個發佈版本有著不同的修訂版本號碼時同樣也存在著問題。在之前的例子中,我們可能准備要將由 File1.java,File2.java 以及 File3.java 構建的軟件交付使用了,但是每個文件都有自己的修訂版本號碼。那麽你要怎樣將這些不同的版本號碼結合起來呢?
Tags 來解決你的難題了。版本控制系統讓你可以在某個時間點爲一組文件(或是模塊、一個完整的項目)命名。假設你將剛才三個文件的這一組定名爲 "PreRelease2" ,隨後你就可以使用相同的 tag 將它們導出了。你將得到 1.11 的 File1.java,1.7 的 File2.java 和 1.10 的 File3.java。
Tags 是跟蹤項目代碼歷史記錄中的重要事件的一個很好的方法。我們將在稍後廣泛的使用 tags。事實上,tags 和 branches(下一節的主題)有著它們各自的章節。
2.7 Branches
常規的開發過程中,多數人都工作在共有的代碼上(盡管他們可能工作在不同的部分)。他們會導出、修改,然後提交,每個人都會參與到這份工作中。這條代碼流常常被稱爲
mainline。(見下圖 2.2)圖中的時間順序從左到右,較粗的那根水平線代表著代碼的進度;也就是
mainline。每個開發人員都從
mainline 上將代碼導出、提交到他們自己的
workspaces中。
但考慮到新的發佈版本即將交付。可以有一組由開發人員組成的小的子團隊爲此做准備,修正最後的一些 bugs,和發佈工程師一起工作,幫助 QA 小組。在這個緊要關頭,他們需要的是穩定;其它開發人員繼續編輯代碼、准備爲下一個發佈版本加入功能都將會影響到他們的工作。
其中一個選擇就是在産生發佈版本時暫停新的開發,但這就意味著其它的組員只能呆坐著了。
另一個選擇就是將軟件複制到一臺備份機器上然後發佈小組就使用這台機器。但如果我們這麽做,他們複制後又進行了改動會怎樣呢?我們怎麽進行追蹤?如果他們在這些代碼中找到的 bugs 同樣存在於
mainline 中,我們怎樣才能有效、可靠的將這些修正應用到
mainline 中呢?一旦他們將軟件發佈,我們如何對客戶報告的 bugs 進行修正;如何能保證可以在交付的時候找到相同狀況的代碼?
一個更好的選擇就是使用版本控制系統內建的
branching 功能。
Branching 有些像常出現在科幻小說中可以拆分時空的東東。也就是說有兩個並行的未來。一些事發生了,其中一個未來也就被拆分了。很快你就要面對一連串充滿選擇的世界(當你沒有頭緒的時候這將會是解釋這個故事的好東東)。
版本控制系統中的 branching 同樣允許你創建多個並行的未來,但裏面住的不是外星人和太空牛仔,它們包含的是源碼和版本信息。
回到小組發佈産品新版的案例上。迄今爲止,所有的小組都工作在
mainline 上(見 Figure 2.2),但是發佈小組想要將發佈版本從
mainline 中分離出來。爲此他們在 Repository 中創建了一個 branch。從現在開始直到他們完工,發佈小組將會從這個 branch 中導出和提交。即使應用發佈了,這個 branch 仍然是有效的;如果用戶提交 bugs,小組會在這個發佈版本的 branch 中進行修正。見下圖:
一個 branch 幾乎就像一個完全獨立的 Repository:
人們使用這個 branch 查看源碼、在其它的 branches 或
mainline 上進行獨立的操作。每個 branch 都有它自己的歷史記錄以及追蹤著人們創建的各個獨立的版本(雖然很明顯,如果你回顧創建 branch 的那一刻,branch 和 mainline 其實是一回事)。
這正是你創建發佈版本時想要的。發佈小組改善和交付的工作會基於一份穩定的代碼。與此同時,主開發組可以繼續對 mainline 中的代碼進行改動而無需在發佈期間暫停開發。當用戶提交發佈版本的問題時,發佈小組可以對發佈的 branch 存取,這樣他們就可以進行修正然後交付不包含任何 mainline 中新近的開發代碼的已更新版本。
Branches 由 tags 標識,branch 內文件的修訂版本號碼有著它們額外的級別號碼。因此如果 File1.java 的修訂版本是 1.14 然後你創建一個 branch,你會發現在 branch 中修訂版本號碼是 1.14.2.1 而 mainline 中仍然是 1.14。在 mainline 中編輯這個文件你會得到修訂版本 1.15;而在 branch 中將會是 1.14.2.2。
你可以從其它的 branches 中創建 branches,但你通常不會這麽幹。我們遇到過很多因爲項目中的分支過於複雜而最終棄用 branching 的開發人員。我們將會在本書中介紹一個你所需要的簡單方案,但會避免不必要的複雜性。
2.8 Merging
回到那個有著多個未來的科幻小說裏。爲了增添些有趣的情節,作者常常會允許他們的人物使用 wormholes(譯注:類似於黑洞的物理空間)穿梭於時空之間,多相發散陰極射線示波管,又或者只是一杯熱茶。
你同樣可以在版本控制系統的各個 branches 間穿梭(要不要茶都無所謂)。雖然每個導出的版本都來自於特定的 branch,然後又會提交回給這個 branch,但還是可以很容易的把多個 branches 導出到一臺開發機器上(當然要在硬盤不同的目錄或文件夾裏)。這樣的話 mainline 上的工作和發佈版本的 bug 的修正就可以同時進行了。
不僅如此,版本控制系統還支持 merging。譬如說你在發佈版本裏修正了一個 bug 然後意識到同樣的 bug 也將在出現在 mainline 的代碼中。你可以讓版本控制系統記錄下你在修正這個 bug 時對源代碼所做的改動,然後將它們應用到 mainline 的代碼上。這在很大程度上剔除了複制和拷貝以及來回於系統的不同版本間的需要。關於 merging 稍後我們會談到很多。
2.9 Locking 選項
想像一下有兩個開發人員,Fred 和 Wilma,工作在同一個項目上。他們都已經將項目文件導出到各自的硬盤上,然後都要編輯他們本地的 File1.java 副本。如果他們提交這個文件時會發生些什麽?
一個壞的狀況就是版本控制系統接收了 Fred 的改動,然後又接受了 Wilma 的同一個文件。因爲 Wilma 的副本裏不會包含 Fred 所做的改動,所以存儲 Wilma 的副本到 Repository 時實際上也就丟失了 Fred 的工作。
爲了阻止這個發生,版本控制系統實現了某些類型的衝突解決系統(可能在 Fred 和 Wilma 這種情形下是好事)。有兩個常用的衝突解決方案。
第一個方案叫
strict locking。在一個 strict locking 的版本控制系統裏,所有導出的文件會被初始標識爲“只讀”。你可以查看它們,用它們來構建你的應用程序,但不可以進行編輯或改動。如果要編輯或改動,你得先請求 Repository 的許可:“請問我是否可以編輯 File1.java?”如果沒有別的人在編輯相同的文件,你就會得到 Repository 的許可,本地的文件副本會被改爲“讀/寫”。然後你就可以進行編輯了。如果當你標識著這個文件時有人請求對其進行編輯,他們將會被拒絕。在你完成改動並提交之後,你本地的副本將會恢複爲只讀的狀態,其它的人就可以進行編輯了。
第二個解決衝突的方案叫
optimistic locking,雖然實際上它並不鎖定任何東西。在這,開發人員可以編輯任何導出的文件:導出的文件被設爲可讀/可寫的狀態。然而,當你上次導出的文件已被更新後,Repository 將不會允許你提交這個文件。它會請求你在提交之前將該 Repository 裏最新的改動應用到你本地的副本中。這就是它在聰明之處:不是使用 Repository 裏的最新版本對本地的副本進行簡單的覆蓋,而是嚐試著將 Repository 的變更和你所做的改動合併。例如,讓我們看看 File1.java:
public class File1 {
public String getName() {
return "Wibble";
}
public int getSize() {
return 42;
}
}
Wilma 和 Fred 都導出了這個文件。Fred 修改了第三行:
return "WIBBLE";
然後他將這個文件提交。這就意味著 Wilma 的副本過時了。Wilma 並不知道這事,她修改了第 6 行,返回 99 而不是 42。提交時,她被告知她的副本已經過時了;她需要合併 Repository 中的改動。詳見 Figure 2.4。
當 Wilma 將改動合併時,版本控制系統可以准確的定位 Fred 的改動而不會與 Wilma 的重疊,因此只需對她的副本更新第三行即可,不會影響其它的改動。當提交時,Wilma 和 Fred 所做的改動都會原封不動的存儲回 Repository 中。
當 Fred 和 Wilma 都更新了第三行,但做出的卻是不同的改動時又會怎樣呢?假定 Fred 先提交,那他的改動將會被接納。當 Wilma 提交的時候,她將再被告知她的副本已經過時了。然而這次她合併時版本控制系統就會留意到她和 Repository 都對一行代碼進行了。這是個衝突。在這個情況下,Wilma 會看到警告的信息,然後她源文件的副本中的衝突會被標出。她將不得不手工解決這個問題(可能得和 Fred 談談爲什麽他們都在修改同一行代碼)。
有了上面的描述後你可能會認爲使用 optimistic locking 開發系統多少有些魯莽;多人同時編輯著相同的文件。人們常常是還沒試就說這行不通,然後堅持使用 strict locking 的版本控制系統。
然而在現實中 strict locking 卻引起了大量毫无意义的争论。如果你试试采用 optimistic locking 的系统(譬如
CVS),你会很驚訝的发现它几乎没有冲突。......
2.10 配置管理
有時你會聽到一些人在談論配置管理或軟件配置管理系統(常簡稱爲 CM 或 SCM)。乍聽之下他們像是在談版本控制。沒錯;實際上 CM 嚴重依賴於好的版本控制。但版本控制只是配置管理中會用到的一個工具而已。
Introduction
本書將會告訴你如何使用版本控制來增進軟件開發過程中的有效性。
版本控制,有時也叫源碼控制。項目的三大支柱,它首當其中。我們把版本控制的應用視爲所有項目的必修課。
無論對於團隊或是個人,版本控制都有著許多優點。
- 它爲團隊提供了一個項目全局範圍內的 undo 按鈕;沒有什麽是不可改變的,錯誤可以輕易的回滾。想像一下,你正在使用這世上最棒的字處理機,它有著所有你可以想像得到的功能,除了一個:因爲某些原因,他們忘了加入 Delete 鍵。想想你將要如何謹慎而緩慢的進行輸入,特別是在一個超長的文檔接近完成的時候。一個錯誤,你就不得不重新開始。同樣的情況下,使用了版本控制,我們可以回到一小時、一天、或是一星期前,這樣就可以解放你的團隊,讓他們快速、自信的進行工作,因爲他們有了一個修正錯誤的方法。
- 它允許多個開發人員工作在一個受控方式下的相同的代碼上。當有人覆寫了團隊成員編輯的代碼時,改動也不再會丟失了。
- 版本控制系統隨時都保留著一份變更的記錄。即便你遇到一些“令人驚訝的代碼”,也可以很容易的找出是誰、什麽時候以及爲什麽(可能的話)進行的改動。
- 版本控制系統允許你在進行主線開發工作時對軟件的多個發佈版本同時進行支援。有了版本控制系統,發佈前的 code freeze 期間,團隊的工作也無需停止。
- 版本控制是一臺項目全局範圍內的時光機,它讓你可以回到某一天,並清楚了解當天項目的狀況。這對研究工作來說是很有用的,但更重要的是,它可以重建較早前向客戶發佈的有問題的版本。(譯注:這對找出之前版本的問題所在極爲有用)
本書主要從項目的角度來闡述版本控制。我們並不是簡單的列出一個版本控制系統內可用的命令,而是從一個成功項目所需的工作出發,看看版本控制系統能給予我們什麽樣的幫助。
在實踐中版本控制是怎樣工作的呢?我們從一個小故事開始吧...
1.1 版本控制的應用
Fred 進入辦公室,急於繼續新 Orinoco 圖書訂購系統的工作。(爲什麽是 Orinoco?因爲 Fred 的公司使用河流的名稱爲所有內部項目命名。)第一杯咖啡過後,Fred 從中央版本控制系統 check out 出項目源碼的最新版本更新他本地的副本。日誌中列出了被更新的文件,他留意到 Wilma 改寫了基礎 Orders 類的代碼。Fred 開始擔心這個改動是不是會影響到他的工作,但今天 Wilma 去客戶那安裝最新發佈的版本了,所以不能直接問她。於是 Fred 求助於版本控制系統,讓它顯示有關 Orders 類改動的記錄。Wilma 的注釋並沒有打消他的顧慮:
* 爲 Order 類加入新的交付選項字段
爲了搞清楚發生了什麽事,Fred 回到版本控制系統看了看源文件中實際的改動。他留意到 Wilma 加入了幾個實例變量,但它們卻被設爲默認值而且似乎沒有被改變。這可能會是個問題,不過這無關痛癢,所以他繼續工作。
Fred 爲系統加入了一個新的類以及幾個測試類,創建它們的同時 Fred 也爲它們在版本控制系統中加入了相同的文件名,而文件本身並不會被加入,直至 Fred 提交他的改動爲止,現在加入它們的名稱可以讓 Fred 在稍後不至於忘記。
幾個小時後,Fred 完成了一些新功能的首部分並通過了其自身的單元測試,它將不會影響系統的其它部分,所以 Fred 決定將它提交到版本控制系統,好讓團隊的其它成員可以看到。這幾年中,Fred 發現經常的 check in/out 要比把它放幾天合理得多:如果你需要擔心的只是幾個文件而不是一週內整個團隊做出的所有改動,那麽解決偶然出現的衝突將會輕而易舉。
爲什麽你永遠都不該接聽電話
正當 Fred 准備開始下一輪的編碼時,他的電話響了。是 Wilma,從客戶那邊打來的。她安裝的那個版本似乎有個 bug:打印出來的發票並沒把銷售稅計算在付運金額上。客戶非常生氣,需要馬上修正。
除非你使用版本控制...
Fred 和 Wilma 再一次檢查了問題版本的名稱,然後從版本控制系統 check out 出這個版本的所有文件。Fred 將它放到自己機器的一個臨時目錄下,因爲他打算在完成工作後將它刪除。現在在他的電腦裏就有了兩份系統源碼的副本:主版本和已對客戶發佈的版本。因爲要進行修正,所以 Fred 讓版本控制系統爲他的源碼加上了標簽。(修正這個 bug 後他會加入另一個標記。這些標記標識著開發中的重點。通過使用統一命名的標記,無論在他完成修改的前後,其它組員都可以清楚的知道稍後他們應當關注的改動是什麽。)
爲了將這個問題分離出來,Fred 先寫了一個測試用例。這當然足夠了,似乎從來就沒有人檢查過與付運相關的銷售稅的計算,因爲他的測試用例馬上顯示出了問題所在。(Fred 爲此做了筆記並在這次的迭代回顧會議上提了出來;這類問題對客戶應當是透明的。)Fred 爲所有應征稅的類都加入了付運的代碼,編譯、檢查,他的測試用例已經通過了。作爲一次快速的完整性測試,他重新運行了測試套件然後把修正後的代碼提交到了中央版本控制系統。最後,Fred 爲發佈版本加入了一個標識表明 bug 已經被修複,然後向負責這部分的 QA 發送了一份筆記。有了他的標識,他們就可以做出一張包含了這個修正的交付磁盤。然後 Fred 回電給 Wilma 告訴她修正後的版本已經在 QA 的手上應該很快就會送到她那的了。
處理完這讓人稍稍分心的事,Fred 在他的機器上刪除了發佈版本的代碼:改動已經安全的提交回中央服務器,代碼並沒有被搞亂。之後他開始想:相同的 bug 是不是也存在於當前的開發版本中?最快的檢驗方法就是將他剛才爲發佈版本編寫的測試用例加入到開發版本的測試套件中。他讓版本控制系統將發佈版本裏具體的改動與開發版本中相應的文件合併。合併過程根據發佈文件中的變更爲開發版本中的文件作出了同樣的改動。當他運行測試用例時,新的測試失敗了:bug 再次出現。然後他將發佈版本中的修正應用到開發版本中。(他的機器上並不需要有發佈版本的代碼,所有的改動都從中央版本控制系統獲得。)一旦測試都通過了,他會將這次的改動提交回版本控制系統,這樣下次就少了個煩人的 bug。
危機過去了,Fred 繼續自己今天的工作。整個下午他都在愉快的編寫著測試用例和代碼直到收工。在他工作的同時,團隊的其它成員也在進行改動,於是 Fred 通過版本控制系統將其它的改動應用到他本地的代碼中。他運行最後一次的測試,然後提交,爲下一個工作日作好准備。
明天
很不幸,這天出了些狀況。前晚 Fred 的中央暖氣系統終於還是掛了。Fred 住在 Minnesota,現在是二月,麻煩大了。他打回公司說他今天可能都要等維修人員的到來。
然而,這並不意味著他得停止工作。Fred 使用安全鏈接通過公網將開發版本的代碼 check out 到他的筆記本電腦上。因爲在前晚他回家的時候已經提交過了,因此所有最新的代碼都在那。他在家裏著裹毛毯坐在火爐旁繼續工作。最後他將筆記本電腦上所做的改動提交,這樣下一個工作日他就可以從這裏繼續了。生活是美好的。(除了那張維修的帳單。)
故事書項目
版本控制在 Fred 和 Wilma 項目上的正確應用並不十分起眼,但卻給予了他們控制權以及幫助他們相互通信,甚至是當 Wilma 在很遠的地方時。Fred 可以查出代碼中的改動然後分別爲他們程序的多個發佈版本作出修正。他們的版本控制系統支持離線工作,所以 Fred 可以不受地點的約束:他可以在他的暖氣系統掛掉的情況下在家裏進行工作。因爲他們在適當的位置上有版本控制(他們知道應該如何使用它),Fred 和 Wilma 處理過很多項目的緊急事件,他們的鎮定常常影響著我們對意外的響應。
版本控制讓 Fred 和 Wilma 即使面對多變的真實世界依然不失控制權以及靈活性。這也是本書的內容所在。
1.2 說明
下一章,
What Is Version Control?,將會介紹版本控制系統的概念及術語。有許多版本控制系統可供我們選擇,本書使用的是自由的
CVS;它可能是目前應用最廣泛的版本控制系統。
第三章,
Getting Started with CVS,是
CVS 的入門指南。其餘章節則是一套
CVS 在項目中的應用技巧。這部分分爲六章,每章都包含了許多的技巧:
- CVS 中不同的連接方法。
- 常用 CVS 命令。
- 使用標記和分支來處理發佈版本和實驗版本的代碼。
- 創建一個項目。
- 創建子模塊。
我們在結尾的附錄中對所有的技巧作了總結,另外還列出了其它的資源以及參考書籍。