雖然很多開發團隊都使用版本控制系統管理代碼變更,但當多個開發人員并行地使用不同的代碼庫進行編碼時,還是會出現問題的。在本期的 讓開發自動化 中,自動化專家 Paul Duvall 展示了如何運用開源的、免費的 Subversion 版本控制系統來有效地進行標記、分支和合併。
說到源代碼分支,可以將大多數的軟體開發團隊大致劃分為兩大陣營:有些是根本不分支;或存在大量的分支(甚至儲存庫),以致開發人員不知道從哪裡簽入變更 — 或者覺得合併變更很痛苦,於是就冒險將這項工作推遲到軟體快要發布時才做。
|
永遠只需要操作主幹是最理想的情況。這使合併兩個或多個代碼支線間的變更沒有那麼複雜。然而,在現實的軟體開發中,您正在開發的可能是未來版本,或者有時您可能需要為一個已經交付使用的版本準備一條後路。你需要有許可權訪問已發布版本的源代碼副本 — 但又不能擾亂正在開發的新代碼。
但當開發團隊試圖使用分開的代碼支線時,問題就會出現了。有些時候,開發團隊可能會選擇不創建分支,免得會延誤發布或導致開發人員瓶頸。而有些時候,開發人員合併的頻率太低,結果導致了合併衝突、瓶頸以及發布延誤。而增加分支則會使導航項目儲存庫很困難,從而導致開發人員無意中更改了不應該更改的代碼。
團隊進行并行開發時,一定要以最高的頻率將代碼合併回幹線(即主幹)。如果無法經常將代碼合併到主幹的話,可以運行測試,這樣就能夠確定是否會發生合併衝突,從而使實施 合併沒有那麼困難。要有效地進行并行開發,您可以使用 Subversion(SVN)中的標記和分支,Subversion 是一個開源的、免費的源代碼管理系統。通過標記,團隊可以安全地返回到源代碼的前一版本中。
我將通過介紹以下內容來示範如何在 SVN 中進行并行開發:
圖 1 顯示了幾個并行代碼支線的基本流程:
在圖 1 中,有效的開發發生在 SVN 主幹的版本 1.0.0 和版本 1.1.0 之間。可以是一組開發人員在版本 1.0.1 分支上進行開發,而其他人員在幹線上開發。
如果想要多個開發人員負責不同的代碼支線的話,可以使用很多策略和技巧。在本文中,我將展示一個很常用的方法,我曾在使用 SVN 的項目上用過它。
為并行開發配置 Subversion
安裝和配置 SVN 伺服器並不在本文的討論範圍之內。如果您有許可權訪問一個有效的 SVN 伺服器,就可以執行以下的步驟了:
從 Tigris.org Web 站點(參見 參考資料)為您的操作系統下載 SVN 客戶機軟體,並將其安裝到您的工作站。確保 SVN 可執行文件在您的工作站的系統目錄中。用 svn co URL 執行儲存庫的 SVN 簽出。
接下來,創建三個本地目錄:
清單 1 展示了在 Windows®、Macintosh 以及基於 *nix 的系統上如何從命令行創建這些目錄:
$ mkdir branches $ mkdir tags $ mkdir trunk |
在操作系統中創建了目錄之後,您可以分別使用 SVN add 和 commit 命令將它們添加並提交到 SVN。在我創建清單 1 的目錄的目錄中,我輸入了如清單 2 所示的命令(在適當的時候替代用戶憑證):
$ svn add *.* $ svn commit -m "Setting up standard SVN branches, tags and trunk directories" \ --username tjefferson --password Mont!cello |
執行了清單 1 和清單 2 中的操作之後,SVN 儲存庫應該類似於圖 2:
基本的 SVN 儲存庫就緒以後,就可以創建版本標記了。
根據主幹創建一個版本標記
標記的用途是在某個特定點及時標識代碼支線副本,以便以後返回到該版本。圖 3 展示了一個名為 brewery-1.0.0 的標記,它是針對 1.0.0 版本創建的。(標記能夠隨時在任何點創建,但通常都是在發布軟體時創建)。
假設主幹包含已發布的軟體的源代碼的話,第一個任務就是要依據主幹創建一個 SVN 標記。清單 3 就是一個關於如何創建這個標記的例子:
<path id="svn.classpath"> <fileset dir="${lib.dir}"> <include name="**/*.jar" /> </fileset> </path> <taskdef name="svn" classpathref="svn.classpath" classname="org.tigris.subversion.svnant.SvnTask"/> <target name="create-tag-from-trunk"> <svn username="jhancock" password="S!gnhere"> <copy srcUrl="https://brewery-ci.googlecode.com/svn/trunk" destUrl="https://brewery-ci.googlecode.com/svn/tags/brewery-1.0.0" message="Tag created by jhancock on ${TODAY}" /> </svn> </target> |
|
清單 3 使用了由 Subclipse 開源項目提供的 SVN Ant 任務(下載地址請參見 參考資料)。運行該 Ant 腳本時,一定要將隨 SVN Ant 任務一起提供的 JARs —svnant.jar、svnClientAdapter.jar 和 svnjavahl.jar— 包含在您的類路徑中。清單 3 的前半部分定義了這個類路徑。中間部分使用 taskdef 定義了 SVN Ant 任務。最後,我向主幹和標記目錄執行了 SVN copy 命令,從而為這個版本提供一個惟一的名稱:brewery-1.0.0.
運行清單 3 中的腳本並創建了一個新標記之後,您的 SVN 儲存庫應該如圖 4 所示。儲存庫的根級下面是標記目錄(在 清單 2 中創建)。而標記目錄的下面是在清單 3 中創建的新標記(目錄):brewery-1.0.0。它含有主幹的副本。
雖然標記的內容在 Subversion 中是可以更改的,但千萬 不要這樣做。
根據版本標記創建一個分支
在技術上,根據版本標記創建分支與根據主幹創建標記是相似的。兩者都要使用到 SVN 的 copy 命令。通常都會依照標記而創建分支,因為標記的是已發布 的代碼的代碼副本 — 而不是正在開發的代碼(可能已經更改)。圖 5 展示了如何根據 1.0.0 版本標記創建 1.0.1 分支:
|
清單 4 通過 SVN Ant 任務調用了 SVN copy 命令,以將 brewery-1.0.0 標記中的所有文件拷貝到分支位置:
<target name="create-branch-from-tag"> <svn username="sadams" password="b0stonM@ss"> <copy srcUrl="https://brewery-ci.googlecode.com/svn/tags/brewery-1.0.0" destUrl="https://brewery-ci.googlecode.com/svn/branches/brewery-1.0.1" message="Branch created by sadams on ${TODAY}" /> </svn> </target> |
運行清單 4 中的腳本之後,SVN 儲存庫應該如圖 6 所示:
|
在創建分支和使用 SVN Ant 任務時,一定要記得使用標記,這樣您才能夠提供一個可重複的過程,該過程方便源代碼的維護,使您可以輕鬆返回到上一版本的源代碼。
根據分支運行 CI
通常 CI 過程都是根據儲存庫的幹線(即主幹)而運行的。但如果想集成開發人員在分支上的變更並檢查支線與幹線的合併,這個原理也適用於分支。
圖 7 展示了 SVN 的位置。在這個 Hudson 配置頁面上,您還能夠定義要調用的 Ant 目標。
運行 Hudson 等 CI 伺服器來測試合併可以提供一個預警系統,警告可能會在開發周期中發生潛在的合併衝突。
將分支的變更合併到主幹
創建分支的主要原因之一就是防止中斷幹線開發。但是,一定要將分支上的更改合併到主幹。圖 8 展示了將版本 1.0.1 合併到幹線,這個幹線是軟體正在開發的版本 1.1.0:
在清單 5 中,我使用了 Subversion 的 merge 命令。我先輸入 svn merge,接著是合併到的目標 URL,然後是合併源的 URL,最後是本地目錄位置:
$ svn merge https://brewery-ci.googlecode.com/svn/trunk \ https://brewery-ci.googlecode.com/svn/branches/brewery-1.0.1 \ /dev/brewery --username pduvall --password password! |
SVN Ant 任務沒有提供合併命令,因此需要從命令行運行 merge 命令。或者使用 Ant 的 exec 任務來運行它。
運行清單 5 中的命令會得到類似於圖 9 的結果:
如果合併成功,則需要提交 Subversion 中的變更,如清單 6 所示。在命令行上輸入 svn commit,然後輸入消息描述和主幹的 SVN URL :
<target name="commit-branch-to-trunk"> <svn username="gwbush" password="IL0veTHEG00g!e"> <commit dir="${basedir}" message="Committing changes from brewery-1.0.1"> </commit> </svn> </target> |
要經常將變更從分支合併到主幹以避免合併衝突,從而使代碼支線始終保持一致。
根據分支創建標記
為了根據特定的分支準備一個版本,我創建了一個 SVN 標記。這裡使用方法與前面一些清單提供的方法類似。圖 10 展示了根據 brewery-1.0.1 分支創建名為 brewery-1.0.1 的標記的方法:
在特定的分支上完成開發后,就需要在 Subversion 中標記它。清單 7 展示了一個根據分支創建標記的例子:
<svn username="jbartlett" password="newHampsh!re"> <copy srcUrl="https://brewery-ci.googlecode.com/svn/branches/brewery-1.0.1" destUrl="https://brewery-ci.googlecode.com/svn/tags/brewery-1.0.1" message="Branch created by jbartlett on ${TODAY}" /> </svn> |
根據具體分支創建標記之後,您就可以在以後的開發周期中返回到該版本了。
結束語
并行開發並不是什麼難事,但如果不按照項目需求進行規劃並不斷改進,它的管理將非常困難。如果必須記住一點的話,那就是一切最終都要回到主幹。您可以將分支看作是一個容納可能中斷幹線開發的源代碼的臨時居所。最後要考慮的是要儘早地、經常地測試合併。當然可能有比 Subversion 更好的支持并行開發的版本控制系統,但根據我的經驗,開發團隊在開發時堅持原則比使用工具解決問題重要得多。(責任編輯:A6)
[火星人 ] 讓開發自動化: 針對廣大開發人員的并行開發已經有641次圍觀