2016 年 02 月 28 日

VCS版本控制圖解指引

以下為我們翻譯的"VCS(Version Control System)的圖解說明"所參考的說明版本控制文章, 這篇文章採用圖解說明,清楚且容易了解,徵得作者的同意,在此將他的文章翻譯為中文, 原文的網址為

http://betterexplained.com/articles/a-visual-guide-to-version-control/

version_control_intro_small

版本控制(又稱Revision Control或Source Control) 讓您隨時追蹤檔案的內容變動. 為什麼你要關心這檔事? 這樣當你搞砸時可以很簡單的回復前一工作版本.
你也許自己DIY了如下的版本控制系統, 卻沒發現這些命名看起來很怪異. 你有類似這樣的檔案嗎? (但願..你沒有完全和下面相同的…..)

  • KalidAzadResumeOct2006.doc
  • KalidAzadResumeMar2007.doc
  • instacalc-logo3.png
  • instacalc-logo4.png
  • logo-old.png

這就是為什麼我們存擋的時候後都會用“另存新檔. 你想要舊的檔案不被新的檔案覆蓋掉. 這是常常發生的問題, 為了避免這種問題, 我們經常會這樣來解決:

  • 將舊檔做個獨立備份拷貝 例如:Document.old.txt.
  • 如果我們夠聰明, 我們加個版本號碼日期: 例如:Document_V1.txt, DocumentMarch2007.txt
  • 我們甚至會用共享目錄, 這樣其他人可以不用e-mail傳檔即可看到或編輯檔案. 希望他們記得加版本編號到新檔案的檔案名稱中再存回去.

所以呢…為什麼我們需要版本控制系統 (VCS)?

運用分享的目錄並遵循共同的版本命名規則, 對於典型的專案或是一次性完成的論文還可以. 可是對於軟體開發的專案呢? 這是不可行的…
你可以想像在分享的檔案夾中的Windows source code像“Windows2007-Latest-UPDATED!!”給大家來編輯嗎? 那麼每位程式設計師只會在各自的子檔案夾工作? 行得通嗎?
因此大型的同時多人參與編輯、快速變更的專案需要一個版本控制系統(Version Control System ), 換個專業說法就是 “檔案資料庫(file database)”, 來追蹤所有的變更以避免混亂.

一個好的版本控制系統(VCS)應該可以滿足以下的需求:

  • 備份與復原. 檔案在編輯時被儲存, 你可以恢復之前每段時間所編輯的版本. 如果你需要回溯到2007年2月23日編輯的檔案…沒問題.
  • 同步. 讓大家分享檔案且隨時可取得最新的版本.
  • 短期的版本回復. 隨意修改檔案或搞砸時? (你就是有可能如此, 不是嗎?). 就把所做的變更扔掉吧, 並回到版本庫(repository)中所知道的"好"版本.
  • 長期的版本回復. 有時我們真的搞得太糟了. 假設你是在一年前做了這個不良的修改,產生了bug. 這系統應能幫你跳到那個版本, 並了解當時做了什麼變動.
  • 追蹤變動. 當更新檔案時, 你可以留下為何變更的紀錄(此變更紀錄要存在版本控制系統,不是在檔案裡). 這樣可以容易的看出這個檔案隨著時間的演進與修改原因.
  • 追蹤負責人. 版本控制系統可以標出每個變更的人名,以示負責.
  • 沙盒(SandBoxing)-建立避免搞砸工作成果的工作區域. 在做大幅的變動嗎? 你可以暫時到獨立的區域做此變動、測試、解決問題, 然後才提交(check in)你的變更.
  • 分支與合併. 更大的沙盒(sandbox). 你可將你的程式碼拷貝一份分支(branch)到獨立的區域做修改, 並獨立追蹤在上面的變更. 過些時候, 再將你的工作成果合併到主要共同開發的區域.
分享的目錄雖然快又簡單, 但無法做到以上所說的這些功能.

學習相關用語

大多數的版本控制系統牽涉以下的觀念, 有可能用詞有點不同.

基本設定

  • 版本庫(Repository or repo): 儲存檔案的資料庫.
  • 伺服器: 版本庫所在的電腦.
  • 局端(Client): 與版本庫連線的電腦.
  • 工作組合或工作備份(Working Set/Working Copy): 你工作電腦上的檔案目錄-你工作編輯變更的地方.
  • 主開發線(Trunk/Main): 在版本庫裡存放程式碼的主要地方. 將程式碼想像為一個族譜(family tree)–"trunk"即為主線.

基本動作

  • 新增: 將一個檔案第一次放進版本庫, 也就是說開始用版本控制來追蹤此檔案
  • 修訂版本(Revision): 一個檔案位於哪個版本 (v1, v2, v3, etc.).
  • 版頭(Head): 存在版本庫的最新版本.
  • 簽出(Check out): 從版本庫下載一個檔案.
  • 簽入/提交(Check in): 如檔案內容變動時,將檔案上傳到版本庫. 此檔案將獲得新的版本號碼, 別人將可簽出(check out)最近的版本.
  • 簽入/提交說明: 說明做了什麼變更的簡短訊息.
  • 變更歷史紀錄: 從一個檔案成立起所經歷的所有變更列表.
  • 更新/同步: 將你工作電腦中的檔案與版本庫的最新變更做同步. 此可讓您擷取到所有最新的檔案.
  • 回復(Revert): 拋棄你工作電腦上的變更並叫出上次從版本庫簽出最新的檔案版本.

進階動作

  • 分支(Branch): 製作分開的檔案/檔案夾備份以便獨立運作(解bug, 測試等等). 分支可說是動詞(分支源始碼)也可說是名詞(在哪一個"分支"?).
  • 差異分析(Diff)/變更/差異部分(Delta): 找出兩的檔案的差異. 可幫忙看出版本間的變動內容.
  • 合併/補釘(Merge/Patch): 將一個檔案的變更合併到另一個檔案, 使其為最新內容. 譬如, 你可以合併一個分支到另一個分支或是主開發線做功能的整合. (在Microsoft, 此稱為 Reverse Integrate and Forward Integrate)
  • 衝突(Conflict): 當一個檔案的變更與其他變更發生衝突時, 兩者無法同時採用各自的變更.
  • 解決(Resolve): 解決變更的衝突問題並簽入(check in)正確版本
  • 上鎖(Locking): 取得此檔案的編輯控制權(Lock), 直到完成編輯後才釋放編輯控制權(Unlock), 其他人才可以編輯.  有的版本控制系統有這樣的功能以避免同一檔案中變更的衝突.
  • 強力解鎖(Breaking the lock): 強迫解鎖某一個檔案以便編輯. 這個功能也許在某人把哪個檔案鎖起來後度假去了(或當Halo 3遊戲發行那天"請病假").
  • 簽出編輯: 簽出(Checking out)一檔案的“可編輯”版本. 有些版本控制系統原始設定簽出後即可開始編輯,有的需要下明確的指令才可開始編輯.
典型的劇情可發展如下:
Alice 新增一個檔案(list.txt)到版本. 她將此檔案簽出,做了些變動(把 “milk”放到列表中),然後將此簽入/提交, 同時寫簽入的說明 (”加入需要的項目.”). 隔天早上, Bob 更新 他電腦的工作備份, 看到最新list.txt的版本, 其中含有“milk”. 他可以瀏覽變更歷史紀錄是做差異比對-看到Alice在前一天加了 “milk” .

 

視覺化案例

這裏從高階的觀念來解釋:大多數的教學指引都給你一堆文字指令. 讓我們以高階觀念來探討,不在指令語法上打轉( Subversion手冊 永遠都在那裡, 不用擔心). 有時去試探到底有什麼可能性總是不錯的.

簽入/提交(Checkins)

最簡單的情境是簽入一個檔案(list.txt)且花時間去修改它.

basic_checkin

每次我們簽入一個檔案的新版, 我們就會得到一個新修訂版(new revision-每個revision的內容含多個檔案at different version) (r1, r2, r3, etc.). 在Subversion你將會做:

svn add list.txt
(modify the file)
svn ci list.txt -m "Changed the list"

此 -m 旗標(flag) 使用來做此簽入的說明.

簽出與編輯

實作上, 你有可能不是一直簽入檔案. 你應該是簽出, 編輯簽入. 這樣的周期看起來如下:

checkout_edit

如果你不喜歡你做的變更, 想重頭來的話, 你可以回復到前一版本再重新來過(或就此停止). 當簽出時, 原始設定讓你取得最近的修訂版本(revision). 如果你想要的話, 你可以指定所要的修訂版(revision). 在Subversion, 執行:

svn co list.txt (get latest version)
...edit file...
svn revert list.txt (throw away changes)

svn co -r2 list.txt (check out particular version)

差異(Diffs)

主開發線(trunk)有檔案變更的歷史紀錄. 差異是你編輯(editing)的變更內容 : 想像你可以 “分離” 他們然後將他們放入一個檔案:basic_diffs

例如, 從 r1 到 r2, 我們加入eggs (+Eggs). 想像分出紅標然後將其放入r1, 以變成r2.
又如要從 r2到 r3, 我們加入 Juice (+Juice). 由 r3 到 r4, 我們去掉 Juice 並加入 Soup (-Juice, +Soup).

大多數的版本控制系統 儲存差異(DIffs)而非 所有檔案的內容. 如此可以省磁碟空間: 4個版本並非指有 4個複製; 我們有1個複製和4個小小的差異. 很簡潔, 是吧?

在SVN, 我們如下做兩個修訂版(revisions)的差異:

svn diff -r3:4 list.txt

差異幫助我們看到變更內容(”你如何再次修正bug?”) 甚至用到一個branch和其他的比較.
加碼問題: r1 到 r4的差異(diff)是?

+Eggs
+Soup

請注意“Juice”甚至完全沒出現 — 若直接從 r1 跳到 r4 根部不需要此變更, 因為 Juice 被Soup蓋掉了.

分支(Branching)

分支讓我們拷貝程式碼到一個獨立的檔案夾隨意修改運用:

first_branch

譬如, 我們可以開一個分支來實驗新想法: 瘋狂的事情像加入Rice和 Eggo waffles. 要看你用的是什麼版本控制系統,有的系統在你開分支時會變動版本的號碼. 好了, 現在我們有一個分支, 我們可以變更我們的程式碼並實驗我們的怪怪想法. (“嗯..waffles? 我不知道老闆會怎麼想. 打賭用Rice應該安全才對) 由於我們是在分開的分支上工作, 我們可以獨立修改或測試, 看看這些修改會不會傷害其他的部份. 且在分支裡的修改都有版本控管的歷史紀錄.

在Subversion,你可以很簡單地將一個目錄做個copy另取名字, 就可以開一個branch了.

svn copy http://path/to/trunk http://path/to/branch

如此分支不是個太艱深的觀念: 假裝你已拷貝你的程式碼到另一個目錄. 你有可能已將學校的專案的程式碼做了分支, 確保你有一個安全的環境嘗試失敗版本以便如果搞砸了可以回復沒問題的版本.

合併(Merging)

分支聽起來很簡單,對吧? 不過, 不見得喔 — 解決如何將一個分支的變更合併到另一個可以說很傷腦筋.

讓我們假設要將 “Rice” 從我們實驗性的分支併入主開發線(mainline). 我們要如何做呢? 做 r6 和 r7的差異分析(diff)然後將差異併到主線?

錯錯錯. 我們只想取用在分支上做的變動. 意思是我們做 r5和r6的diff, 然後將diff送到主開發線 (main trunk):

merging

如果你做r6 和 r7的差異分析, 我們將漏掉 “Bread” (譯者註:因為r6和r7的diff將為-Bread, +Rice, 將這個diff加到r7, r8將少了Bread),而"Bread"應該在主開發線上. 這是精巧之處 — 想像從實驗性branch“分離”的變更(+Rice)並加進去主線. 主線上也許有別的變更, 如此做就沒問題了-我們只是插入Rice功能.

在Subversion, 合併和差異分析非常相近. 請在主線上, 執行以下指令 :

svn merge -r5:6 http://path/to/branch

此在實驗性的分支上所做的指令diffs r5-r6並將此diff加到目前所在. 不幸地, Subversion並沒有簡單的方法來追蹤已做過那些合併,如果你不小心, 有可能會重併同樣的變更. merge tracking為SVN改善計畫中的功能, 目前我們勸你做好變更記錄,提醒自己"r5-r6變動已合併到主開發線了". (譯者註:SVN1.5後已經開始支援merge tracking功能)

衝突(Conflicts)

大部分的版本控制系統可以自動合併一個檔案不同地方的變更. 當有些變更顯然無法併入時衝突就出現了: Joe想要去掉eggs以cheese替代(-eggs, +cheese), 而Sue 想要以hot dog 替代eggs(-eggs, +hot dog).

vcs_conflict

此時競賽產生: 若 Joe 先簽入, 系統可自然接受此變更 (而後Sue就無法做她要的變更了).

當變更的地方相同且有像這樣的衝突時, 版本控制系統將可能呈報衝突訊息(conflict)而不讓你簽入— 此時如果你是Sue, 你可以決定是否要簽入一個解決(resolves)兩難的新版本. 處理方法可為:

  • 重新導入你的變更. 先同步到最近的版本(r4)然後再到此最新版做你要的變更: 到此已經有cheese的列表加入hot dog.
  • 以你的變更去覆蓋別人的變更. 將最新的版本(r4)簽出,將你的版本內容拷貝過去 , 再簽入. 結果, 這將把cheese換成hot dog.

雖然衝突不會常常發生, 但蠻惱人的. 通常我採用先同步到最近版本再將我的變更加進去.

標籤(Tagging)

誰曾想過版本控制系統竟然有Web 2.0的觀念? 很多系統讓你在任一想要的版本貼標籤tag(label)以方便參考. 如此你可以說參考"“Release 1.0″而不用指出在系統內特有的建置號碼(build number):

tagging

在Subversion, 標籤跟分支編輯方法類似,只要你想要做即可執行; 此功能存在乃為了清楚之後所有版本的發展, 所以你可以完全清楚看出在 version 1.0的發佈版本中內容為何. 且且就終止於此. 沒有別的地方可去了. (譯註: 也就是說標籤具有"凍結"的作用, 不會看到不相干的資訊)

(in trunk)
svn copy http://path/to/revision http://path/to/tag

實作的案例: 管理Windows開發程式

我們猜想Windows開發可運用其自己的分享檔案夾來管理它的開發程式, 不過事實並非如此. 那麼, 它是如何做的呢?

  • 有個主開發線, 此處存有穩定建置的Windows.
  • 每一個功能群組(網路, 使用者介面, 多媒體播放器, 等等.) 有他們自己的分支發展他們各自的功能. 相較於主開發線這些分支為正在開發中且較不穩定的發展內容.

你在新功能的分支開發,然後用“Reverse Integrate (RI)” 併回主線. 之後, 你 “Forward Integrate(FI)"且從主線拿到最近的變更併入你的分支:

windows

假設我們在 Media Player 10 和 IE 6. Media Player 團隊在他們的分支做了version 11. 當其完成並通過測試, 產生從10到11的patch, 此被主線採用 (就像 “Rice” 的例子, 不過複雜些 ). 此為reverse integration, 從branch到trunk. IE團隊也可同樣執行.

之後, Media Player 團隊可以取得其他團隊最近更新的程式碼, 如IE的. 如此, Media Player forward integrates 並從主線獲得最近的patches整合回他們的branch. 這有點像之前的例子去拉主線的"Bread"到實驗性的branch, 但, 也是比較複雜.

所以這就是所謂的RI(Reverse Integrate)和FI(Forward Integrate). 哼哼, 這樣的安排可以讓變更先在分支互相協調, 也同時讓新的程式碼不干擾主線的穩定度. 酷吧 ?

事實上, 可以有很多層次的分支和副分支, 配合品管度量來決定你何時要做RI. 不過要知道: 分支幫助您處理複雜度. 現在你知道一個大型的軟體專案基本上是如何組織了吧.

重點提示

我的目標是分享有關版本控管系統的高階想法. 以下為基礎要點: :

  • 使用版本控制. 認真告訴你,這是個好東西,就算你不是寫OS那麼大的東西. 就算單人用也值得.
  • 從小處著手(Take it slow). 我也是現在才開始為我的專案了解如何做分支和合併. 才開始要運用這樣的功能來管理. 如果你的專案還小, 分支和合併可能不是個問題. 維護大專案的人應該對分支和補丁的追蹤很有經驗了.
  • 保持學習. 可以參考很多指引: SVNCVSRCSGitPerforce 或更多你正在使用的系統資料. 重要的是把觀念搞清楚並意識到每個系統有它自己的行話和哲理. 也建議參考Eric Sink的 detailed version control guide.

以上是基礎資料 — 隨著時間我將分享我從做my projects學到的東西. 現在你已了解一般的版本控制系統(VCS),也可以看看 an illustrated guide to distributed version control (DVCS分散式版本控制系統圖解說明).

想要加入Soft & Share的Slack線上討論群組與讀書會嗎加入Soft & Share Slack 

Soft & Share在Facebook有經營兩個粉絲團, 歡迎來加入

喜歡我們的分享嗎? 記得使用以下社群分享按鈕分享給您的社群朋友吧!

 

Join the conversation! 2 Comments

  1. […] — 如果你想快速回憶的話請參考VCS版本控制視覺指引 . 當然, 有些人可能會嘲笑你還在用"古老"的系統. 但在我看來仍然是OK的: […]

    喜歡

    回應
  2. […] VCS版本控制圖解指引 翻出了好幾年前翻譯的一篇文章並重新校稿一遍, 想起了Subversion, CVS, Visual Source Safe, 這些應該都變成古董了吧!, 現在主流是DVCS-Git, 所以小編還再校稿DVCS的圖解指引, 剛剛在校稿這份文件時裡面是引用SVN,雖然有點老舊, 但是這份指引可以讓有些不了解為何要使用版本控制的新手明瞭版本控制系統到底要解決什麼問題? 小編相信還是很多人排斥版本控制系統 […]

    喜歡

    回應

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s

分類

未分類