網頁

2013年1月31日 星期四

Mercurial - 1 分散式版本管理工具 Mercurial 簡介

對軟體開發來說, 版本控管是很重要的一環. 而分散式版本管理, 更可以兼顧控管及彈性. 試用了一下這個軟體, 感覺還不錯, 介紹一下. 我的目標是能夠對了解及使用有實質的幫助, 其他的部份, 就不在此贅述, 但是會把相關的連結給出來.

以下是預計完成的大綱.

1. Mercurial 簡介
2. 一個簡單的操作範例
3. 安裝/設定 (Windows)
4. 安裝/設定 (Linux)
5. 安裝/設定 (Eclipse)
6. 基本操作
7. 進階操作
8. 結論及建議

1. Mercurial 簡介

Mercurial 是一款跨平台的分散式版本管理軟體. 主要由 Python實作, 以及 C 實作的比較工具 diff. 可以用在Windows, Mac OS X, 和大多數 類似 Unix 的平台. Mercurial 的操作, 主要是在命令列下 hg 這個命令完成. hg 是 mercurial (水銀) 的化學符號. 現在也有圖形化介面以及 web 介面.

Mercurial 的建立者及主要開發者是 Matt Mackal, 原始碼授權採用 GNU General Public License v2.

參考連結:

Mercurail 的官方網站
http://mercurial.selenic.com/

詳細的Mercurail 命令說明
http://hgbook.red-bean.com/

Mercurial 的中文維基 :
http://zh.wikipedia.org/wiki/Mercurial

2. 一個簡單的操作範例

先舉一個例子, 示範如何建立 Mercurial 資料庫, 複製資料庫, 從資料庫中取出原始碼, 將原始碼更新回資料庫, 輸出差異檔, 整合差異檔到原始的資料庫, 檢視修改的歷史.

Mercurial 也提供了圖形化的介面, 但是強烈建議先熟悉命令列的操作. 使用任何的版本控制軟體, 都不能保證版本控制運行正常. 參與開發的成員對版本控制的了解是關鍵. 版本的控制, 需要掌握版本的狀態, 命令列的操作有助於熟悉狀況的了解.

雖然 Mercurial 的安裝還沒有說明, 但是以下的範例提供了所有的實例的細節, 對了解Mercurial  的操作, 已經足夠.

* 注意: 這只是一個例子, 甚至可以說不是典型的. Mercurial 提供了很多不同的方式來完成版本控制的工作. 實務上要用並不需要完全都用到, 要採取那些方式及流程, 應該考量專案的特性, 來找出最適合的組合.

以下列出需要的檔案資料

d:\work\Hello                        (範例的工作目錄)
d:\work\Hello\Hello.java     (範例檔案)

Hello.java 的內容如下:
public class Hello {   
    public static void main(String[] args) {      
    System.out.printf("Hello");   
    }
}

* 注意: 雖然我們用了一個 java 程式當例子, 但是並不會去編譯或執行, 把它當做一般的文字檔案就可以了.

1.建立 Mercurial 檔案

現在我們有一個 Hello 資料夾, 資料夾中有一個檔案, Hello.java
在hello 資料夾中, 以命令列執行以下命令:

d:\work\Hello> hg init

執行完後, 資料夾中的的檔案如下

d:\work\Hello                                    (範例的工作目錄)
d:\work\Hello\.hg                              (Mercurial 的檔案目錄)
d:\work\Hello\Hello.java                  (範例檔案)

就可以在 hello 資料夾中, 建立 Mercurial 資料夾 .hg, 這部份儘量不要自己去修改, 這邊也不多作介紹.

注意: hg init 並不會自動加入檔案, 只是建立 Mercurial 所需要的檔案架構, 內容是空的.

接著我們用 tip 命令, 看看現在的狀況是怎樣 :
d:\work\Hello> hg tip   -vp
changeset:   -1:000000000000tag:         tip
user:       
date:        Thu Jan 01 00:00:00 1970 +0000


大概觀察一下, 就可以知道這是一個空空的內容.

2.加入檔案到 Mercurial 中

接下來我們要把原始碼檔案加入 Mercurial
首先使用 status 命令, 觀察目前工作目錄, 然後執行 add 命令, 再來看看 status 的結果有什麼不一樣.

d:\work\Hello> hg status
? Hello.java
d:\work\Hello> hg add Hello.java
d:\work\Hello> hg status
A Hello.java
這邊可以看到 status 命令的結果, 檔案名稱前面的符號已經由 ? 變成 A. 表示這個檔案名稱已經被加入 Mercurail, 但是實際上, 檔案的內容要 commit 之後, 才會進到 Mercurial 的資料庫.

d:\work\hello> hg commit
如果只是下這樣的命令, 會跳出一個文字編輯視窗. 告訴你沒有 commit message

d:\work\hello> hg commit -m "Initial version of Hello"
如果操作成功, 這個命令就不顯示訊息. "沒有消息就是好消息"

再來執行 hg tip   -vp 這個命令
d:\work\hello> hg tip -vp
changeset:   0:c35c3dfa8d27
tag:         tip
user:        Nick Lin <
lxnick@gmail.com>
date:        Mon Feb 04 14:58:48 2013 +0800
files:       Hello.java
description:
Initial version of Hello


diff -r 000000000000 -r c35c3dfa8d27 Hello.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Hello.java Mon Feb 04 14:58:48 2013 +0800
@@ -0,0 +1,7 @@
+public class Hello {   
+ public static void main(String[] args) {      
+  System.out.printf("Hello");   
+ }
+}
+
+

到這邊, 我們已經成功的把檔案加入 Mercurial 的資料庫 (嚴格說, 它不算資料庫, 但是這樣講會比教容易了解, 也比較不抝口). 觀察輸出的內容, 除了原始碼的內容, 編輯者的資訊, 更新的註解, 更新的日期, 和前一個版本的差異等等, 都詳細的列出來了.

注意: 在 commit 的命令之後, hg tip -vp 看到的, 才會是更新過的結果. 在這之前, hg tip  -vp 看到的仍然會是空空的內容. commit 之後, hg status 也不再顯示 Hello.java 這個檔案.

3.複製 Mercurial 工作區

日常作業中, 最常進行的工作, 就是取出目前的版本, 修改後, 更新成新的版本.
以下說明如何從目前的版本, 創建一個複製的版本.以下會以實例, 將 Hello.java 取出, 將輸出改成Hello World!.

d:\work> hg clone Hello HelloWorld
updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved


Mercurial 把 Hello 的內容, 複製了一份到 HelloWorld. 至於是不是和 copy 的效果一樣 ? 我覺得這個問題不需要, 也不必要去探討.

d:\work> dir
2013/02/04  下午 02:47    <DIR>          Hello
2013/02/04  下午 03:13    <DIR>          HelloWorld


進到 HelloWorld 的目錄, 觀察 tip -vp 的輸出, 和 Hello 幾乎完全相同.

d:\work\HelloWorld> hg tip -vp
changeset:   0:c35c3dfa8d27
tag:         tip
user:        Nick Lin <lxnick@gmail.com>
date:        Mon Feb 04 14:58:48 2013 +0800
files:       Hello.java
description:
Initial version of Hello


diff -r 000000000000 -r c35c3dfa8d27 Hello.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Hello.java Mon Feb 04 14:58:48 2013 +0800
@@ -0,0 +1,7 @@
+public class Hello {   
+ public static void main(String[] args) {      
+  System.out.printf("Hello");   
+ }
+}
+
+

4. 修改原始碼

將以下程式
  System.out.printf("Hello");

修改為
  System.out.printf("HelloWorld");

就不多說了, 請用任意的編輯器完成.
 

5. 同步修改結果到資料庫

我們先檢查一下, 修改前後的差異. 請下 hg diff 命令來比較目前工作目錄和資料庫中的內容

d:\work\HelloWorld> hg diff
diff -r c35c3dfa8d27 Hello.java
--- a/Hello.java Mon Feb 04 14:58:48 2013 +0800
+++ b/Hello.java Mon Feb 04 16:45:33 2013 +0800
@@ -1,6 +1,6 @@
 public class Hello {   
  public static void main(String[] args) {      
-  System.out.printf("Hello");   
+  System.out.printf("HelloWorld");   
  }
 }


比較的結果, 除了檔案的資訊, 差異點的部份,  也可以從列表之中看出來,  新的版本刪除了 - 號的這一行, 新增了 + 號的這一行, 也就是對這一行做了修改.

注意: 務必 commit

d:\work\HelloWorld> hg commit -m "Change to HelloWorld"

此時無聲勝有聲.接下來, 我們看看 Mercurial 所記錄的資料. 請下 hg tip -vp

d:\work\HelloWorld> hg tip -vp
changeset:   1:d55cc16933a2
tag:         tip
user:        Nick Lin <
lxnick@gmail.com>
date:        Mon Feb 04 16:59:13 2013 +0800
files:       Hello.java
description:
Change to HelloWorld


diff -r c35c3dfa8d27 -r d55cc16933a2 Hello.java
--- a/Hello.java Mon Feb 04 14:58:48 2013 +0800
+++ b/Hello.java Mon Feb 04 16:59:13 2013 +0800
@@ -1,6 +1,6 @@
 public class Hello {   
  public static void main(String[] args) {      
-  System.out.printf("Hello");   
+  System.out.printf("HelloWorld");   
  }
 }
 

對照之前的輸出結果, changeset 這一欄已經改變, 冒號 (:) 前面的數字已經變成 1, 這是版本號. 冒號 (:) 後面的文數字,是 16 進位的驗證碼, 用來區分不同人, 不同次修改的版本. description 後面的註解, 也已經更新為新的版本. 修正的部份, 也已經以 +/- 號標出來.

 



6. 輸出/合併修改

如果修改過的結果, 我們還需要傳送整個工作目錄, 那我們在這邊介紹這個軟體, 就真的是浪費時間了. 接下來就來說明如何輸出修改的部份.

請執行以下命令
d:\work\HelloWorld> hg export 0:c35c3dfa8d27 1:d55cc16933a2 > 20130204-nick.diff
將 hg export 的輸出結果, 重導到  20130204-nick.diff . 這結果其實是一個文字檔, 開啟後內容如下:
# HG changeset patch
# User Nick Lin <
lxnick@gmail.com>
# Date 1359961128 -28800
# Node ID c35c3dfa8d27802e160d462588793272baa9cc18
# Parent  0000000000000000000000000000000000000000
Initial version of Hello

diff -r 000000000000 -r c35c3dfa8d27 Hello.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Hello.java Mon Feb 04 14:58:48 2013 +0800
@@ -0,0 +1,7 @@
+public class Hello {   
+ public static void main(String[] args) {      
+  System.out.printf("Hello");   
+ }
+}
+
+
# HG changeset patch
# User Nick Lin <
lxnick@gmail.com>
# Date 1359968353 -28800
# Node ID d55cc16933a28296b444136dc6babe29c07fd0cf
# Parent  c35c3dfa8d27802e160d462588793272baa9cc18
Change to HelloWorld

diff -r c35c3dfa8d27 -r d55cc16933a2 Hello.java
--- a/Hello.java Mon Feb 04 14:58:48 2013 +0800
+++ b/Hello.java Mon Feb 04 16:59:13 2013 +0800
@@ -1,6 +1,6 @@
 public class Hello {   
  public static void main(String[] args) {      
-  System.out.printf("Hello");   
+  System.out.printf("HelloWorld");   
  }
 }

 這部份我們就不多做說明, 並沒有太多不同的地方. 除了命令列的參數的意思是從版本 0 到版本 1 的差異部份, 其他沒有需要說明的部份.

接下來, 我們要把修改的部份整合到原始資料庫.
首先, 把 20130204-nick.diff 這個檔案, copy 到 d:\work\Hello, 然後用 import 來合併

d:\work\Hello> hg import --exact 20130204-nick.diff
applying 20130204-nick.diff
0 files updated, 0 files merged, 1 files removed, 0 files unresolved

接下來, 看看合併的結果
d:\work\Hello> hg tip -vp
changeset:   1:d55cc16933a2
tag:         tip
user:        Nick Lin <
lxnick@gmail.com>
date:        Mon Feb 04 16:59:13 2013 +0800
files:       Hello.java
description:
Change to HelloWorld


diff -r c35c3dfa8d27 -r d55cc16933a2 Hello.java
--- a/Hello.java Mon Feb 04 14:58:48 2013 +0800
+++ b/Hello.java Mon Feb 04 16:59:13 2013 +0800
@@ -1,6 +1,6 @@
 public class Hello {   
  public static void main(String[] args) {      
-  System.out.printf("Hello");   
+  System.out.printf("HelloWorld");   
  }
 }


用 hg log 看看這個版本的發展過程, 版本發展的歷程, 可以一目了然的列出來.
d:\work\Hello> hg log
changeset:   1:d55cc16933a2
tag:         tip
user:        Nick Lin <
lxnick@gmail.com>
date:        Mon Feb 04 16:59:13 2013 +0800
summary:     Change to HelloWorld


changeset:   0:c35c3dfa8d27
user:        Nick Lin <
lxnick@gmail.com>
date:        Mon Feb 04 14:58:48 2013 +0800
summary:     Initial version of Hello


將資料庫的變更, 同步到目前的工作目錄
d:\work\Hello>hg update tip
0 files updated, 0 files merged, 0 files removed, 0 files unresolved