《配置管理最佳實踐》——1.3 源代碼管理核心概念
本節書摘來自異步社區《配置管理最佳實踐》一書中的第1章,第1.3節,作者: 【美】Bob Aiello , Leslie Sachs著,更多章節內容可以訪問雲棲社區“異步社區”公眾號查看
1.3 源代碼管理核心概念
就像其他學科一樣,源代碼管理也有它自己的術語。可能你以前從其他一些配置管理人員那裏聽到一些技術行話卻百思不得其解,讀過這一章你就不會再對這些術語感到陌生了。這一章將會幫助你理解這些核心概念。理解了這些概念對理解源代碼管理最佳實踐也是非常有幫助的。
1.3.1 建立基線和時間機器
許多開發者認為源代碼管理就是簡單地從源代碼管理工具中(一個代碼庫)簽入和簽出代碼。就像大多數人認為的那樣,多年前一些比較老的版本管理係統的確是這個樣子。如今,雖然大多數配置管理代碼庫具備的可靠性和功能性不同,但都能支持變更,確保所有變更的安全。簡單地簽入代碼到源代碼管理工具中確實是必需的,但是在配置管理過程中這顯然是不夠的。實施有效的源代碼管理,要點是提供一個虛擬的時間機器,可以隨時把你帶到一個特定的時間點,通常是一個穩定版本發布時代碼的某個狀態。對於某個特定發布,識別出其源代碼的確切版本,常常稱作建立代碼基線。一些權威的資料中稱此為代碼的配置。對於用這個特定的術語,我認為它有誤導性。在第3章“環境配置”中,我們將會討論配置這個詞更好的用法。
很多配置管理工具把建立基線的操作叫作給代碼做標記(tagging)或標簽(labeling)。也有的工具在涉及相互依賴的多個組件時用代碼快照(Snapshotting) 這個術語。基線是不可改變的。這就意味著你必須具備可以鎖住標記或者標簽使其無法修改的能力。這一點是非常重要的,因為我們必須有一個代碼版本不變的記錄,從而構建出部署到生產環境(或QA環境)中的發布。除了標記(或標簽)用於創建基線,很多人用額外的標記(通常稱作“生產”)來指出生產環境中的當前版本,或者指那些最後完好的構建。標記是可以隨著生產環境中代碼的當前基線浮動的。生產環境下的代碼發生變化了,對應的標記也要變化。這是一種常見而且方便的做法。但是這種做法是不能用來替代創建基線的,因為它不能用來識別那些已經被發布到生產環境(或QA環境)下的代碼。區別就在於基線是不能改變的,而標記是可以改變的。大多數優秀的源代碼管理工具對標記或標簽用元數據來實現。元數據是關於數據的數據。這意味著源代碼管理存儲庫中保留著一個單獨數據庫,用來記錄簽入代碼庫中代碼的信息。元數據包括簽入的注釋,相應缺陷(觸發對代碼的變更)的鏈接,以及記錄了代碼合並的鏈接。事實上,所有針對代碼的變更都應該用變更請求(CR,Change Request)來跟蹤,這樣你就可以確切地知道為什麼會進行某個變更。很多源代碼管理工具利用元數據提供了一係列豐富的特性,如在很多年後依然可以為用戶提供從代碼庫創建後的所有變更曆史。在處理跟蹤變更時,通常的做法是在開發前期采用一個輕量級(非正式化)的過程,然後隨著軟件即將發布變得越來越嚴格,並且要一直保持這種趨勢。這是一個很好的把敏捷和精益原則應用到配置管理的例子。在第9章“合理精簡過程”中我們將會討論這些。一些源代碼管理工具用分支創建基線,這也許不是最優的辦法,因為分支上的代碼常常是可以修改的,除非利用一些機製來鎖住分支。利用分支來創建代碼基線的工具之所以這樣做是因為工具性能上的局限。在這類工具中創建分支要比創建標記或標簽快。雖然一些配置管理工具的確嚐試用分支來創建基線,但是這並不是分支的常見用法。我們將會在這章的後麵部分對此進行討論。首先,我們需要了解源代碼管理中的簽入和簽出。
1.3.2 保留與非保留簽出
保留與非保留簽出,也就是我們經常說的代碼是否可以鎖住的問題。一些源代碼管理解決方案是建立在保留簽出模型1基礎上的。默認情況下,當一個開發人員想做修改而簽出一個文件時,存儲庫就會在這個文件的版本上放置一個鎖。其他源代碼管理工具是基於非保留簽出模型工作的(有時也被稱為“樂觀簽出模型”)。這就意味著不管有多少個開發人員同時簽出並且修改代碼,存儲庫都不會在該文件上放置鎖。我比較傾向於使用保留簽出模型,因為這可以主動地讓我知道我是否正在嚐試和別人修改同一個文件。樂觀簽出模型可能會造成額外的合並代碼的工作,如果開發人員知道他們在試圖同時修改相同的文件,這種形式的合並本來是可以避免的。當其他人試圖和你修改同一段代碼時,一些現代的源代碼管理工具會通過提高可視化的方法來避免這種問題。另外一個非保留簽出的用途是簡單地得到一份可寫的(可能永遠不會簽回到存儲庫的)文件副本。
1.3.3 沙箱和工作空間
大多數源代碼管理工具都有私有沙箱和工作空間的概念。在私有沙箱裏,你可以隔離你的工作,然後簽入你的變更,如果有必要,還可以合並更改到另外一個分支(通常被稱作代碼集成)。普遍的做法是開發人員有多個工作空間(或沙箱)來協調和安排自己的工作。分支提供了一個安排工作的有效方法,並且提高了開發人員的生產率。
我的第一個沙箱
當我介紹配置管理和如何使用源代碼管理工具的時候,我常常提到小時候在沙箱裏玩的經曆。像很多其他男孩一樣,我喜歡牛仔、軍隊士兵和其他玩具娃娃。我家隔壁是一個小女孩,她時常到訪,在我的沙箱裏玩她的玩具娃娃。不可避免地,我在玩牛仔的時候扔得到處是沙子,然後蘇西就跑回家抱怨我把沙子弄到她的娃娃上了。結果你也知道了,給我帶來了很大的麻煩。之所以提到這件事情,就是想說明不應該讓別人進入你的私人沙箱,否則你就會遇到麻煩。在隔離並且受控的環境中工作是軟件開發中提高生產率的首選方法。良好定義的處理代碼變體的方法是提高生產率的另外一個方麵。
1.3.4 變體管理(分支)
變體(variant)是指同一產品不同版本之間具有很多相同功能,但是又有區別的情況。優秀的源代碼管理工具最重要的特性之一就是很容易地支持相同代碼庫的多個變體。這樣就可以很容易地支持在相同代碼庫下僅為了某些特殊需求而對代碼子集的修改。創建變體常常是創建多條並行的開發線來實現,通常被稱作分支。創建分支的原因有很多。例如,想寫某個軟件顯示一個國際時鍾。這個時鍾最基本的功能都是一樣的,無論此時鍾是運行在Windows, Mac, Linux 或者是 Unix 的機器上。但是,為了使這個時鍾可以工作,需要一個函數去讀底層的操作係統時鍾。因此,編碼了一個特定模塊來處理那些為每個操作係統定製化的係統調用。
在實踐中,我們假設Linux 上通用的共同代碼將放在主分支(main branch)[通常被稱為主幹(trunk),主(main)]。然後我們創建了一個變體(例如,Windows, Mac, 和 Unix分支),這些變體都包含了主幹上的所有代碼和另外一些必需的修改(例如,針對特定係統時鍾的操作係統調用)。在這個例子中(如圖1.1所示),所有分支唯一不同的代碼是係統時鍾查找函數。通過這種方法,可以有效地管理代碼的不同變體。
圖1.1 程序hello.c最初是在Linux下編寫的,然後在特定的操作係統分支上進行了修改。相對Linux 上的版本,每一個都叫作一個代碼的變體。
1.3.5 複製分支與增量分支
一些源代碼管理工具要求所有的分支是主幹上所有代碼的一個複本。如圖1.2所示,這種做法通常叫作拷貝分支。其他工具則是僅僅包含發生改變的代碼(通常叫作增量分支)。
主幹是分支的基礎。有時分支會很複雜,尤其是當人們開始用分支管理所有的開發活動時。一個常見的做法是每修複一個缺陷就建立一個分支。這種做法常被稱作建立功能分支。
圖1.2 拷貝分支是組織代碼變體一個通常的做法。
1.3.6 如何處理缺陷修複
支持缺陷修複通常是一個最常見的創建分支的需求。例如,你正在寫“Hello world”程序時,想在屏幕上也顯示一個漂亮的時鍾。這是一個現在版本不需要的額外功能。在將程序發給客戶幾天後,你發現每個月第三個星期二的時間都是錯誤的。圖1.3 展示了這種情況。雖然時鍾不是這個發布版本的內容,但是我們不能容忍屏幕上的時鍾是錯誤的。
圖1.3 在版本3中,已經開始的工作也許幾個月也完不成,但是現在該如何修補時鍾出現的問題呢?
程序的其他部分工作得都很好,所以隻要把時鍾隱藏起來,讓客戶看不到錯誤的時間就可以了。在產品的下一個版本中,我們會發布一個帶有全新時鍾的控製台,這個問題就不會存在了;而目前我們需要為客戶提供一個快速的修複,把這個錯誤的時鍾給隱藏起來。現在的問題是我們已經在版本3上做了很多隻有在下個版本中才會發布的更改,可那已經是2個月後的事情了。而與此同時,我們需要發布一個臨時解決代碼問題的缺陷修複,如圖1.4所示。
圖1.4 版本2.1的缺陷修複提供了一個可以立刻解決問題的補丁(隱藏那個錯誤時鍾)。
1.3.7 流
流是另外一種強大的管理代碼變體的方法。雖然和分支十分類似,但是流常常具有強大的管理代碼變體的特性。如下是其中的一些特性:
清楚的使用範例
清楚直觀的層次組織結構
流之間管理和操作變更集的能力(有時須借助工作空間)
通過快照建立代碼基線的能力
找到並獲取組件的某一個特定基線的能力
找到並獲得特定快照的能力(例如一個或者多個組件的基線)
強大的安全授權和認證機製
有助於流設計和使用的可視化流拓撲結構圖
可提供追溯能力的完整曆史信息
例如,一些源代碼管理工具可以層次化地組織流。也就是說,有一個父級流(常被用作集成)和子級開發流。一些源代碼管理工具可以允許你動態地變更子流的源。這個特性很有用,可以讓你在接口還未完成的組件上十分方便地工作。例如,創建一個代碼樁(Code stubs or mocks), 把其放在一個臨時流中供使用,一旦那些代碼被真正實現後再變更流的源。變更從子流到其父流的提升,也被稱為交付(deliver)。通常的做法是在交付特定變更集(Changesets)到上一級父流的時候,子流首先要從父流更新 (有時也稱作變基,rebasing) 中得到最新的更改。最佳實踐的做法是變基代碼到私有的工作空間,進行測試確保成功構建,然後交付變更到父級流。流通常有流向這個概念,以便變更集可以從一個流複製(或者說提升)到另外一個流。當有開發流和集成流的時候,通常會選擇這麼做。發布管理團隊(RM, Release Management)通常有一個自己控製的單獨分支來確保可以獨立地構建、打包、發布代碼和部署(常常由運維團隊來做)。沒有流,就需要額外的工作來將源代碼管理工具和缺陷或變更跟蹤係統集成到一起來組織分支,特別是如果想讓每個分支僅實現一個功能。在實際中,流比分支提供了更多的功能。流有點複雜,但是更強大,因此值得去學習和理解它們的用法。
1.3.8 合並
分支是一個很強大的特性,許多開發員發現分支可以幫助他們更有效地工作。問題是我發現很多開發人員忘記拉出分支的代碼需要經常合並(Merging) 回主幹。例如,在一個分支上修複了一個bug,通常在主幹上也需要修複(假設這兩者之間是相關的)。從分支合並回主幹通常被稱作內合並(Inner merge),如圖1.5所示。
有些時候,需要從主幹合並代碼到缺陷修複分支,通常被稱作外合並,如圖1.6所示。
圖1.6 外合並示例
1.3.9 變更集
變更集(Changesets) 是一種可以方便地將一個或多個對代碼庫的修改組織到一起的方法。大多數支持變更集的源代碼管理工具都可以提供應用或者回退變更集的功能。提交變更集(又被稱作簽入)通常是一個原子事務,這意味著整個變更都可以成功地簽入(或出現錯誤,完全地回退)。我非常感激變更集帶來的好處。還記得某個漫長的夜晚,當我試圖去提交幾千個文件的時候,在簽入的中途卻停止了的困窘。跟蹤哪些文件已經被成功簽入和哪些文件在簽入中途失敗需要再次提交是非常痛苦且容易出錯的事。考慮到變更集,一些源代碼管理工具提供了管理整個代碼庫的能力,這樣如果不小心失誤了,就可以容易地回退。通常可以把一個變更和一個缺陷或者一個需求關聯到一起,我們將會在下一節詳細討論具體方法。
1保留簽出模型,也被稱為悲觀鎖模型;而非保留簽出模型,也被稱為樂觀鎖模型。
最後更新:2017-06-06 07:35:52