先前提到 TFVC 與 Git 的抉擇,我個人評估轉向 Git 利大於弊,長遠來看會是較佳的選擇。(我認為 TFVC 跟 WebForm 一樣已是待退老兵,淡出舞台只是時間早晚問題)

決定轉向 Git 的第一個問題是,現有 TFS 上的 TFVC 專案要怎麼搬到 Git 專案?

簡單,從 TFVC Get Latest 取回專案程式碼,全部 Commit + Push 進 Git 專案,搞定收工!

不對啦!! 這樣程式修改歷程就消失了,將來要怎麼查問題?

正確做法是使用 TFS-Git 橋接工具將 TFS 原始碼連同所有變更集、修改歷程都轉成 Git 格式。TFS-Git 轉換工具有幾種選擇:

Git 有許多操作下指令比 GUI 工具簡便快速,強力推薦 cmder,cmder 內建 Git 支援,當切換到 Git 儲存庫資料夾時會顯示分支狀態(如下圖顯示的 (master -> orgin)),偵測有檔案異動等待 Stage 時,分支狀態會變紅,很好用:

使用指令 git-tfs clone http://tfs-server:8080/CollectionName $/{Project},可以將整個 TFS 某個 Collection 某個專案目錄下的程式碼連同 Changeset(變更集) 歷程都轉成 Git 格式,再用下列指令將程式含修改歷程上傳到 TFS:

git remote add origin http://tfs-server:8080/tfs/NewCollectionName/_git/ProjectName
git push -u origin --all

實務上我遇到兩個問題:

  1. TFVC 專案裡有些廢棄多時的系統,程式碼已無保留必要,趁此次搬家想一併移除,不要帶去新家繼續佔空間。
  2. Git-TF 的執行速度不快(正確來說,應該要講「很慢」),當 TFVC 的原始檔案數案龐大,Changeset 次數頻繁,需要的下載時間長到可怕。
    我有個檔案數千個,容量合計 4GB 的老專案(包含大量 PDF、圖片),足足花了三天才下載完,另一個更老檔案更多的專案我沒勇氣試,直接放棄。
    git-tf/git-tfs 的原理是「重播」每一次 Changeset,比較檔案異動,更新儲存庫,因此 Changeset 次數愈頻繁,檔案愈多,耗費時間便以幾何級數上升,我在網路上還看過要花 42 天的案例。

關於不搬廢檔,我試過幾種做法,但效果都不好:

  1. 用 filter-branch 移除不要的目錄
    參考: Git filter-branch 的使用 by Pjack
    filter-branch 做法原理跟 git-TF 類似,也是將所有的 Commit 重跑一次,移除指定檔案再重新 Commit。git-tf 已經很慢了,filter-branch 也不遑多讓... 來人哪,快給我一刀來個痛快吧!
  2. 反過頭用 subtree 分割要保留的目錄,只搬移需要的目錄
    參考:Git 切割方法:subtree by Heresy's Space
    git subtree split 速度比 filter-branch 快多了,在合理範圍內。但我遇到一個狀況,在切割某個資料夾時,不知何故會與該資料夾無關的 Commit 也包進去,切割出來的 Git 儲存庫跟原始儲存庫一樣大,具有全部的檔案及歷程。在 Stackoverflow 有查到網友回報類似狀況,我的 Git 功力仍在幼幼班等級,無力越級打怪,放棄。

最後,我琢磨出的可行做法是這樣 - Git-TF 階段就縮小範圍,只載回特定目錄下的內容(git-tfs clone http://tfs-server:8080/tfs/CollectionName $/{Project}/{folder}/{subFolder})。範圍變小,下載時間會明顯縮短,原本下載整個專案要三天,限定特定目錄時,只需不到十分鐘。

舉例來說,假設 TFVC $/MyProject 有 V1, V2, V3 三個資料夾,我只想搬 V2 跟 V3,所以要跑兩次:

git-tfs clone http://tfs-server:8080/tfs/CollectionName $/MyProject/V2
git-tfs clone http://tfs-server:8080/tfs/CollectionName $/MyProject/V3  

如此會得到 V2, V3 兩個資料夾,是兩個各自獨立的 Git 儲存庫。接著我們可以先建立一個空白的 Git 儲存庫(例如:在 V2, V3 資料夾同一層新增 NewHome 資料夾),再用合併 Subtree 的手法把他們整併到指定的子資料夾(參考),如以下示範:

cd NewHome
git init
echo 這是新建的Repo > README.md
git add .
git commit -m "喬遷之喜"

git fetch ../V2 master
git merge -s ours --no-commit FETCH_HEAD --allow-unrelated-histories
git read-tree --prefix=Ver2 -u FETCH_HEAD 
git commit -m "將TFVC V2匯入到Ver2資料夾"

git fetch ../V3 master
git merge -s ours --no-commit FETCH_HEAD --allow-unrelated-histories
git read-tree --prefix=Ver3 -u FETCH_HEAD &&
git commit -m "將TFVC V3匯入到Ver3資料夾"

以上動作會在 NewHome 資料夾建立一個 Git 儲存庫,並將 TFVC V2, V3 資料夾下的程式庫含修改歷史分別掛在 Ver2、Ver3 資料夾下。依照這套做法,我搬過幾個專案,截至目前還挺順利,沒遇上什麼大問題,分享給大家參考。

Tips for moving specific projects from TFVC collection to Git repository.


Comments

# by eric

我只想搬 V2 跟 V2,所以要跑兩次 <- 到底要跑幾次呢?? 這是黑大親手寫的無誤. 哈哈哈~~

# by Jeffrey

to eric, 打錯字錯到遠近馳名,也算個人特色啦 Orz 謝謝指正

# by JW

請問黑大這句話是已經clone之後要push到git嗎? 筆誤? 還是就是要在一次上傳TFS? 謝謝! 再用下列指令將程式含修改歷程上傳到 TFS: git remote add origin http://tfs-server:8080/tfs/NewCollectionName/_git/ProjectName

# by Jeffrey

to JW, 操作程序是 git-tfs clone 將 TFVC 內容轉成本地 Git Repository,git remote add 設定上傳目的地,git push 將本地 Repository 推上去。

Post a comment