前言對於程式設計師而言,一款好的版本控制軟體是非常重要的,程式設計師在設計程式時,可能會因為一些效能上的需要,或是實際應用上的需求調整,都會產生許多新增與修改程式碼的動作,進而使得一份程式碼通常會有多種版本。然而若每次修改都沒有留下紀錄,不管是程式碼本身或者是附加些註解說明,當往後要管理程式碼就會越來越困難,可能會不記得某次的改動原因,而使得要再加新的部分或者是修改舊的部分變得寸步難行。這還可能只是單人開發程式時的小狀況,當情況擴展至團體開發時,這問題就更加需要被關注了。許多人在不同的地方,不同的時間點修改程式,若是沒有相關工具的輔助,就會發生沒有人知道其他人到底在做什麼,以及大家同時修改程式碼導致衝突發生,諸如此類的問題就會接踵而來。
版本控管就在這樣的需求下被廣泛的使用,像是常聽到的Subversion, GitHub, 或是本篇將提到的Team Foundation Server(以下稱TFS),都是常被拿來使用的版控工具,並且因為TFS可直接與開發工具Microsoft Visual Studio作結合,讓程式設計師在想要修改程式碼時,直接藉由Microsoft Visual Studio透過TFS功能幫你做簽出的動作,並在完成程式的同時,將程式碼簽入,省下了許多開發的時間,同時也避免了許多前述所提到的問題。可以說如何好好操作這類的工具跟程式碼品質息息相關。

《圖一》Team Foundation 的物件模型
但對於程式設計師而言,問題還沒有真正結束,隨著使用者不斷產生新的想法,需求也跟著不斷冒出,換來的就是專案的持續膨脹,專案資料夾越來越多,越分越細,底下跟隨著的程式碼也無止盡的增加,管理上也越來越需要注意,一個不小心做了錯誤的操控,引發的問題或許不是三言兩語能夠道盡的,縱使TFS本身提供了這麼方便的版控平台,但到頭來要好好管理程式碼,還是需要投入許多人為操作的時間,若要在算上檢查的部分,這時間需求就更加龐大了。
為此,其實微軟不單單只是提供了TFS這個工具本身,還包含了TFS的API,程式設計師能夠藉由程式碼來完成各種TFS上的操作,進而避免掉過版時可能會發生的人為疏失,同時也能夠因此節省掉許多時間,以下就將介紹該如何使用TFS 中版本控制功能的API以及簡單的應用。
專案的初始設定專案的起頭都是從設定開始,我們想要使用別人所提供的API,當然也要先有其相關的DLL檔案,而這部分其實在安裝Microsoft Visual Studio時就一併放到你的電腦裡了,因此需要做的事情就簡單許多,只要加入該參考就等於完成了基本的設定,以下的教學都以C#語言為範例。
需要的參考為Microsoft.TeamFoundation.Client與Microsoft.TeamFoundation.VersionControl兩個,當然為了之後使用上的方便,也一併將命名空間設定好。

《圖二》加入Team Foundation Server相關參考
到這裡,這就可以開始使用相關的功能了,接下來的重點會放於平常比較會使用到的「簽出」、「分支」、「合併」等功能上,該功能屬於Microsoft.TeamFoundation.VersionControl的部分,而Microsoft.TeamFoundation.Client則是處理連接TFS的部分。
首先是連接到TFS傳統我們透過Microsoft Visual Studio與TFS做連接,可直接從上方工具列中的「小組」→「連接到TFS」→「輸入Server的網址並選取要做連結的專案」(文字描述可能會依照版本的不同也有所差異),這裡就不詳細介紹連結的詳細操作了。

《圖三》連接到Team Foundation Server
而我們想透過程式碼來操作TFS上的各種行動,當然也必須要在程式中先進行連接TFS 的動作,所使用的物件為Microsoft.TeamFoundation.Client.TfsTeamProjectCollection,如同我們平常連接TFS需要做登入驗證,透過程式當然也需要相同的處理,但通常來說TFS會直接從Windows認證中來取得資料,所以我們平常連接TFS通常是不需輸入任何帳號密碼的。這邊藉由程式來登入同樣可以採取相同的動作,直接藉由Windows認證來做連接,程式碼如下所示。

《圖四》
我們需要宣告一個TfsTeamProjectCollection(Uri, ICredentials)物件,該物件是用來擷取TFS系統的通用基礎結構,兩個參數分別為Team Foundation Server的網址以及驗證認證的資訊,上面利用了CredentialCache.DefaultCredentials來取得預設的認證,若你平常連接TFS時就不需額外輸入認證資料的話,即可採用此方法。當然也有提供完整認證資料的方法,如下方所示。

《圖五》
上面的例子可以看到改成輸入認證帳號與密碼的方式來做連接,但必須要注意的是,若是提供的認證不存在,導致認證失敗的話,程式執行上並不會馬上發生錯誤,因為這裡依然會產生一個連結TFS的物件,但是它其實還沒實際與TFS做連接,在往後要利用此物件去執行TFS上各種行動時,才會發生錯誤,這麼可能要注意一下。
若想要避免前面提到的問題,可於產生TfsTeamProjectCollection物件後,嘗試實際連結至TFS看看,所使用的方法為Authenticate(),該方法會去嘗試取得授權呼叫執行 Team Foundation Server之伺服器的識別,若失敗的話則會拋出exception,這麼一來就可以事先得知是否會有認證問題了。

《圖六》Authenticate拋出的exception
在確認可以連結TFS後,最後就是取得Server上面的版控資料,這邊需要利用另一個物件VersionControlServer (Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer),該物件用來表示表示版本控制儲存機制,取得的方式為使用TfsTeamProjectCollection其中的方法GetService
(),程式碼如下所展示。

《圖七》
到這邊連接TFS可以說是告一段落了,再來就是取得Server上的物件並執行各項操作。
開始版控動作-簽出
對於程式設計員來說,修改程式可以說是最基本的動作,當我們想要修改TFS上的程式時,如同一開始提到的,為了避免衝突發生,我們都必須做簽出的動作,以確保這隻程式只能同時有一個人在做修改,雖然TFS通常會設定當你修改了程式就會自動作簽出,但有時也會發生說你希望在修改前就簽出程式,主要也是因為當檔案在沒簽出狀態時是屬於唯獨的,所以不簽出檔案的話無法在Microsoft Visual Studio以外的地方做修改。
因此當我們打算將一份新的程式碼覆蓋到某一版控系統中時,像是你的專案為了控管發開環境與正式環境的差異,因此會有類似的兩個專案,或者客戶方也有儲存一份我們開發的程式碼,我們要提供一批新換版的內容時,若不將先檔案簽出就無法執行覆蓋的動作。這時問題就來了,因為有大量的程式碼等著版控人員來一個個做簽出,不僅耗時而且還容易出錯。
若我們要利用程式來做簽出的動作,首先在前面我們已經連結上TFS了,再來就必須取得TFS上的物件,通常我們本機都會有一份專案與Server上的做對應,因此這邊我們想取得Server上的資料,也是藉由本機的對應資料來找,這代表的是我們不需要記得Server該檔案的路徑,只需要提供本機所儲存的路徑即可。
首先需先取得該工作區,取得的方式為VersionControlServer的GetWorkspace(String)方法,傳入的參數為本機檔案的絕對路徑,使用方式如下面所示。

《圖八》
針對這個Workspace物件(Microsoft.TeamFoundation.VersionControl.Client.Workspace),就可以看到平常使用的各種版本控管指令,像是簽入、簽出、分支、合併、新增、刪除等等,以下將不會對於所有操作都做說明,若有需要可於以下網址查看相關指令與用法。
http://msdn.microsoft.com/zh-tw/library/microsoft.teamfoundation.versioncontrol.client.workspace.aspx

《圖九》Workspace類別
回到正題若要執行簽出的動作,即是使用Workspace中的PendEdit(String)方法,後面的參數是Server上要簽出的檔案的路徑,大概會是一個”$/FEP10/Development/Source/AA/……”的內容,但前面有提到,有時程式設計師不會去在乎Server上檔案的路徑,只記得自己本機上的,所以這邊可以採用另一種做法,也就是藉由本機的檔案來照到對應的內容。
這邊將利用ItemSpec(Microsoft.TeamFoundation.VersionControl.Client.ItemSpec)與ItemSet(Microsoft.TeamFoundation.VersionControl.Client.ItemSet)來取得,前者可表示本機或者是版控伺服器上的檔案,在這邊的範例是本機上的,而後者就是儲存利用本機內容去搜尋版控伺服器上的結果。使用範例如下所示。

《圖十》
在上面的例子中,我們將會取得"C:\\Source\\AA\\AABase\\"路徑下所有檔案其對應的伺服器檔案,同時就可以查看ItemSet中的Item物件了,前面說到做簽出動作需要知道該檔案的伺服器路徑,因此最後的簽出動作程式碼就如以下所示。

《圖十一》
當然,你可以用一個迴圈來簽出所有的檔案,並且檢查簽出是否程式,PendEdit方法會回傳一數值,以0表示簽出失敗。

《圖十二》
其餘版控處理
除了簽出外,另一個可能也常需要大量做的動作為分支與合併,這通常是屬於版控人員要處理的,程式設計師可能較少會需要使用到。對於一個專案,為了區分開發環境與正式環境的差異,我們通常會有複數個專案資料夾來儲存,當想要將正式環境的內容做更新時,就是把開發環境的程式碼,移至正式環境的資料夾內,通常這個動作就是利用分支與合併來完成。
對於版控人員來說,這通常是件苦差事,他必須將大家完成的程式一個個合併(或分支)到另一個資料夾下,除了要避免點選到錯誤的檔案或是遺漏掉什麼,同時還要注意變更集的正確性與否,因此這邊也就來簡單說明該如何利用程式碼來完成這件事情。
前置動作如同簽出檔案一樣,我們需要取得伺服器上的檔案路徑,而且是來源與目標兩個,也就是你打算將程式碼從哪個來源資料夾合併至哪個目標資料夾,除此之外還需要給定變更集代號。變更集我們將使用VersionSpec這個物件,通常我們可以直接使用VersionSpec.Latest來代表最新的變更集,而當我們要指定特定的變更集號時,就須使用以下所示的方式,即可指定變更集代號為55056。

《圖十三》
分支的方法為PendBranch(String, String, VersionSpec),第一個參數為來源路徑,第二個參數為目標路徑,最後的即是變更集號。路徑皆是使用伺服器上的,可參考前面簽出檔案的處理方式來取得。

《圖十四》
第一行的作用是復原暫止的變更,避免重複分支會發生失敗,以上操作程式碼中利用取代資料夾名稱的方式來得到目標的完整路徑,但實際該怎麼操作則需視不同的專案設定來調整。另外同樣可以判斷回傳值若為0則代表分支失敗。
最後一個操作就是合併,合併的方法為Merge(String, String, VersionSpec, VersionSpec),前兩個參數如同分支一樣,分別為來源與目標,後面則分為別起始變更集與結束變更集,若給null的話則會自動抓取該檔案的第一個變更與最後一個變更,說到這裡應該也很容易想像的到該如何做使用了。

《圖十五》
這裡可以發現Merge方法的回傳值不太一樣,為一個GetStatus物件,我們可以使用NoActionNeeded方法來確認是否有錯誤發生。
總結
版控工具與程式設計師的關係可以說是密不可分,但有時版控需要大量繁瑣的操作,使得版控這個動作需要非常多的細心與耐心,但縱使如此還是然容易出錯,同時程式設計師也總是希望可以藉由寫程式來讓之後的程式開發更加順利,所以這邊藉由TFS的API,我們可以用更輕鬆的方式來執行各種版控操作,當然這篇文章中只先介紹了較常見的幾項功能,若對版控有興趣或者是同樣認為版控是件麻煩事的版控人員,希望這篇文章能夠對於你有所幫助。
參考資料
1.管理 Team Foundation Server
2.Team Foundation Service的官方網頁
3.從主控台應用程式連接到 Team Foundation Server