原文地址:http://www.cnblogs.com/MeteorSeed/archive/2012/08/14/2604185.html
一 概述
軟件配置管理(SCM)是“系統化地定義軟件項目工作和處理變化,以使項目保持其完整性”的實踐活動。SCM關注於程序的需求、源碼、文檔和測試數據等所有項目相關的產物。配置管理策略將決定如何管理項目中發生的一切變化。因此,它記錄了你的系統以及應用程序的演進過程。另外,它也是對團隊成員協作方式的管理。
SCM的主要任務有兩個:第一,變更控制;第二,版本控制。以上兩點只是對SCM的基本概括,想要做好SCM不僅需要完善的流程也需要強大的軟件工具支持。
唯一不變的就是變化本身,接受變更是不爭的實事。軟件產品易於掌握的特性和不可見性,往往導致它的構建人員面臨着永恆的變更。因此必須制定相應的變更管理計劃,防止變更失控。
執導原則:
- 遵循某種系統化的變更控制手續。
- 將變更請求作爲整體考慮。
- 評估變更的成本,並告知相關干係人。
- 提防大量的變更請求,它表示先前的工作做得不夠好。
- 成立變更控制委員會或類似組織。
一般程序:
- 識別變更。
- 評價變更對項目整體的影響。
- 尋找處理變更的備選方案。
- 徵求項目干係人的意見。
- 批准或否決變更。
- 追蹤變更的實施情況。
必須清醒的認識到——沒有不變的需求。變更控制只是被動地應對到來的變更,此外,我們還可以主動迎擊變更。做法就是將不穩定的區域隔離出來,從而把變化帶來的影響限制在一個層、類或者組件的內部。下面給出相應措施:
1 找出看起來容易變化的範圍。需求中應該包含這份潛在變化的清單,下面是一些容易變化的區域:
-
- 業務規則
- 對硬件的依賴
- 輸入和輸出
- 非標準語言特性
- 困難的設計區域和構建區域
- 狀態變量(使用枚舉;使用訪問程序取代多狀態變量的直接檢查)
- 數據量的限制(eg.數組使用命名常量定義容量)
2 把容易變化的項目分離出來。
3 把看起來容易變化的項目隔離起來。
(這絕不是超前設計,而是利用分層、設計模式來達到犧牲少許性能來降低耦合度,提高靈活性的方法。)
版本控制系統是保存文件多個版本的一種機制。版本控制軟件可以幫助你得到以下益處:
- 別人正在修該某文件的同時,你可以同時修改這個文件。
- 你能方便地將你機器上的全部項目文件更新到當前版本。
- 你可以回溯到任何文件的任意版本。
- 你可以獲得一份文件更改的歷史記錄。
- 你無需本地備份,因爲版本控制提供了安全保障。
(一) 版本控制的內容
版本控制的目標是能夠隨時獲取軟件在整個生命週期中任意時間點的文件狀態。因此,任何與項目相關的文件都應該進行版本控制。以下內容應該進行版本控制:
- 管理相關文檔(包括項目章程、合同、項目範圍說明書、各類計劃、各類覈對表等項目管理過程中涉及的文檔)
- 開發相關文檔(包括編碼規範、需求分析、概要設計、詳細設計、數據字典、API幫助手冊、測試報告等涉及軟件構建的文檔)
- 系統原型
- 源代碼
- 實例代碼
- 測試腳本
- 數據庫腳本
- 構建和部署腳本
- 配置文件
- 編譯器及第三方工具
- 庫、SDK
以上內容都應該納入到版本控制行列,但根據項目規模可以有所裁剪,但以上加粗的內容必須進行版本控制。而有些內容不納入版本控制,這些內容是:
- IDE、DBMS、CASE工具等 這類軟件太大,而且管理他們沒有意義。
- 源代碼編譯後的二進制文件 沒有保存的意義,一些編譯器會針對本地CPU進行優化。
- 項目無關的任何文件 禁止在公共版本庫中籤入自己的測試DEMO。
(二) 版本控制策略
- 保證頻繁地提交可靠代碼 只有頻繁地提交代碼才能體現出配置管理的好處(輕鬆回滾到某個版本),而且能夠儘快釋放獨佔的資源,方便其他成員編輯。但是,並不是無限制的肆意追求提交頻率,而應該保證提交代碼的質量。在提交前獲取最新版本,並保證編碼編譯沒有問題是最基本的要求。將缺陷簽入版本控制只會浪費團隊的時間。更好的做法是,集中進行代碼審查,並將審查過後的代碼簽入版本控制庫。
- 對提交權限進行限制 不應該對全員開放全部權限,否則存在因爲人爲操作而影響開發的各種事件。最常見的是,將未經審查的代碼簽入當前版本,並導致全員的編譯錯誤。還有,就是由於對軟件使用不熟練而引起的各種誤操作。對不同成員,開放不同的權限是個比較穩妥的方案。
- 確保團隊中所有人在下班前簽入代碼,並在開發中都能及時獲取最新版本 如果你的團隊在使用版本控制軟件,那麼你上班的第一件事就不僅僅的查收郵件,還有同時獲取最新版本的工程。及時獲取最新版本是件好事,因爲多數開發人員是在進行C+V的操作,這也是爲什麼BUG只是出現集羣現象的原因之一。當我們Copy代碼時,複製新的往往比舊的更好,因爲新版本可能修復了某些缺陷。
- 使用意義明確的提交註釋 強制要求團隊成員使用註釋的原因是:當構建失敗後,你知道是誰破壞了構建,以及可能的原因及缺陷位置。這些附加信息,可以縮短我們修復缺陷的時間。推薦的註釋風格是這樣的:第一段是簡潔的概括性描述,之後是更新的具體細節。如果版本控制軟件支持,可以插入鏈接,將其鏈接到相關資源。
- 及時同步並定義明確的基線 基線應該是當前可交付成果的一個已確認的穩定的,具有正確部署狀態的版本。基線管理的主要內容包括: 應用程序的源代碼、構建腳本、測試、相關文檔、數據庫腳本、庫及配置文件等等。相關文檔必須保持同步更新,例如測試腳本的版本應該與代碼的版本一致。基線作爲某個重要的里程碑,其可交付成果應該是已確認的,是經過實際測試過的,具有較高質量的穩定版本。基線必須被妥善的管理和維護,因爲它將是今後開發的基礎,並且是具有價值的存在,是重要的組織過程資產。
- 使用增量的方式對當前代碼進行修改,儘量避免創建分支 創建分支存在以下問題:
1 推遲了新功能的整合,對分支進行合並存在發生集成問題的風險。
2 多個分支難以管理,並會增長集成的風險。
3 很難避免重名問題。
4 代碼整合將會隨着分支數量的上升,變得越來越困難。
備份計劃指的是定期備份工作的成果。你的備份計劃應該包括定期進行備份,並且定期地將備份介質轉移到脫機存儲介質中。
在制定備份計劃的時候,人們常常忽略的一點是:要測試你的備份過程。應該對數據恢復進行演練,以確認備份數據中包含了所需要的全部數據,並且可以成功恢復。
必須由專人負責,做到責任到人並且明確定義需要備份的內容以及備份的週期。負責人往往具有版本控制軟件最高的權限,可以查閱所有文檔,而且其維護的是企業的重要組織過程資產,責任重大,應該謹慎選擇。
(一) 介紹
SVN是一款非常知名的中央版本控制系統。(與中央版本控制系統對應的是分佈式版本控制系統,如DVCS。分佈式版本控制系統支持大型開源團隊的需要,一般項目使用中央版本控制系統足以。)版本控制系統用於維護應用程序每次修改的完整歷史,包括源代碼、文檔、數據、腳本等等。它的另一個重要用途,讓團隊一起工作在應用程序的不同部分,同時維護系統記錄,即應用程序的權威代碼基。SVN由商業組織Collabnet維護,它提供收費支持。
SVN中,版本控制的單元是修訂(revision),它由多個目錄內文件的一組變更構成。可以把每個修訂看成當時版本庫中所有文件的一個快照。在SVN中每次提交都會應用所有更改,這一過程是原子性的,並且創建一個新的修訂版本。SVN版本號是針對整個代碼庫的,而不是每個文件對應一個版本號。SVN用對待文件的方式對待目錄、文件屬性和元數據。
每個SVN版本庫在創建時,可以選擇生成3個默認目錄:trunk(主幹)、tags(標籤)和branches(分支)。當創建分支(標籤)時,只是在branches(tags)目錄下創建一個新目錄,並將trunk上你想創建分支(標籤)的那個修訂版本的內容複製到該目錄中。剛剛創建的分支(標籤)和trunk指向同一組對象級,直到它與主幹開始不一致爲止。SVN不區分標籤與分支,它們只是用途不同而已。
SVN的一些限制或者說是需要注意的地方:
- 只能在線提交變更。
- SVN在本地客戶端用於更正變更的數據被保存在版本庫每個文件夾的.svn目錄下。
- 儘管服務端是原子性操作,但客戶端的操作不是。
- 修訂號在知道的版本庫中是唯一的,在不同版本庫之間不唯一。
- SVN支持樂觀鎖和悲觀鎖。
(二) VisualSVN-Server(服務端)
1 安裝
第一步:
第二步:
2 使用
主界面如下:
第一步:創建版本庫
第二步:創建用戶
第三步:創建分組
第四步:配置權限
(三) SVN(客戶端)
1 安裝SVN
安裝過程唯一需要注意的是,在安裝完成中文語言包後,要在SVN中設置使用語言,否則還是會按英文顯示。
第一步:點擊“Settings”。
第二步:設置使用語言。
2 SVN的使用
SVN的定義的圖標及其含義如下:
無版本控制。
正常狀態。
表示本地文件與版本庫不同步。
本地文件與版本庫存在衝突。
表示只讀文件(被他人加鎖的文件)。
表示該文件已加鎖。
表示當前文件夾下有文件或文件夾從版本庫中刪除或者丟失了。
表示有文件或目錄被添加到了版本庫中。
- 連接SVN版本庫
第一步:新建一個存放版本庫的目錄。
第二步:點擊目錄,右鍵彈出菜單選擇“SVN檢出”。
第三步:輸入URL。
第四步:輸入用戶名及密碼。
- 更新文件
獲取最新版本 在右鍵菜單中點擊“SVN更新”。
獲取特定版本 在右鍵菜單中點擊“更新至版本”。
- 添加文件
第一步:新增文件。新增文檔會顯示圖標,表示無版本控制。在該文件上右鍵選擇菜單選擇“增加”,以聲明對該文件進行版本控制。
第二步:聲明對其應用版本控制。添加之後,圖標會變爲,此時文件只是存在於本地,只有“簽入”後纔會上傳到版本庫中。
第三步:“簽入”修改。
- 刪除文件
處在版本控制下的文件,不能在本地刪除,需要從版本庫中刪除才能徹底刪除文件。
第一步:選中目錄或文件,在右鍵菜單中選擇“刪除”:
第二步:“簽入”修改。
- 簽入文件
選中目錄或文件,在右鍵菜單中選擇“”,會出現提交對話框,按需填寫相應信息,並選擇需要簽入的文件:
點擊“確定”開始簽入文件,簽入成功後會返回本次操作的結果:
- 重命名文件
第一步:右鍵菜單選擇“改名”。
第二步:輸入新名稱。
第三步:“簽入”修改。
- 創建分支
第一步:選中目錄,點擊右鍵菜單中的“分支/標記”。
第二步:輸入分支保存路徑以及日誌信息。
第三步:獲取最新版本。如果不獲取最新版本,在目標位置是找不到分支目錄的。
- 合併分支
第一步:在相關目錄下的空白處(即合併起始位置的URL,可以從分支合併到主幹,也可以從主幹合併到分支),點擊右鍵菜單中的“合併”。
第二步:選擇合併類型。
第三步:合併。
第四步:如果存在衝突請解決衝突。(這裏不做詳細解釋,下文會詳細解釋衝突產生過程及如果解決。)
- 其它常用功能
- 使用瀏覽器查看版本庫
輸入正確的服務地址,用戶名密碼即可。
3 版本衝突
- 版本衝突的產生過程
以下流程模擬了一個版本衝突的產生。
用戶B在簽入時會失敗。
- 版本衝突的解決方法
- A放棄自己的更新。(迴避問題)
- B放棄自己的更新。(迴避問題)
- 解決A與B的衝突。(解決問題)
前兩種做法比較極端,且在迴避問題,以下說明第三種做法的步驟。
第一步:用戶B獲取最新版本。獲取後,會發現衝突文件被會顯示。
第二步:選中衝突文件,點擊右鍵菜單中的“編輯衝突”。
第三步:將修改後的文件標記爲已解決。
第四步:用戶B“簽入”文件。
我在“三 版本控制”中闡述版本控制的基礎知識,其中主要包括的版本控制的內容與策略。爲了能將理論聯繫實際,以下給出了一些比較好的實踐,以供參考。
(一) 工作之前先更新
應該頻繁地更新工作有關的文件,頻繁獲取最新版本,這對一個團隊活動來說十分有益。因爲當你在發現返回結果莫名其妙的時候,可能你的隊友已經提交了對某個缺陷的修改。很多開發人員都知道新的版本一般會包含更少的缺陷,但仍有兩方面,在困擾這開發人員去頻繁獲取最新版本:
第一,隊友在肆意破壞構建。可能你更新一次,就會因爲別人引人的某個缺陷而無法繼續工作。
第二,不穩定的網絡。更新時間很長,長到難以忍受。
可能最多的情況還是因爲第一點。對於這種情況,如果這種肆意破壞構建的現象已經普遍了,那認命吧,脫機使用可能更好一些。
(二) 構建失敗了不要提交新代碼
正確的步驟是在提交修改之前,應該先獲取相關文件的最新版本,然後重新編譯工程。待本地測試並通過代碼審查(如果有)後,再提交新代碼。如果構建失敗了,你應該修復代碼,而不是把問題擴散到版本庫中。我曾目睹過可笑的事情:一位開發人員,把編譯不通過的代碼簽入服務器的原因竟然是想讓別人幫他看看是哪裏寫錯了。不要讓這種鬧劇在你的團隊中上演,如果你的某位成員經常性地破壞構建,那麼你應該果斷地收回權限,以維持版本庫正常的構建,減少不必要的修復時間。
(三) 提交有受測的或已審查的代碼
版本控制要求成員提交可靠代碼。測試和代碼審查均是代碼質量保障的有效手段。代碼審查的軟件質量控制最有力的手段,其缺陷檢出率也是最高的,但也將消耗掉相當大的成本。並不是所有團隊都能做好代碼審查,代碼審查需要技術硬手,要有一定的重構經驗。事實是,很多小項目沒有配置相應代碼審查人員,或因團隊能力有限而放棄代碼審查步驟。導致缺陷在項目中蔓延,代碼的味道越來越壞。代碼審查與測試的重要性,並不是本文討論的重點。我要說明的是,我們進行配置管理的對象是可靠的代碼,可靠的代碼不等於編譯通過即可,它至少意味着更少量的缺陷。所以,即使你的項目團隊沒有配置代碼審查,沒有人寫單元測試,最起碼地請保證在警告數量爲0後簽入代碼,因爲IDE的作者們比你更瞭解語言。
(四) 提交後測試主版本是否可以工作,成功後再幹別的
這是很多團隊都在採用的方法。道理很簡單,你的代碼沒問題了你纔可以下班。很多團隊都是在快下班時,要求成員提交代碼。實踐證明並不理想,因爲經常會發生一件最最痛苦的事情——下班了,活兒來了。我更傾向於在下午3:00發佈系統。第一,每位成員都清楚程序發佈的時間,在早晨他們就會處理手上的代碼(爲了按時回家),一般在上午就能完成調試和測試工作,很多事情沒有必要非拖到下班來幹。第二,因爲下午2:00~3:00,人會犯困,在開發人員等待測試人員的反饋時,他們可以休息一下,畢竟發佈前的準備可能已導致了數日的趕工。第三,也是最重要的一點,一旦構建出現問題,我有足夠的時間處理缺陷,並能確保當天測試團隊能拿到一個可測試的版本。
(五) 簽入代碼時最好進行一次代碼走查並確保簽入了所有相關代碼
在簽入代碼時進行一次差異比較是一種好的實踐,成員可以最後一次查看自己的代碼,進一步降低缺陷別引入的機率。切忌不要僅僅簽入部分代碼,這樣做很可破壞構建。你自己可能沒有察覺,因爲本地構建是正常的,可是,其他人卻因爲缺少某個文件而無法通過編譯。
(六) 時刻準備着回滾到候選版本
應該維護好我們的版本信息,清楚當前回滾的最佳選擇。
(七) 在回滾之前規定一個修復時間
建立一個團隊規則:如果因某次提交而導致構建失敗,必須在某個時間之內修復它,如果無法修復,則回滾到之前的最好版本。
(八) 責任到人
將其與績效考覈緊密聯繫,讓守規矩的成員得到褒獎,並讓肆意破壞構建,違反團隊規則的人,爲他的行爲負責。
(九) 樂觀鎖往往更高效
SVN默認使用樂觀鎖(不會以獨佔方式打開文件,他人也可以修改該文件),但同時支持悲觀鎖(獨佔文件方式)。兩者的選取主要由成員的分工,系統架構的耦合程度來決定。如果,某些文件很少被公用,對於這些文件則使用悲觀鎖比較有利於解決合併衝突。相對的,如果團隊成員可能會同時工作在同一個文件上,例如:所有成員均需要編輯DAL層某個類的代碼,那麼這個文件就需要使用樂觀鎖。事實上,越是大項目,人員越多,系統解耦越不徹底,越適合使用樂觀鎖。對於某個文檔的修改時,可能是你使用悲觀鎖的少有的情況。
我在上文提到過,應該儘量避免創建分支,而是使用增量式的開發。每次創建分支,都要認識帶它帶來的成本。這種成本在於“增加了風險”,而唯一最小化風險的方法就是無論由於什麼樣的理由創建了分支,都要努力保證任何活躍分支合併到主幹。
在開發過程中,通過頻繁向主幹提交的方式做增量式修改幾乎總是最正確的做法,主幹開發也是集成成本最低的模式。在主幹開發中,開發人員幾乎總是簽入代碼到主幹,而使用分支的情況很少。主幹開發有以下優點:
- 確保所有的代碼被持續集成。
- 確保開發人員及時獲得他人的修改。
- 避免項目後期的“合併地獄”和“集成地獄”。
主幹開發並不排除分支。它意味着——所有的開發活動在某一時間點上都會以單一代碼基線而告終。主幹開發的一個可預測結果是:每次向主幹簽入並不都是可發佈狀態。任何人對主幹的修改都可能破壞構建。因此,在使用主幹開發的團隊中,應該進行持續的過程改進以縮短修復構建的時間,保證主幹在大多數時間處於可工作的狀態。
推薦使用分支的情況包括:
- 爲發佈創建分支 爲了發佈應用程序的一個新版本需要創建一個分支。這使開發人員能夠不斷開發新功能,而不影響穩定對外發布版本。當發現缺陷時,首先在相應的對外發布分支上修復,然後再合併到主幹上。而該分支從來不會合並回主幹。
- 爲研發創建分支 當需要調研一個新功能或做一次重構時——調研分支最終會被丟棄並且從來不會合並回主幹。
- 大範圍的修改 比如決定使用WPF代替WinFrom時,需要一部分人繼續維護WinFrom版本,而另一些人開發WPF版本。類似這種大範圍的修改,也可以使用分支。
下面介紹常見的分支模式。
(一) 爲發佈創建分支
在某個版本即將發佈之前,爲該版本創建分支,該版本的測試和驗證全部在該分支上進行,而最新的開發工作仍舊在主幹上進行。在這種模式中,要遵循如下規則:
- 一直在主幹上開發新功能。
- 當待發佈版本的所有功能都完成了,且希望繼續開發新功能時才創建一個分支。
- 在分支上只允許提交那些修復缺陷的代碼,並保證這些代碼必須儘快(最好立即)並完整地被合併回主幹。(我使用覈對表,來確保不遺漏問題。)
- 當執行實際發佈時,這個分支可以選擇性地打一個標籤。
- 不要在已有的發佈分支上再創建更多的分支,所有的後繼分支應該從主幹上創建,分支的形狀如下:
(二) 按功能模塊分支
使明確分工的團隊,在各自負責的模塊上工作,在並行開發的同時,保持主幹的可發佈狀態。使用該模式要做到以下環節:
- 每天都要把主幹上的所有變更合併到分支上。
- 每個功能模塊分支都應該是短生命週期的。
- 分支只有經過用戶確認後,才能合併回主幹。我這裏所說的確認,應該理解爲由關鍵用戶對設計的認可,而不是走完完整測試流程後進行的驗收確認。因爲,在分支上,開發人員只能進行單元測試,其它大規模測試針對的是主幹。所以,分支合併前,不建議做大規模測試,也很難有足夠的資源做大規模測試。我建議,讓關鍵用戶確認其設計是否能滿足用戶當前需求,如果滿足,則合併回主幹進行大規模測試;如果不滿足,則走變更流程。
- 重構必須即時合併,從而將合併衝突最小化。
- 保證主幹的可發佈狀態。
- 不屈服於交付壓力而合併爲達標的分支。
- 儘量避免在分支上再創建分支。
使用這種模式太容易“將確保應用程序處於可發佈狀態所需要解決的痛苦”推遲到後期。應該做成分的估計,確保採用這種模式所取得的收益遠比其開銷更重要,而且要確保在按進度發佈系統。
該模式分支形如:
(三) 按團隊分支
該模式分支形如:
按團隊分支的流程如下:
- 創建多個團隊,每個團隊自己都有對應的分支。
- 一旦該團隊的階段性工作完成,就讓該分支穩定下來,併合並回主幹。
- 每天將主幹上的變更合併到每個分支上。
- 對於每個分支,每次簽入後都要運行單元和驗收測試。
- 每次一個分支合併回主幹時,在主幹上都要運行所有的測試。
非常重要的是,每個分支都要有一個責任人,一般是TeamLeader,由他負責定義和維護該分支的規則,包括管理誰可以向分支提交代碼。
這種分支模式與按功能模塊相比,優點是分支較少,而缺點是各分支很快會變得差異很大。主要風險是,在進度壓力下,團隊不能充分遵守關於合併回主幹以及從主幹更新代碼的規則。團隊分支很快會和主幹變得不一樣,彼此之間的差異也會很大,所以,合併會很耗時。
現實的項目中,需求變更以及在配置管理中使用分支幾乎無法避免。好的SCM可以有效地使變更處於受控狀態,降低合併衝突,保證主幹代碼儘可能處於可發佈狀態。但是,項目規模做大,管理力度失衡,就會發生種種失控的狀態。變更控制會失效,成員深陷於痛苦的“變更>修改>發佈>變更>修改>發佈”的死循環中。分支之間相互嵌套,團隊規範形如廢紙。版本庫形成非常複雜的網絡已經無法維護。伴隨着分支的積增,資源的分配也更加細碎,成員之間會彼此抱怨別人的代碼破壞了構建。需求無法凍結,導致長期的趕工,資源開始流失。這種情況,很可能在我們的身邊上演。不要寄希望於SCM能完全防禦掉這些風險。SCM只是我們促成項目成功的方式之一。
我的建議是,在合理範圍下,使用再多的有利於促成項目成功的措施也不爲過。對於配置管理,應該以主幹開發爲首選,如果沒有必要絕不要使用分支。此外,程序架構應該盡力解耦,鬆耦合的開發可以將影響和變化源縮減到很少。鬆散的耦合,更利於應對突如其來的變更。此外,越少的修改,合併衝突的機率就越低。因此,降低集成成本,不僅需要合理的SCM策略,也需要在程序架構方面動一番腦筋(例如面向組件開發,面向服務開發)。
主要參考書籍:
- 《代碼大全》
- 《持續交付:發佈可靠軟件的系統方法》