[ Topページへ戻る ]
Mercurialでバージョン管理
「分散リポジトリ方式」なる言葉によって興味をひかれたバージョン管理ツールがこのMercurialです。
オフラインでもバージョン管理したい
いままで、職場や自宅において、CVSやSubversionを使うときは、1台のマシン上にリポジトリを置き、そのリポジトリに対してチェックアウトやコミットといった変更の払い出し・登録を行っています。
したがって、リポジトリのあるマシンと作業マシンがネットワークで接続できないときは、チェックアウトした作業ディレクトリの変更をコミットできませんし、過去の変更履歴も調べられません。ネットワークに接続できない期間が短時間ならいいのですが、長期間になるとこれはバージョン管理ができないに等しい状態です。
気軽なリポジトリ作成ができたらいい
ちょっと作ったプログラムや文書などをバージョン管理下に置きたいとします。CVSやSubversionを使う場合は、リポジトリとなるマシン上にディレクトリを決め、importでファイルをリポジトリに新規登録します。リポジトリは、新規作成するか既存のリポジトリにディレクトリ追加するか、以外と悩みます。
このあと、編集作業を続行するには、一度リポジトリからチェックアウトする必要があります。importしたディレクトリはバージョン管理下にはないからです。今まで作業していたディレクトリを打ち切って、新たなディレクトリにチェックアウトするという中断を余儀なくされます。これはけっこういやなものです。
バージョン管理操作で待たされるのはいやだな
更新する、コミットする、履歴を調べるといったバージョン管理操作に時間がかかるのはうれしくありません。かといってハイスペックなマシンをバージョン管理サーバに割くのは本末転倒です。自宅PCの場合、ファイルサーバやリポジトリを置くマシンは「お下がり」PCで性能が遅いことがあります。
リポジトリは作業ディレクトリと同じ場所
リポジトリは作業ディレクトリと同じディレクトリにあります。作業するマシンと一緒なので、オフラインでもリポジトリと切り離されることはありません。
それではリポジトリが一元管理できないじゃないか、と思いますが、分散リポジトリ方式のバージョン管理ツールの場合、リポジトリ間で変更内容の伝播(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はよくドキュメントが整備されており、各種プラットフォーム用バイナリも用意されています。
本家ホームページには、チュートリアルの日本語訳も掲載されています。
その他Mercurialに関する解説資料(日本語)
Windowsでは、コマンドラインから使用するMercurialパッケージと、ファイル・エクスプローラにアドオンされるTortoiseHGの2種類があります。
Mercurial 本家ホームページから、"BinaryPackages"のリンクをたどって、Windows用の最新バージョンを入手します。バイナリパッケージは以下の3種類が存在します。Mercurialの公式サイトの説明(英語)は、WindowsInstallersを参照。
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をダウンロードし、実行します。日本語版では3wayの比較・マージができるよう機能追加されています。(英語版に付いては未確認)
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.iniファイルに[merge-tools]セクションが追加され、ここで細かな設定を記述できるようになりました。
Mercurialでファイルの差分を見ると、標準ではコマンドラインにdiff形式の出力が表示されます。特にWindows上ではコマンド環境は貧弱なので、できれば差分表示はGUIの差分ツールを使用したいところです。
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設定ファイル(後述)に、外部diffツールとしてWinMergeを使用する設定を記述します。
[extensions] hgext.extdiff = [extdiff] cmd.wmdiff = C:\Program Files (x86)\WinMerge\WinMergeU.exe opts.wmdiff = /r /e /x /ub
RPMバイナリ版と、pythonのeasy_installモジュールによるPythonモジュールとしてのインストールと2つの方法があります。
CentOS 5.xでは、epelリポジトリにMercurail 0.9.3-1.el5 が用意されています。ちょっとバージョンが古いです。
サードパーティ・リポジトリのRPMForgeには、mercurial の新しいバージョンがあります。が、ここのリポジトリを使用すると、依存するパッケージもどんどんRPMForgeのものが入ってしまいます。(rpm-priorityパッケージを入れて、/etc/yum.repos.d/下のファイルにpriorityを設定する方法で回避はできるようですが未確認)
easy_installがインストール済みであれば、以下を実行します。
# easy_install -U mercurial : #
easy_installは、
Mercurialに関する設定は、設定の適用範囲によって異なるパスに設定ファイルを記述し配置します。
<リポジトリ>/.hg/hgrc
環境変数HOMEを設定している場合は、%HOME%\Mercurial.iniの名前で設定ファイルを記述します。
環境変数HOMEを設定していない場合は、%USRPROFILE%で示されるディレクトリの中にMercurial.iniの名前で設定ファイルを記述します。
以下の設定ファイルの内容をすべて読み込みます。
$HOME/.hgrc
Mercurialをインストールしたディレクトリ(例:C:\tool\Mercurial)の中に、"Mercurial.ini"の名前で設定ファイルを記述します。
/etc/mercurial/hgrc
設定ファイルに、無視ファイルのパターンを記述したファイルパスを指定します。
作業ディレクトリのトップディレクトリに、.hgignoreという名前のファイルをおきます。
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では、デフォルトではメモ帳が立ち上がります。
Mercurial.iniに記述します。gnuclientを導入している場合は、以下の記述となります。
[ui] editor = C:\Meadow2\bin\gnuclient.exe
※ gnuclientw.exeではなくgnuclient.exeとなっている点に注意
hg commitコマンドで、Meadowにコミットログ記載用バッファが表示されます。記述後、C-x # を実行すると、コミットの処理が継続します。(ただ保存するだけでは駄目)
diffの結果を表示するツールを指定します。
[extensions] extdiff = [extdiff] cmd.xxx = <外部diffツールのパス> opts.xxx = <外部diffツールのコマンドラインオプション>
diffの起動は、hg diff ではなく、[extdiff]のcmd.xxx に記述したxxxと同じ、hg xxx となります。
なお、WinMergeを外部diffに使用する際の設定記述はこちら。
まずは、1つのリポジトリのみを扱う操作を見ていきます。
以下のディレクトリ・ファイルがあったとします。
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コマンドで見てみます。
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コマンドでリポジトリへファイルを追加します。コマンドのオプションでファイルを指定しないときは、追加可能なすべてのファイルが追加されます。
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>
作業ディレクトリ上のファイルを削除し、次回コミット時にリポジトリからも削除する指定を行います。
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元が使われるようです。