[ Topページへ戻る ]

Mercurialでバージョン管理

概要

 「分散リポジトリ方式」なる言葉によって興味をひかれたバージョン管理ツールがこのMercurialです。

今までのバージョン管理ツールへの不満

オフラインでもバージョン管理したい

 いままで、職場や自宅において、CVSやSubversionを使うときは、1台のマシン上にリポジトリを置き、そのリポジトリに対してチェックアウトやコミットといった変更の払い出し・登録を行っています。

 したがって、リポジトリのあるマシンと作業マシンがネットワークで接続できないときは、チェックアウトした作業ディレクトリの変更をコミットできませんし、過去の変更履歴も調べられません。ネットワークに接続できない期間が短時間ならいいのですが、長期間になるとこれはバージョン管理ができないに等しい状態です。

気軽なリポジトリ作成ができたらいい

 ちょっと作ったプログラムや文書などをバージョン管理下に置きたいとします。CVSやSubversionを使う場合は、リポジトリとなるマシン上にディレクトリを決め、importでファイルをリポジトリに新規登録します。リポジトリは、新規作成するか既存のリポジトリにディレクトリ追加するか、以外と悩みます。

 このあと、編集作業を続行するには、一度リポジトリからチェックアウトする必要があります。importしたディレクトリはバージョン管理下にはないからです。今まで作業していたディレクトリを打ち切って、新たなディレクトリにチェックアウトするという中断を余儀なくされます。これはけっこういやなものです。

バージョン管理操作で待たされるのはいやだな

 更新する、コミットする、履歴を調べるといったバージョン管理操作に時間がかかるのはうれしくありません。かといってハイスペックなマシンをバージョン管理サーバに割くのは本末転倒です。自宅PCの場合、ファイルサーバやリポジトリを置くマシンは「お下がり」PCで性能が遅いことがあります。

Mercurialって不満を解消してくれそうな気がする 

リポジトリは作業ディレクトリと同じ場所

 リポジトリは作業ディレクトリと同じディレクトリにあります。作業するマシンと一緒なので、オフラインでもリポジトリと切り離されることはありません。

 それではリポジトリが一元管理できないじゃないか、と思いますが、分散リポジトリ方式のバージョン管理ツールの場合、リポジトリ間で変更内容の伝播(clone/push/pull)ができるようになっています。マスターとして扱うリポジトリがあって、そのリポジトリにあるコンテンツを手元のマシンで編集する場合、まずリポジトリをマスター・リポジトリから手元のマシンへコピー(clone)します。

 コピー(clone)したリポジトリ(+作業ディレクトリ)は手元にあるので、Mercurialではローカルリポジトリと呼ぶことがあるようです。ローカルリポジトリはネットワークに接続していようと切り離されていようと自在に変更を行うことができ、変更履歴はローカルリポジトリに蓄積されていきます。このローカルリポジトリに対するupdate/commit操作は、集中リポジトリ型のCVSやSubversionでのupdate/commit操作と同様です。

 そして、いずれは手元のローカルリポジトリに加えた変更内容を別のリポジトリ(当初clone元のリポジトリであったり、別なリポジトリであったり)に反映させたくなります。このときは、リポジトリ間でpush(ローカル→リモート)、pull(リモート→ローカル)を行います。

 リポジトリ間に不整合があるとpushがエラーとなり、不整合の解消(merge)を行う必要が生じます。このときは、リモートリポジトリの内容をいったんpullでローカルリポジトリへ取り込み、mergeを行ってからpushします。

 他の開発者の変更を取り込むときは、取り込みたい変更を持っているリポジトリからpullします。

リポジトリを作成してもチェックアウトは不要

 最初はバージョン管理なしにファイルをつくって、ある程度したら、バージョン管理を始めたいと思います。そのときは、管理したいディレクトリのトップでリポジトリ作成(init)し、管理したいファイルを追加(add)し、コミットすればOKです。再度チェックアウトせずともバージョン管理下になっています。とっても簡単です。

バージョン管理操作が速い

 更新、コミット、履歴表示などはすべてローカル操作です。集中リポジトリ方式と違ってネットワークアクセスが発生しません。また、Mercurial自体の処理は高速なようです。また、開発作業に使うマシンは大抵ハイスペックなので、ますます処理が速くなります。

 リポジトリ間の変更内容の伝播(push/pull)は時間がかかるかもしれません。ただし、頻度がぐっと少ないのでまあ問題ないでしょう。

Mercurialメモ

バイナリ・ファイルの扱い
Subversionのような効率的なバイナリ・ファイルの差分蓄積はしていないようです。
空ディレクトリは扱えない
ファイルが存在しないと、リポジトリにディレクトリを追加したり空のディレクトリをコピー(clone)してくれない

 

Mercurialの導入・使い方

 Mercurialはよくドキュメントが整備されており、各種プラットフォーム用バイナリも用意されています。

 本家ホームページには、チュートリアルの日本語訳も掲載されています。

 その他Mercurialに関する解説資料(日本語)

WindowsへのMercurialインストール

 Windowsでは、コマンドラインから使用するMercurialパッケージと、ファイル・エクスプローラにアドオンされるTortoiseHGの2種類があります。

Mercurialバイナリパッケージのインストール

Mercurial 本家ホームページから、"BinaryPackages"のリンクをたどって、Windows用の最新バージョンを入手します。バイナリパッケージは以下の3種類が存在します。Mercurialの公式サイトの説明(英語)は、WindowsInstallersを参照。

  1. オフィシャル・インストーラ
  2. 非オフィシャル・インストーラ
  3. "Batteries included"インストーラ

1.と2.はほとんど同じ内容で、必要最低限のコマンドラインツールが入っています。3.は、マージツール(kdiff3)やTCLインタプリター、GUIコミットツールなどが入っています。

ここでは、1.のオフィシャル・インストーラを使ってインストールしていきます。

オフィシャルインストーラ

これを実行すると、インストーラが起動します。インストールディレクトリはどこでもよいです。コマンドラインから利用するので環境変数PATHを通しておくと便利です。インストール時にデフォルトで環境変数PATHを設定するチェックが入っています("Add the installation path to the search path")ので、これを外さなければPATHが設定されます。

マージツールの設定

 MercurialのWindows版オフィシャル・インストーラには、マージツールが含まれていません。また、Windows自体に標準でマージツールはないので、別途入手し設定する必要があります。

 Mercurialの公式サイト上でいくつかのマージツールの説明と設定方法を記述したMergeProgramのページがあります(英語)。このページでは、KDiff3, TortoiseMerge, WinMerge, DiffMergeについて設定記述が載っています。

 Windows版MercurialではKDiff3がマージツールに使用されることが多いようですが、ここではWinMerge(日本語版)を使用してみます。

WinMergeの設定

入手とインストール

日本語版WinMergeをダウンロードし、実行します。日本語版では3wayの比較・マージができるよう機能追加されています。(英語版に付いては未確認)

Mercurial Ver.0.9.5までの設定方法

hgmerge.cmdの作成

Mercurialの実行ディレクトリに、hgmerge.cmdという名前のバッチファイルを作成します。

※バッチファイル内容要確認

@echo off
"C:\Program Files (x86)\WinMerge\WinMergeU.exe" /dl Base /dm Local /dr Other /wl /wr %2 %1 %3
if not errorlevel 0 (exit /b 1) else (exit /b 0)

※MergeProgramのページには、2つのファイルを比較するWinMerge版のhgmerge.cmdバッチファイル例が記載されています。

Mercurial Ver.1.0以降の設定方法

Mercurial.iniファイルに[merge-tools]セクションが追加され、ここで細かな設定を記述できるようになりました。

外部diffツールの設定

 Mercurialでファイルの差分を見ると、標準ではコマンドラインにdiff形式の出力が表示されます。特にWindows上ではコマンド環境は貧弱なので、できれば差分表示はGUIの差分ツールを使用したいところです。

WinMergeを外部diffツールに設定する

Mercurial Ver.0.9.5間での設定

Mercurial設定ファイル(後述)に、外部diffツールとしてWinMergeを使用する設定を記述します。

[extensions]
extdiff =

[extdiff]
cmd.wmdiff = C:\Program Files (x86)\WinMerge\WinMergeU.exe
opts.wmdiff = /r /e /x /ub

cmd.の後に指定した文字列(wmdiff)は、使用者が任意の(hgサブコマンドとかぶらないもの)名前を使うことができます。ここで指定した文字列は、hg wmdiff とhgのサブコマンドのように使います。

cmd.wmdiffには、hg wmdiff で起動する外部diffツールのパスを記述します。
opts.wmdiffには、hg wmdiff で起動する外部ツールに渡すコマンドラインオプションを記述します。

これで、以下のコマンドを実行するとdiffの結果をWinMerge上で見ることが出来ます。

C:\Users\torutk\work\clock> hg wmdiff

[extdiff]項に記述していない外部ツールもコマンドラインで指定して実行することができます。

C:\Users\torutk\work\clock> hg extdiff -p C:\tools\somediff -o "/x /y"
Mercurial Ver.1.0以降の設定

Mercurial設定ファイル(後述)に、外部diffツールとしてWinMergeを使用する設定を記述します。

[extensions]
hgext.extdiff =

[extdiff]
cmd.wmdiff = C:\Program Files (x86)\WinMerge\WinMergeU.exe
opts.wmdiff = /r /e /x /ub

Linux(CentOS 5)へのMercurialインストール

インストール形式

RPMバイナリ版と、pythonのeasy_installモジュールによるPythonモジュールとしてのインストールと2つの方法があります。

RPMパッケージ

CentOS 5.xでは、epelリポジトリにMercurail 0.9.3-1.el5 が用意されています。ちょっとバージョンが古いです。

サードパーティ・リポジトリのRPMForgeには、mercurial の新しいバージョンがあります。が、ここのリポジトリを使用すると、依存するパッケージもどんどんRPMForgeのものが入ってしまいます。(rpm-priorityパッケージを入れて、/etc/yum.repos.d/下のファイルにpriorityを設定する方法で回避はできるようですが未確認)

easy_install

easy_installがインストール済みであれば、以下を実行します。

# easy_install -U mercurial
    :
#

easy_installは、

Mercurial設定ファイルの場所と適用範囲

Mercurialに関する設定は、設定の適用範囲によって異なるパスに設定ファイルを記述し配置します。

リポジトリ毎に設定する

<リポジトリ>/.hg/hgrc

ユーザー毎に設定する

Windows版の場合

Mercurial Ver.0.9.5までの場合

 環境変数HOMEを設定している場合は、%HOME%\Mercurial.iniの名前で設定ファイルを記述します。

 環境変数HOMEを設定していない場合は、%USRPROFILE%で示されるディレクトリの中にMercurial.iniの名前で設定ファイルを記述します。

Mercurial Ver.1.0以降

以下の設定ファイルの内容をすべて読み込みます。

  1. 環境変数HOMEが指すディレクトリにある .hgrc ファイル
  2. 環境変数HOMEが指すディレクトリにある Mercurial.ini ファイル
  3. 環境変数USRPROFILEが指すディレクトリにある Mercurial.ini ファイル

UNIX版の場合

 $HOME/.hgrc

マシン全体で共通に設定する

Windows版の場合

 Mercurialをインストールしたディレクトリ(例:C:\tool\Mercurial)の中に、"Mercurial.ini"の名前で設定ファイルを記述します。

UNIX版の場合

/etc/mercurial/hgrc

Mercurial設定

無視ファイルの設定

設定ファイルに、無視ファイルのパターンを記述したファイルパスを指定します。

リポジトリ固有の無視ファイルの指定

作業ディレクトリのトップディレクトリに、.hgignoreという名前のファイルをおきます。

マシン全体で共通の無視ファイルの指定

Windows版

C:\tool\Mercurial\Mercurial.iniに以下の記述を追加します。(無視ファイルの名前は変えてもかまいません)

[ui]
ignore = C:\tool\Mercurial\hgignore.ini

C:\tool\Mercurial\hgignore.iniファイルを作成します。

syntax: glob
*.class
*~
*.bak

syntaxにはワイルドカード指定(glob)のほか、正規表現指定(regexp)も可能です。混在可。

ディレクトリの無視

 あるディレクトリ以下をそっくり無視するには、ディレクトリ名を記述します。

classesディレクトリ以下を無視したい

syntax: glob
classes

または、

syntax: regexp
^classes

前者だと、ファイル名"classes"やディレクトリ名"classes"が別な場所にあった場合に無視されてしまいます。

後者は、トップディレクトリ下に"classes"がある場合に無視されます。

syntax: regexp
^src/test

<トップディレクトリ>/src/test以下を無視

エディタの設定

 コミット等のリポジトリ更新操作に際してログを記述するときのエディタを指定します。

Windows版

 Windowsでは、デフォルトではメモ帳が立ち上がります。

Meadowの設定

 Mercurial.iniに記述します。gnuclientを導入している場合は、以下の記述となります。

[ui]
editor = C:\Meadow2\bin\gnuclient.exe

※ gnuclientw.exeではなくgnuclient.exeとなっている点に注意

 hg commitコマンドで、Meadowにコミットログ記載用バッファが表示されます。記述後、C-x # を実行すると、コミットの処理が継続します。(ただ保存するだけでは駄目)

外部diffツールの設定

diffの結果を表示するツールを指定します。

[extensions]
extdiff =

[extdiff]
cmd.xxx = <外部diffツールのパス>
opts.xxx = <外部diffツールのコマンドラインオプション>

diffの起動は、hg diff ではなく、[extdiff]のcmd.xxx に記述したxxxと同じ、hg xxx となります。

なお、WinMergeを外部diffに使用する際の設定記述はこちら

Mercurialの基本操作

 まずは、1つのリポジトリのみを扱う操作を見ていきます。

Windows版

リポジトリの新規作成(hg init)

 以下のディレクトリ・ファイルがあったとします。

C:\Users\torutk\work\clock
              +-- classes
              |     +-- model
              |     |     +-- Time.class
              |     +-- view
              |     |     +-- DigitalClockView.class
              |     |     +-- DigitalClockView$1.class
              |     +-- controller
              |           +-- DigitalClockController.class
              +-- src
                    +-- model
                    |     +-- Time.java
                    +-- view
                    |     +-- DigitalClockView.java
                    +-- controller
                          +-- DigitalClockController.java

 バージョン管理を開始するために、リポジトリを新規作成します。コマンドプロンプト(またはCygwin等のコンソール)で、リポジトリとするディレクトリへ移動し、hg initコマンドを実行します。

C:\Users\torutk> cd work\clock
C:\Users\torutk\work\clock> dir /B
classes
src
C:\Users\torutk\work\clock> hg init
C:\Users\torutk\work\clock> dir /B
.hg
classes
src
C:\Users\torutk\work\clock> 

.hgというディレクトリが新たに作成され、その中にはバージョン管理用のファイル・ディレクトリが作られています。

C:\Users\torutk\work\clock> dir /B /S .hg
C:\Users\torutk\work\clock\.hg\00changelog.i
C:\Users\torutk\work\clock\.hg\requires
C:\Users\torutk\work\clock\.hg\store

C:\Users\torutk\work\clock>

リポジトリのステータスを確認(hg status)

ここで、リポジトリと作業ディレクトリの状況をhg statusコマンドで見てみます。

C:\Users\torutk\work\clock> hg status
? src\controller\DigitalClockController.java
? src\model\Time.java
? src\view\DigitalClockView.java
C:\Users\torutk\work\clock>

?のステータスが付いたファイルはバージョン管理下に置かれていないことを示します。リポジトリを初期化した直後はリポジトリの中は空です。なお、無視ファイルの設定で"*.class"を指定しているので、classesディレクトリ以下のクラスファイルはここでは対象外となっています。

リポジトリへのファイルの追加(hg add)

 hg addコマンドでリポジトリへファイルを追加します。コマンドのオプションでファイルを指定しないときは、追加可能なすべてのファイルが追加されます。

C:\Users\torutk\work\clock> hg add
adding src\controller\DigitalClockController.java
adding src\model\Time.java
adding src\view\DigitalClockView.java
C:\Users\torutk\work\clock>

なお、コミットするまではリポジトリには格納されません。

addコマンドで追加指定したファイルは、コミット前であれば hg revert コマンドで追加を取り消すことが出来ます。

C:\Users\torutk\work\clock> hg status
A src\controller\DigitalClockController.java
A src\model\Time.java
A src\view\DigitalClockView.java
C:\Users\torutk\work\clock> hg revert src\model\Time.java

C:\Users\torutk\work\clock> hg status
A src\controller\DigitalClockController.java
? src\model\Time.java
A src\view\DigitalClockView.java
C:\Users\torutk\work\clock> 

ファイルの削除

作業ディレクトリ上からファイルを削除し、リポジトリへ削除要請を行う(hg remove)

作業ディレクトリ上のファイルを削除し、次回コミット時にリポジトリからも削除する指定を行います。

リポジトリへのコミット

 hgg commitコマンドでコミットします。コマンドラインにログを記述するオプションを指定しないとエディタが表示され、そこにログを記述するよう要求されます。

コマンドラインでログを指定してコミット

C:\Users\torutk\work\clock> hg commit -m "Initial"
No username found, using 'torutk@tanqueray' instead

C:\Users\torutk\work\clock>

 ユーザー名を設定していないと、実行しているマシンのログイン名・コンピュータ名が使用されます。ユーザー名の設定は、Mercurialの設定ファイルに、[ui]カテゴリのusername項目で指定します。

コミットの取り消し

 最後の1回だけコミットを取り消すことができます。

C:\Users\torutk\work\clock> hg log
changeset:   3:aaaaaaaaaaaa
tag:         tip
(略)

C:\Users\torutk\work\clock> hg status
M digital\src\jp\gr\java_conf\torutk\clock\view\DigitalClockView.java

C:\Users\torutk\work\clock>

 現在の履歴はchengesetが3 で、ファイルが1つ変更されて未コミット状態です。

C:\Users\torutk\work\clock> hg commit -m "add test main"

C:\Users\torutk\work\clock> hg log
changeset:   4:bbbbbbbbbbbb
tag:         tip
(略)

C:\Users\torutk\work\clock>

 ここでコミットをすると、chengesetが4に更新されます。

C:\Users\torutk\work\clock> hg rollback
rolling back last transaction

C:\Users\torutk\work\clock> hg log
changeset:   3:aaaaaaaaaaaa
tag:         tip
(略)

C:\Users\torutk\work\clock> hg status
M digital\src\jp\gr\java_conf\torutk\clock\view\DigitalClockView.java

C:\Users\torutk\work\clock>

 今のコミットをやめたいので、rollbackを実行します。直前のトランザクションはchangeset 3からchangeset 4へのコミットなので、このコミットが取り消され、リポジトリの最新はchengeset 3に戻ります。

C:\Users\torutk\work\clock> hg rollback
rolling back last transaction

C:\Users\torutk\work\clock>

 この状態でこれ以上rollbackしても何もおきません。

ファイルの修正

 バージョン管理下のファイルを修正すると、リポジトリの内容と変更が発生します。変更有無は、hg statusコマンドで確認できます。変更が発生しているファイルには、Mが付きます。

C:\Users\torutk\work\clock> hg status
M src\view\DigitalClockView.java
C:\Users\torutk\work\clock>

 変更をリポジトリへ反映するために、コミットを実行します。

C:\Users\torutk\work\clock> hg commit -m "ボタンを追加"
C:\Users\torutk\work\clock>

リポジトリの変更履歴の確認

 リポジトリの変更履歴を見るために、hg logコマンドを実行します。

C:\Users\torutk\work\clock> hg log
changeset:   1:7c45ec514678
tag:         tip
user:        torutk@tanqueray
date:        Mon Aug 27 22:02:14 2007 +0900
summary:     ボタンを追加

changeset:   0:c9f8dd1781bb
user:        torutk@tanqueray
date:        Mon Aug 27 21:49:51 2007 +0900
summary:     Initial
               
C:\Users\torutk\work\clock>

 hg -v log と-vオプションを指定すると、変更対象ファイル名も表示されます。

差分の確認

 リポジトリと作業中のファイル、あるいはリポジトリ中の任意のレビジョン間の差分を見るためには、hg diffコマンドを実行します。

C:\Users\torutk\work\clock> hg diff
diff -r 7c45ec514678 src/view/DigitalClockView.java
--- a/src/view/DigitalClockView.java      Mon Aug 27 22:02:14 2007 +0900
+++ b/src/view/DigitalClockView.java      Tue Aug 28 18:20:47 2007 +0900
@@ -115,7 +110,7 @@ public class DigitalClockView extends JP 
     private JLabel currentTimeLabel;
     private JLabel elapsedTimeLabel;
     private JLabel elapsedLabel;
-    private JButton elapsedStartButton;
+    private JButton elapsedButton;
     private Time currentTime;
     private Time elapsedTime;
 }   
C:\Users\torutk\work\clock>

任意のリビジョン間の差分

-rオプションで任意のリビジョン番号を指定します。-rオプションを2つ指定すると、指定したリビジョン間の差分を、-rオプションを1つ指定すると、師弟したリビジョンと最新リビジョンとの間の差分を表示します。

C:\Users\torutk\work\clock> hg diff -r 2 - r5 src\view\DigitalClockView.java
--- a/src/view/DigitalClockView.java      Tue Aug 28 18:36:34 2007 +0900
+++ b/src/view/DigitalClockView.java      Mon Sep 17 22:22:21 2007 +0900
@@ -9,6 +9,10 @@
 import javax.swing.JPanel;
 import jp.gr.java_conf.torutk.clock.model.Time;

+/**
+ * ディジタル時計のビューを司るクラス。
+ * JPanelのサブクラスであり、任意のSwingコンテナに貼って利用できる。
+ */
 public class DigitalClockView extends JPanel {
     public DigitalClockView() {
         GroupLayout layout = new GroupLayout(this);
@@ -98,6 +102,9 @@
         return sb.toString();
     }

+    /**
+     * ビュー単体で実行するためのmainメソッド。
+     */
     public static final void main(final String[] args) {
         JFrame f = new JFrame();
         f.add(new DigitalClockView());

C:\Users\torutk\work\clock>

リポジトリ間のやりとり

リポジトリのコピー

ローカル・ファイルシステム上でのリポジトリのコピー

ローカル・ファイルシステム上において、既存のリポジトリの複製を生成します。hg cloneコマンドを実行します。

C:\Users\torutk\work> hg clone clock C:\Users\torutk\tmp\clock
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
C:\Users\torutk\work>

cloneでコピーした新しいリポジトリには、clone元への参照情報が含まれます。pushやpullコマンドでリポジトリを指定しないときは、このclone元が使われるようです。

レビジョン間のやりとり

前のレビジョンを取り出す

指定したファイルを指定したレビジョンのものに戻す