歡迎您光臨本站 註冊首頁

Mercurial - 分散式版本控制系統

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  
是一個輕量級的分散式版本控制系統,它以方便的控制、極強的擴展性贏得了眾多開放源代碼項目的青睞。本文從版本控制系統中的基本概念、操作和擴展性等方面,有側重的介紹了 Mercurial。此外本文列出了幾個其他常用的分散式版本控制系統,並和 Mercurial 作了簡單的比較。通過閱讀本文,讀者可以了解基礎的Mercurial操作,進而熟悉這個備受青睞的工具。

Mercurial 簡介

Mercurial 是一種輕量級分散式版本控制系統,採用 Python 語言實現,易於學習和使用,擴展性強。其是基於 GNU General Public License (GPL) 授權的開源項目。相對於傳統的版本控制,具有如下優點:

  • 更輕鬆的管理。傳統的版本控制系統使用集中式的 repository,一些和 repository相關的管理就只能由管理員一個人進行。由於採用了分散式的模型,Mercurial 中就沒有這樣的困擾,每個用戶管理自己的 repository,管理員只需協調同步這些repository。
  • 更健壯的系統。分散式系統比集中式的單伺服器系統更健壯,單伺服器系統一旦伺服器出現問題整個系統就不能運行了,分散式系統通常不會因為一兩個節點而受到影響。
  • 對網路的依賴性更低。由於同步可以放在任意時刻進行,Mercurial 甚至可以離線進行管理,只需在有網路連接時同步。




從 repository 開始

版本控制系統中的 repository 就像一個倉庫一樣,用來存儲被管理的數據文件,包含數據文件的不同版本。傳統的版本控制系統中,這樣的repository 是集中式的。除了這樣一個集中式的 repository 之外,每個用戶會有一份自己的工作版本拷貝。用戶通過命令同步自己的拷貝和集中式的repository。

分散式版本控制系統中的 repository 則採用的是對等網路式的方式。傳統集中式的管理中,只有一份 repository,其他的只是工作拷貝,不包含額外的版本。分散式的管理當中,每個用戶所持有的都是一個真實的 repository,當中存儲有不同的版本信息和維護一個 repository 的必要的輔助元數據。這樣一個對等工作模式當中,用戶通過交換下文即將提到的 changeset 來完成同步。

這樣做的一些優點在於,工作的并行度將大大的提高。每個用戶都可以帶著這樣的repository,從這裡他可以把當前的工作拷貝切換到 repository 裡面存儲的任何一個版本。這個版本可以是之前正在工作的版本,現在需要合併進一些別人的意見,也可以是用戶私有的一個版本,當前正在做很多前瞻性的工作,還沒有能同步給其他用戶使用。也同樣是因為這樣的模式,每個用戶可以任意把自己的 repository 當中的一個版本交換給其他用戶,而不需要對自己手頭正在工作的版本進行回退。下圖是這樣一個靈活的工作模式的演示。


圖 1. 工作模式的演示





Mercurial 里的元素

Revision

在使用 Mercurial 的系統中每個改動隔離在各自的 repository 里,既避免把不相關的代碼混雜起來, 又便於一個接一個的測試每一部分工作,用戶做的每個改動稱為一個 revision。一般會有一個所有用戶都可以訪問得到的 repository 保存了項目的“主要”版本,工作repository 是用戶自己做事情的地方,實現新的特性,修改漏洞,重構,實驗等,當完成改變后,你可以 push 到共用的 repositor y中,即完成了一個 revision。

Changeset

一個或多個文件的改變集合在一起形成一個邏輯單元,稱為 changeset。每一個 changeset由兩部分內容描述,版本號和 changeset 標識,例如:

	changeset:   207:58e4906e69e3   

冒號前面的數字代表版本號,它用來標識本地 changeset。這個版本號只有在用戶的本地repository 中才有意義。冒號後面的那個很長的十六進位串是 changeset標識, 它是確定changeset的全局唯一標識符, 在所有包含這個 changese 的 repository 中都相同。多個用戶之間討論changeset,一般使用這個 changeset 標識,而不是上面說的版本號,因為完全有可能每個用戶的 repository 中同樣的 changeset 版本號不同。

Head

Head 表示 repository 中每個分支最新的 revision,通常在合併幾個分支時會用到這個概念。

Tip

Tip 是最新的一個 changeset 的版本號的一個別名。在命令中任何使用版本號的地方都可以使用 tip 來代替最新的 changeset的版本號。Tip在各個repository中是不同的,同時一個repository 中只有一個 tip。

Log

Log 命令按時間順序從近到遠的記錄著在 repository 中發生的每一次事件。可以通過指定-v診斷輸出選項來獲得更多更詳細的歷史信息,或者指定—debug選項來獲得歷史信息中的一切細節。





動手操作起來

以下是一些實際使用 Mercurial 中常用的例子。Mercurial 的原意是元素周期表當中的汞元素,但是 Mercurial 這樣的單詞顯然不太適合日常使用。事實上 Mercurial 的命令取了元素周期表當中汞元素的化學符號:hg,所有的 Mercurial 命令都以 hg 開始。

    $ hg command [options]  

其中 command 是 Mercurial 的命令。每個命令的具體的命令行選項可以使用:

    $ hg help command  

來獲得。

克隆一個 repository

我們使用 clone 命令克隆一個 repository,它生成一個完整的 repository 複本,這樣我們就有一個本地私有的 repository 來工作。

例如:

     $ hg clone  http://foo.com/hg/project1  

如果所有都沒問題,clone 命令輸出:

    destination directory: project1      requesting all changes  	adding changesets  	adding manifests  	adding file changes  	added 127 changesets with 448 changes to 143 files  	139 files updated, 0 files merged, 0 files removed, 0 files 	unresolved  

此時,我們應該在當前目錄下發現一個目錄叫 project1,目錄下的文件是我們剛克隆的 repository 的精確複本。在 Mercurial 中,每一個 repository 是自包含的。當你克隆一個repository 后,新 repository 變成克隆時它的精確複本,但是後續的兩個 repository 當中任一方改變都不會在對方顯示,除非用戶使用 pull 或 push 命令明確地傳遞改變,這個將在後面介紹。

另外,每個用戶可以使用 init 命令將本地的一個目錄初始化為一個 Mercurial 的 repository,只需要在那個目錄下運行:

   $ hg init  

如果設置好了同步共享的發布方式,其他用戶就可以來克隆該用戶的 repository 了。

發布你的改動

進入工作目錄,使用我們喜歡的編輯軟體修改,例如我們要修改 main.py 讓它增加列印一行輸出:

def main():      print "I'm in the a function."      print "Great joy of using Mercurial!"  #新加的一行    if __name__ == "__main__":      sys.exit(main())  

完成之後退出編輯器,任務完成。有了剛才的修改我們就可以創建一個changeset。

在創建 changeset 之前如果想確認一下哪些文件被改動了,可以使用 status 命令。

$ hg status  M main.py  

使用 diff 命令可以檢查實際文件內容的改變:

diff -r a58db6f0e482 main.py  --- a/main.py   Thu Nov 29 13:38:38 2007 +0800  +++ b/main.py   Thu Nov 29 13:46:10 2007 +0800  @@ -1,5 +1,6 @@ def main():   def main():       print "I'm in the a function."  +    print "Great joy of using Mercurial!" #新加的一行     if __name__ == "__main__":       sys.exit(main())  

diff 命令的默認輸出是通用的補丁格式,易於在各種系統之間交換和討論。

創建一個 changeset 后我們就可以用 commit 命令提交了。

$ hg commit  

這個命令把我們帶到一個編輯器內,預設的編輯器是 vi,同時給我們展示了幾行如下的文字:

HG: user: Guolian Yun <yunguol@cn.ibm.com>  HG: changed main.py  

第一行是空的,接下來的幾行表明哪些文件將進入本 changeset。為了改變 changeset,我們必須描述它的原因,這通常稱為 changeset註釋。讓我們輸入一些:

I’m using Mercurial!  HG: user: Guolian Yun <yunguol@cn.ibm.com>  HG: changed main.py  

接著,保存並退出編輯器,如果一切正常,commit 命令將沒有任何提示地輸出。

讓我們看看status命令現在告訴我們什麼?

$ hg status  

什麼也沒有!我們的改動已經提交到changeset里了,那裡沒有修改的文件需要提交的。Repository 中內容和當前工作目錄的內容一致了。

現在可以檢查以下最新的改動是不是包含剛才所添加的 changeset 註釋,使用 tip 命令就可以顯示了:

$ hg tip:  changeset:   2:2874393e3d9c  tag:         tip  user:        Guolian Yun <yunguol@cn.ibm.com>  date:        Thu Nov 29 10:10:39 2007 +0800  summary:     I'm using Mercurial!  

目前新的 changeset 只存在本地 repository 中,如果想和其它 repository 分享改動,我們需要使用 push。

$ hg push project2  

project2 為你想要 push 的目標 repository 的名字。

引入他人的改動

想要得到所有在別的 repository中而在本地repository中沒有的改動,可以採用 pull命令。

$ hg pull project3  

project3為我們想要得到更新的目標repository的名字。

在 Pull 后,預設情況下Mercurial不更新工作目錄。這意味著雖然repository現在有changeset,但在工作目錄中的 main.py 文件仍然是 pull 之前老的內容。

如果只想從hg clone的 repository 中更新當前 revision 到最新版,可以直接採用:

$ hg pull -u  

Repository 之間的同步

上文當中的 push 和 pull 的操作,是處於不同位置的 repository 之間的同步。之前給出的兩個例子是本地目錄 repository 之間的同步。Mercurial 還支持以下形式的 repository 之間的同步:

file://local/filesystem/path  http://[user@]host[:port]/[path]  https://[user@]host[:port]/[path]  ssh://[user@]host[:port]/[path]  

其中 file 協議和本地目錄相同。在 http 和 https 協議上使用push命令,需要在遠端的伺服器上啟用相關的屬性。ssh協議是眾多系統中支持的shell。

標準的 Mercurial 發行包中還附帶一個 Python CGI 腳本 hgweb.cgi, 可以用來參考搭建一個多用戶可以集中式的同步改動的界面,如下圖所示:


圖 2. 界面





擴展 Mercurial

Mercurial 系統中提供一種擴展機制來添加新的命令。通過擴展添加的命令可以在現有的Mercurial 系統的基礎上添加新的功能,這些命令跟隨 hg 后被調用時就像原生的命令一樣。本文介紹兩個常用的Mercurial擴展:Patchbomb和Mq。

Patchbomb

Patchbomb是一個在Mercurial系統中利用發送郵件的方式來交換changeset的擴展。Patchbomb添加了一個新的email命令。通過調用 hg email 命令,changeset 提交時的信息的第一行將作為郵件的主題,信件的正文包括完整的 changese t提交信息,以 patch 的形式發布出來的 changeset 完整補丁。如果一次發送的是多個changeset,那麼Patchbomb會提示輸入本次 changeset 集的總提示信息,這部分信息將作為第一封信,信件主題以[PATCH 0 of N]開頭,changeset 則會以[PATCH i of N]將的主題開頭髮出,其中i是 changeset在本地 repository 當中的順序。多changeset 系列郵件中,每封信會在郵件頭中包含合適的回複信息,這樣在郵件客戶端可以清晰的顯示出系列 changeset 之間的層次關係。


圖 3. 層次關係

Patchbomb 支持使用本地系統中的 sendmail 程序來發送郵件,同時支持使用 SMTP 郵件伺服器。用戶如果長期固定為某個項目工作,還可以將郵件的收件地址和發信地址提供給Patchbomb,免去每次手動輸入的麻煩。這一切都可以在 Mercurial 統一的 .hgrc 當中設置,以下是一個完整的例子。

[extensions]  hgext.patchbomb =  [email]  method = smtp # 還可以在這裡指定/usr/sbin/sendmail  from = Zhengang Li <lizg@cn.ibm.com>  to = groupmail@foo.com  [smtp]  host = smtp.foo.com  

Patchbomb 作者為Bryan O’Sullivan,該擴展現在隨同 Mercurial 系統一起發布,用戶不需額外下載安裝,只需如上例中一樣啟用即可。

Mq

Mq 擴展的全名是Mercurial Queues,顧名思義該擴展將用戶本地的多個 changeset 排列到隊列中。原先分散式版本控制系統中,changeset 一旦提交並不能修改。有了 Mq 擴展之後,用戶可以將本地的任意數量的 changeset 存放至一個本地的隊列當中,對這些 changeset 用戶除了可以使用傳統的 changeset 上的任何命令之外,還可以修改changeset,包括提交信息和版本補丁的改動。

啟用 Mq 擴展的辦法同其他擴展一樣,在 .hgrc 當中添加如下信息:

[extensions]  hgext.mq=  

Mq的命令是一系列以字母q打頭的命令,qinit, qnew, qrefresh, qdiff, qpop和qpush等。Qinit 用來初始化用來存放補丁隊列的目錄,qnew創建一個新的補丁changeset,qrefresh 將改動刷新到當前的補丁當中去,qdiff 將當前的補丁列印到屏幕,qpop 和 qpush 用來移動當前存放在隊列頂部的補丁。完整的 q 系列命令可以從 hg help給出的列表中獲得。

Mq中所有的 changeset 補丁存放在項目頂層目錄的.hg/patches下面,用戶可以手動修改這些補丁當中的提交信息。Changeset 補丁的順序存放在.hg/patches/series文件當中,同樣的,用戶可以修改這些補丁的順序。

Mq的作者是Chris Mason,該擴展現在隨同 Mercurial 系統一起發布,用戶不需額外下載安裝。

如果現有的擴展不能滿足用戶的要求,編寫自己的擴展也不困難。Mercurial 使用 Python編寫,編寫一個新的擴展相當於在 Mercurial 系統的 hgext 包當中編寫一個新的模塊。具體的擴展實現還有些約定的規則,用戶可以參考 Mercurial 所提供的文檔。





其他分散式版本控制系統

分散式版本控制系統領域還有一些其他的系統,如GNU arch,monotone,Bazaar,git,darcs。

各類系統在各在一定的領域內長處,如GNU arch在GNU Savannah主機上應用,Bazaar 主要用於 Ubuntu Linux 系統的開發當中,git 源於Linux kernel 的開發,現在在多處和內核相關的項目中使用。他們大多數提供友好的Web界面和多種版本同步協議的支持。Git 和Gnu arch 由 C 和 shell 腳本語言編寫實現,monotone 由C++語言實現,darcs 由 Haskell語言實現,Bazaar 和本文介紹的 Mercurial 由 Python 語言實現。從開放和擴展性方面來說,類似 Python 這樣的腳本語言的更易於用戶編寫自己的擴展。

在眾多的分散式版本控制系統中,Mercurial 是最年輕的,它的第一個版本發佈於2005年4月。Mercurial 吸收了眾多前輩的特性,被眾多的項目採用。





結束語

Mercurial 基本的概念和操作熟悉之後,讀者可以進一步閱讀參考資料當中的文獻,進一步了解Mercurial 的系統維護和實現細節。(責任編輯:A6)



[火星人 ] Mercurial - 分散式版本控制系統已經有927次圍觀

http://coctec.com/docs/linux/show-post-69062.html