基本定義
版本控制透過文檔控制(documentationcontrol)記錄程序各個模組的改動,并為每次改動編上序号。這種方法是工程圖(engineeringdrawings)維護(maintenance)的标準做法,它伴随着工程圖從圖的誕生一直到圖的定型。一種簡單的版本控制形式,例如,賦給圖的初版一個版本等級“A”。當做了第一次改變後,版本等級改為“B”,以此類推等等。
相關系統
1.軟件系統的版本控制是指可以自行運行的各子系統的版本控制。
2.軟件系統的版本号由評測小組的人員确定,由評測小組進行版本控制工作。
3.軟件系統的版本号由3部分構成,即主版本号+次版本号+修改号。主版本号1位,隻有當系統在結構和功能上有重大突破改進後才發生變化;次版本号有2位;修改号8位,采用提交時的日期,當系統進行任何修改後,包括數據庫結構發生變化,修改号都要随之改變。例如:Ver3.31.19990317
4.各子系統的版本号獨立。
5.各軟件系統應該有顯示詳細版本号的功能。例如help菜單下的about功能。系統提交存檔時,評測服務部要進行版本号檢查。
6.新系統開發完成、或已存檔的系統進行修改,修改完成後,進行提交存檔時,由評測評測小組系統分析工程師确定新版本号、或更改版本号。
7.軟件系統,産生新的版本後,老版本的軟件系統是否繼續保存,取決于以下條件:
a.老版本的系統如果有客戶還在使用,在客戶升級以前,必須繼續保存。
b.老版本的系統已經沒有客戶使用了,并且新版本的系統已經把老系統的文檔完整地升級過來,這樣可以删除或複蓋老版本的系統資源。
c.對于要删除或複蓋的老版本系統,可以統一備份起來。
本地系統
許多人習慣用複制整個項目目錄的方式來保存不同的版本,或許還會改名加上備份時間以示區别。這麼做唯一的好處就是簡單,不過壞處卻不少:有時候會混淆所在的工作目錄,弄錯了文件丢了數據就沒了退路。
為了解決這個問題,人們很久以前就開發了許多種本地版本控制系統,大多都是采用某種簡單的數據庫來記錄文件的曆次更新差異。
其中最流行的一種叫做rcs,現今許多計算機系統上都還看得到它的蹤影。甚至在流行的MacOSX系統上安裝了開發者工具包之後,也可以使用rcs命令。它的工作原理基本上就是保存并管理文件補丁(patch)。文件補丁是一種特定格式的文本文件,記錄着對應文件修訂前後的内容變化。所以,根據每次修訂後的補丁,rcs可以通過不斷打補丁,計算出各個版本的文件内容。
集中化的系統
接下來人們又遇到一個問題,如何讓在不同系統上的開發者協同工作?于是,集中化的版本控制系統(CentralizedVersionControlSystems,簡稱CVCS)應運而生。這類系統,諸如CVS,Subversion以及Perforce等,都有一個單一的集中管理的服務器,保存所有文件的修訂版本,而協同工作的人們都通過客戶端連到這台服務器,取出最新的文件或者提交更新。多年以來,這已成為版本控制系統的标準做法。
這種做法帶來了許多好處,特别是相較于老式的本地VCS來說。現在,每個人都可以一定程度上看到項目中的其他人正在做些什麼。而管理員也可以輕松掌控每個開發者的權限,并且管理一個CVCS要遠比在各個客戶端上維護本地數據庫輕松容易得多。
事分兩面,有好有壞。這麼做最顯而易見的缺點是中央服務器的單點故障。若是服務器當機一小時,那麼在這一小時内,誰都無法提交更新,也就無法協同工作。
如果中央服務器的磁盤發生故障,并且沒做過備份或者備份得不夠及時的話,還會有丢失數據的風險。最壞的情況是徹底丢失整個項目的所有曆史更改記錄,被客戶端提取出來的某些快照數據除外,但這樣的話依然是個問題,你不能保證所有的數據都已經有人提取出來。本地版本控制系統也存在類似問題,隻要整個項目的曆史記錄被保存在單一位置,就有丢失所有曆史更新信息的風險。
分布式系統
于是分布式版本控制系統(DistributedVersionControlSystem,簡稱DVCS)面世了。在這類系統中,諸如Git,Mercurial,Bazaar還有Darcs等,客戶端并不隻提取最新版本的文件快照,而是把原始的代碼倉庫完整地鏡像下來。這麼一來,任何一處協同工作用的服務器發生故障,事後都可以用任何一個鏡像出來的本地倉庫恢複。因為每一次的提取操作,實際上都是一次對代碼倉庫的完整備份。
更進一步,許多這類系統都可以指定和若幹不同的遠端代碼倉庫進行交互。籍此,你就可以在同一個項目中,分别和不同工作小組的人相互協作。你可以根據需要設定不同的協作流程,比方說層次模型式的工作流,這在以前的集中式系統中是無法實現的。
詳細内容
版本控制包括兩個方面:保正人人得到的是最新的版本,記錄需求的曆史版本。
如果有專門的需求管理商業工具可以助您一臂之力,由于我并沒有條件試用所有的需求管理工具,能夠向大家推薦的隻有瑞理公司的RequisitePro,推薦的一個重要原因是它能夠把需求和瑞理的其他工具如Rose、TeamTest等聯系起來,從而實現需求鍊。
能夠借助工具将需求自動化固然很好,不過,工具使用不當也不會提高生産效率。需求管理的工具其實用簡單的Office和任一個關系型數據庫就可以解決,而且根據企業自身的特點,摸索出最适合企業用的工具。
版本控制的最簡單方法是在每一個公布的需求文檔的版本應該包括一個修正版本的曆史情況,即已做變更的内容、變更日期、變更人的姓名以及變更的原因并根據标準約定手工标記軟件需求規格說明的每一次修改。
一、版本控制業務流程
利用WebLogicWorkshop的版本控制功能,能夠在不中斷當前正在運行的任何流程實例的情況下對業務流程進行更改。對業務流程進行版本控制時,便是創建了業務流程的子版本,該版本與其父版本共享同一公共URI(接口)。運行時,标記為有效的流程版本便是将由外部客戶端通過公共URI來訪問的流程。
注意:可以對業務流程進行版本控制,但無法對與該流程關聯的單個控件或其他與業務流程有關的組件(如schema和轉換)進行版本控制。對業務流程進行版本控制時,還必須對該流程的子流程進行版本控制,因為對父流程進行版本控制時,該控制對其子流程無效。
二、版本控制中的關鍵術語
簽入文件或目錄
此操作将工作目錄作為新版本複制回存儲庫。
簽出文件或目錄
此操作從存儲庫中将文件的最新修訂版本複制到工作空間。簽出目錄時,将簽出該目錄下的所有文件和子目錄。
提交文件或目錄
此操作與簽入文件或目錄相同。版本控制用戶會經常說他們“已提交更改”;這表示他們對各自文件的工作副本做了更改,并将這些更改提交到存儲庫。
沖突
當兩名開發人員對同一文件的工作副本進行更改,并将這些更改提交到存儲庫時,他們的工作可能會發生沖突。在這種情況下,CVS或Subversion将檢測沖突,并要求某個人先解決該沖突,然後再提交他們的更改。
合并
将對相同文件的不同工作副本進行的多個更改合并到源存儲庫中。合并是一種管理沖突的策略,它允許多名開發人員同時工作(不必對文件進行鎖定),然後将他們的工作并入一個組合版本中。
當對同一文件的不同行進行兩組更改時,合并這兩組更改很容易,而合并操作也可正常進行。但對文件的同一行或幾行進行更改時,将發生沖突,這就要求有人手動編輯該文件,然後才能将這些更改成功提交到源存儲庫。
存儲庫
具有受版本控制的所有文件的完整修訂曆史的共享數據庫。
解決
當兩名開發人員試圖提交發生沖突的更改,而造成文件内的沖突時,必須通過手動編輯該文件進行處理。必須有人逐行檢查該文件,以接受一組更改并删除另一組更改。除非沖突解決,否則存在沖突的文件無法成功提交到源存儲庫中。
修訂版本
對各個文件進行具體更新的編号草案。每次編輯文件并将它提交回存儲庫時,該文件的修訂版本号将會增加。
版本
用于标識文件集的編号方案,可在某個時間點标記并命名這些文件集。
工作空間
要在本地硬盤或Unix用戶帳戶上編輯的文件副本。在工作空間中編輯文件時,這些文件将不再與存儲庫同步。這就是進度!然後您需要将更改返回存儲庫,以便其他人可以看到這些更改。
Subversion詞彙表
APR
Subversio置于稱為APR(Apache 可移植運行庫)的可移植層上。這意味着Subversion應該在任何運行Apache httpd的操作系統上工作:Windows、Linux、BSD 的所有 flavors、Mac OS X、Netware 以及其它操作系統。
分支
分支是指目錄和文件的現有原始樹的副本。分支的生命周期是從某事物的副本開始的,并從此副本處移動,生成自己的曆史。通常創建分支以嘗試新功能,同時不影響具有編譯器錯誤和小問題的開發的主分支。
檢出
檢查存儲庫,會在本地計算機上創建所需分支的副本。此副本包含了您指定的存儲庫的最新版本。
提交
文件的提交意味着已将對本地副本所做的更改更新到存儲庫中。提交文件後,用戶可以查看對特定文件執行“更新”後的最新版本。
沖突
有時候,當您更新存儲庫中的文件時,可能會遇到沖突。當兩個或多個用戶更改文件中的一些相同行時,将發生沖突。
Hook
Hook是被存儲庫事件觸發的程序,例如創建新版本或修改無版本屬性。Hook中保留了足夠的信息,可以告知該事件是什麼、正被操作的目标是什麼,以及觸發此事件的人員的用戶名是什麼。
鎖定
鎖定是指一種機制,在此機制下,用戶請求獲得修改工作副本文件的更改的專有權限。
合并
合并是指将某分支上的更改聯接到此主幹或同為主幹的另一個分支。
存儲庫
Subversion的核心為存儲庫。它是一個存儲和共享數據的集中式系統。存儲庫以一組樹和分支的形式(即目錄和文件的層次結構)存儲信息。任何數量的客戶端都可以連接到存儲庫中,并對這些文件進行讀取和寫入。
存儲庫浏覽器
在某些情況下中,可能需要直接在存儲庫中工作而不使用工作副本。這便是存儲庫浏覽器的由來。它與資源管理器窗口具有同樣的圖标以及用于鍵入将顯示的存儲庫URL名稱的地址欄。它還具有諸如複制、移動和删除等的命令。
存儲庫URL
可以通過本地磁盤上的不同方法或通過網絡協議訪問存儲庫。存儲庫位置通常指URL。這些URL使用标準的語法,其中引用要指定的服務器名稱和端口号。
撤消
如果檢查時決定取消對文件所做的更改,可以使用“撤消”命令跳轉回先前的更改。
修訂版本
每次存儲庫接受提交時,都将創建文件系統樹的新版本,稱為修訂版本。會為每個修訂版本分配唯一号,此号比上一修訂版本的号大。剛創建的存儲庫的初始修訂版本編号為零,且其中除了空的根目錄外不包含任何信息。
修訂圖形
修訂圖形是主幹位置的圖形化表示,其中分支與标記與主幹是分離的。這與樹結構非常相似,且很容易查看這類信息。
修訂版本号
創建新存儲庫時,其生命周期從修訂版本号零開始,且每次後續提交會将修訂版本号增加一。提交後,Subversion客戶端将提供新的修訂版本号。每個修訂版本号都會在下方挂起一棵樹,每棵樹都是存儲庫對待每次提交的方式快照。
轉換
此子命令将更新工作副本以鏡像新的URL;通常是共享工作副本中的公共祖先的URL。這是将工作副本移動到新分支上的Subversion方法。
标記
标記主要指在每個文件上置入一個标簽,無論此文件的修訂版本号。這既可以在工作副本上執行,也可以在存儲庫自身執行;其效果相同。
更新
更新可以使工作副本與用戶對存儲庫所做的最新更改同步。它會取出文件的最新工作副本置于本地驅動器上。遵照滑塊規則,總是在更改文件前更新此文件。
工作副本
工作副本是指從存儲庫獲取的文件的現有副本和已更新副本。若要獲得工作副本文件,需要執行檢出。
複制-修改-合并開發周期
由于CVS和Subversion都是功能強大的工具,所以學習過程可能會讓人望而卻步。大量的書籍和網站提供全面的CVS知識庫,但提供Subversion知識庫的并不多。但是,不是非得學會了整本書才能在軟件開發實踐中迅速有效地使用CVS或 Subversion。
在與項目的整體開發周期保持一緻的情況下,CVS和Subversion都允許您進行自己的開發。
1、要開始項目工作之前,您需要簽出源代碼。您可以簽出該項目的整個CVS或Subversion存儲庫,也可以隻簽出您希望處理的那些模塊。
2、通過修改這些文件和創建新文件,為項目作出貢獻。
周期的這一部分并不直接涉及CVS或Subversion。您可在本地計算機上使用文件編輯器修改項目文件的工作副本。還可以保存并編譯您編輯過的文件,以測試您所做的更改如何影響正在處理的特定項目模塊,此過程不影響其他人對同一項目文件的工作。您所執行的任何操作都不影響其他項目參與者,除非您将更改合并到項目存儲庫中。
3、您在自己的工作空間中測試和調整您最新的更改,以确保這些工作不中斷或損壞整個項目。
4、最後,将您所做的更改返回或簽入項目文件的主要或“頂級”主體,将您的工作合并到最近的工作版本(在版本控制術語表中稱為head)。
提交您的更改以與其他開發人員的工作合并是CVS和Subversion最強大的功能,但此功能也使它成為最危險的方面。有時可能因為一時糊塗,無意中複蓋了他人或您自己的更改。您所提交的更改将始終在某些方面與其他人的更改相沖突。在合作開發項目中,理解?沖突是使用CVS或Subversion時兩個特别關鍵的方面。
所有起作用的開發人員在項目生命周期中都在不斷重複着這個複制-修改-合并周期.CVS和Subversion使得每個人都能同時處理項目文件,掌握其他人進行的最新更改,以及測試自己的更改如何影響整個項目,這些過程都不會中斷其他開發人員的周期。
三、版本控制的選擇
在八月份的時候,一些讀者寫信要求我說明如何控制接口的版本。實作新版接口時,有些情況你隻需要強化現有類别,在其它情況你則需要實作一個可能使用前版的新類别。我想要提出的是相信大部分讀者都會遇到的組合情況,這裡列出當您在更新Web服務時最常面臨到的工作:
1.新增額外的方法。新方法在概念上和現有的Web服務是相關連的,而且應該在相同的端點上實作。
2.變更方法簽名碼。在這個實例中,輸入組件的數目和類型會改變。
3.更新數據模型。在這個實例中,資料型别會擴充且資料成員可能會改變名稱。
為了準備場景,我想要規劃一個簡單的Web服務,讓它能夠接受所有類型的改變,這個Web服務代表版本1。類别和這個Web服務會對應一個命名空間同步進行。所有動作都會随着這篇文章逐步發展。
版本1
我要為稍後會作的修改提供一個起點。每一個區段都建置在這個初始Web服務之上,然而下面的區段卻是建立在彼此的Web服務上。這個服務是設定用來處理訊息,以便将兩個數字加起來,以及将一些基本個人資料轉換成字符串。
【WebServiceAttribute(Namespace=NamespaceConsts.AYS15Oct2002)】
publicclassVersionOne:System.Web.Services.WebService
【WebMethodAttribute】
publicstringGetDisplayValue(Personperson)
returnperson.ToString();
【WebMethodAttribute】
publicintAdd(inta,intb)
returna+b;
命名空間字符串會儲存在一個位置中,即NamspaceConsts類别,來減少由于打字錯誤産生的問題。我會大量重複使用這個命名空間值,來減少輸入錯誤字符串的機會。
publicclassNamespaceConsts
這個内容值包含用于XML命名空間的字符串
http:msdn.microsoft.comsamplesAYS20021015
publicconststringAYS15Oct2002=
"http:msdn.microsoft.comsamplesAYS20021015";
這個内容值包含用于XML命名空間的字符串
http:msdn.microsoft.comsamplesAYS20021022
publicconststringAYS22Oct2002=
"http:msdn.microsoft.comsamplesAYS20021022";
最後,第一版的Web服務采用了一些其它類别:
·PersonName:這個類别包含三個字符串成員變量分别紀錄一個人的名字、中間名和姓氏。
·USAddress:這個類别包含其它成員變量代表街道地址、城市、州和郵政編碼。
·Person:這個類别包含兩個公用成員,PersonName和USAddress。
所有的類别都使用System.Xml.Serialization.XmlTypeAttribute來确定當類别序列化成XML時,這些類别是使用相同的XML命名空間。在這裡以Person類别為例子。
【XmlTypeAttribute(Namespace=NamespaceConsts.AYS15Oct2002)】
publicclassPerson
publicPersonNameName;
publicUSAddressAddress;
publicoverridestringToString()
returnstring.Format("0n1",
Name.ToString(),
Address.ToString();
這裡非常詳細的說明這個Web服務版本1的内容。
增加額外的訊息
【WebServiceAttribute(Namespace=NamespaceConsts.AYS15Oct2002)】
publicclassVersion2a:System.Web.Services.WebService
【WebMethodAttribute】
publicstringGetDisplayValue(Personperson)
returnperson.ToString();
【WebMethodAttribute】
publicintAdd(inta,intb)
returna+b;
【WebMethodAttribute】
publicintSubtract(inta,intb)
returna-b;
【WebServiceAttribute(Namespace=NamespaceConsts.AYS22Oct2002)】
publicclassVersion2a:System.Web.Services.WebService
【WebMethodAttribute】
【SoapDocumentMethodAttribute(
NamespaceConsts.AYS15Oct2002+"GetDisplayValue",
RequestNamespace=NamespaceConsts.AYS15Oct2002,
ResponseNamespace=NamespaceConsts.AYS15Oct2002)】
publicstringGetDisplayValue(Personperson)
returnperson.ToString();
【WebMethodAttribute】
【SoapDocumentMethodAttribute(
NamespaceConsts.AYS15Oct2002+"Add",
RequestNamespace=NamespaceConsts.AYS15Oct2002,
ResponseNamespace=NamespaceConsts.AYS15Oct2002)】
publicintAdd(inta,intb)
returna+b;
【WebMethodAttribute】
【SoapDocumentMethodAttribute(
NamespaceConsts.AYS22Oct2002+"Subtract",
RequestNamespace=NamespaceConsts.AYS22Oct2002,
ResponseNamespace=NamespaceConsts.AYS22Oct2002)】
publicintSubtract(inta,intb)
returna-b;
因為我們選擇建立這個Web服務的方式的不同,現有的屬性方法,亦即允許開發人員指定特定的作業所屬的系結,在這個情況是沒有作用的。Add和GetDisplayValue的訊息并不會因為任何方法而改變,意味着我不能隻是「正确地」設定屬性然後就繼續進行。關于這點,您有兩個選擇:
1.撰寫一些額外的程序代碼而且放棄在一個端點上支持兩個版本。
2.将這兩個版本的系結信息儲存在各自的檔案、并為這個端點撰寫一個自訂的WSDL檔案,然後關閉這個Web應用程序文件。
讓我們依序來看這兩種選擇。
撰寫額外的程序代碼
【WebServiceAttribute(Namespace=NamespaceConsts.AYS22Oct2002)】
publicclassVersion2b:WebService
【WebMethodAttribute】
publicstringGetDisplayValue(Personperson)
呼叫原始函式
VersionOnev1=newVersionOne();
returnv1.GetDisplayValue(person);
【WebMethodAttribute】
publicintAdd(inta,intb)
呼叫原始函式
VersionOnev1=newVersionOne();
re
turnv1.Add(a,b);
【WebMethodAttribute】
publicintSubtract(inta,intb)
returna-b;
如您所見,對Add和GetDisplayValue的呼叫會委派原來版本的要求。
自訂的WSDL
目前我能确知的是,不可能找到方法讓ASP.NET自動産生正确的WSDL。這裡是我們接下來的步驟:
1.儲存VersionOne.asmxWeb服務的WSDL。
2.儲存Version2a.asmxWeb服務的WSDL。
3.從WSDL文件中移除服務元素。
4.将版本1命名空間的結構描述(Schema)放進各自的XSD檔案。
5.撰寫并儲存Version2a.asmxWeb服務的WSDL檔案。
6.将文件關閉。
namespace="http:msdn.microsoft.comsamplesAYS20021015"
location="http:localhostAYS15Oct2002MessageTypes.xsd">
這個Web服務的新WSDL是:
xmlns:s1="http:msdn.microsoft.comsamplesAYS20021022"
xmlns:soap="http:schemas.xmlsoap.orgwsdlsoap"
xmlns:s0="http:msdn.microsoft.comsamplesAYS20021015"
targetNamespace="http:msdn.microsoft.comsamplesAYS200210Impl"
xmlns="http:schemas.xmlsoap.orgwsdl">
location="http:localhostAYS15Oct2002VersionOne.WSDL">
location="http:localhostAYS15Oct2002Version2a.WSDL">
location="http:localhostAYS15Oct2002Version2a.asmx">
location="http:localhostAYS15Oct2002Version2a.asmx">
注意到這兩個連接端口的位置是相同的。最後關閉這個文件。要做到這點,必須在web.config中的configurationsystem.webwebServicesprotocols區段加入下面幾行:
那麼這個選擇完成了什麼事情?我們修改了WSDL,讓它準确反映兩個Web服務版本都接受的訊息。建立的新WSDL顯示這個端點了解兩份文件中所定義的訊息。這個基礎Web服務因其編碼方式而能夠接受兩種版本的訊息。
增加新方法不是開發Web服務的唯一方式。接下來,讓我們來看假使您想變更方法簽名碼時,要如何處理。
變更方法簽名碼
在這個例子中,我假設您想要讓現有版本仍然能夠運作,而且您想要改變特定訊息的内容。讓我們來看看改變Add訊息以接受一個整數數組,然後傳回這些整數的總和。這個新的Add訊息和舊的是不兼容的。
我可以賦予這兩個訊息不同的名稱使它們都在同一個端點操作,但這不是我想要做的。這個WSDL需要反映出這個方法是較新且較佳的Add的形式。為了讓這項分别更清楚,并與其它函式一起運作,我建立一個新的Web服務叫做版本2c。我也決定将GetDisplayValue移到新的XML命名空間。這個函式還是呼叫内部的GetDisplayValue方法來利用現有的程序代碼和未來的錯誤修正。
【WebServiceAttribute(Namespace=NamespaceConsts.AYS22Oct2002)】
【WebServiceBinding("Version2",NamespaceConsts.AYS22Oct2002)】
publicclassVersion2c:System.Web.Services.WebService
【WebMethodAttribute】
【SoapDocumentMethodAttribute(Binding="Version2")】
publicstringGetDisplayValue(Personperson)
VersionOnev1=newVersionOne();
returnv1.GetDisplayValue(person);