Mercurialではじめる分散構成管理

第1回「分散」への第一歩 ~ ローカルでの複製

Mercurialとは、Pythonで実装された、分散リポジトリ形式の構成管理ツールです(⁠⁠バージョン管理ツール」⁠リビジョン管理ツール」とも呼ばれています⁠⁠。OpenJDKやMozilla、OpenSolaris等を始めとする多くのプロジェクトで採用されています。

単に「分散構成管理ツール」と言った場合、ネットワーク経由で連携する、いわゆる「クライアント・サーバ」形式で動作可能なCVSやSubversionも含まれますが、履歴情報の記録・管理が中央のサーバに集約されていることから、これらのツールは「中央集約リポジトリ(centralized repository⁠⁠」形式と言えます。

一方Mercurialは、リポジトリを複数の場所に分散させることができるため、⁠分散リポジトリ(distributed repository⁠⁠」形式と言えます。

「分散リポジトリ」などという仰々しい謳い文句に対して「理解するのが難しいのでは?」と気後れしたり、試しに使ってはみたものの今ひとつ便利さがわからない(あるいは「面倒なのでもう使いたくない!⁠⁠)と思わた方もおられるかもしれません。

しかし、ちょっとしたコツを飲み込みさえすれば、⁠分散リポジトリ」によって非常に高い自由度を獲得することができるのです。

この連載では、読者がイメージし易いように具体的な利用シチュエーションを設定しながら、Mercurialによる ⁠分散リポジトリ」での構成管理について解説しようと思います。本連載がMercurial、ひいては「分散リポジトリ」での構成管理に対する理解の一助になれば幸いです。

Mercurialのインストール

本原稿執筆時点では、OS環境ごとに以下のインストール方法を選択可能です。

表1 インストール方法一覧
OS 形式 方法
Linux/Unix Pythonスクリプト パッケージ管理経由
ソースからビルド
Windows バイナリ(*.exe) インストーラ経由
ソースからビルド
Pythonスクリプト Cygwinのsetup.exe経由
ソースからビルド

Linux/Unix環境ならパッケージ管理経由での導入が簡単です。

Windows環境への導入は、Cygwinを常用しているならCygwinのsetup.exe経由でPythonスクリプト形式を、そうでないならインストーラ経由でのバイナリ形式の導入が簡単です。

それぞれのインストール手順に関しては、別途詳細を説明したページを参照してください。

インストールが済んだなら、早速使ってみましょう。

まっさらな状態からリポジトリを作成する手順は次ページ以降で説明しますので、手近な対象としてMercurial自身のリポジトリを複製してみましょう。

コマンド1
% hg clone http://selenic.com/repo/hg mercurial-repo
requesting all changes
adding changesets
adding manifests
adding file changes
added 6571 changesets with 12628 changes to 910 files
795 files updated, 0 files merged, 0 files removed, 0 files unresolved
% ls -F
mercurial-repo/
%

これで"mercurial-repo"ディレクトリ配下にMercurial自身のリポジトリが複製されました("mercurial-repo"引数を省略した場合、URL末尾の"hg"が複製先ディレクトリ名として使用されます⁠⁠。

例えば、以下のように"hg log"コマンドを実行することで、Mercurial開発における履歴を参照することができます。

コマンド2
% cd mercurial-repo
% hg log -l 10
changeset:   6570:626cb86a6523
tag:         tip
user:        Benoit Allard <benoit@aeteurope.nl>
date:        Thu Apr 24 17:16:02 2008 +0200
summary:     add compression type type parameter to bundle command

changeset:   6569:c15bfe9cdcd6
user:        Benoit Allard <benoit@aeteurope.nl>
date:        Thu Apr 24 11:48:07 2008 +0200
summary:     add support for HG10GZ bundles to bundlerepo.bundlerevlog()

        :
     (省略)
        :

%

"-l 10"オプションは、表示対象を最新の10件に限定するためのものです。指定しなくても問題ありませんが、大量の履歴が表示されますのでご注意ください。

もしもネットワーク接続を簡単に切断できる状況にあるなら、切断した上で"hg log"を実行してみてください。いわゆる「オフライン」状態でも"hg log"が機能していることがわかるでしょう。

一旦構成管理情報を取り込んでしまえば、オフライン状態でも殆どの機能が利用可能な点は、⁠中央集約リポジトリ」形式に対する「分散リポジトリ」形式の利点の1つです。

リポジトリの作成

プロジェクトの開始等で、新たにリポジトリを作成する場合は、"hg init"コマンドを使用します。

コマンド3
% hg init myrepo
%

以上で"myrepo"ディレクトリ配下がMercurialの構成管理対象になりました("myrepo"ディレクトリが存在しない場合は自動的に作成されます⁠⁠。

「リポジトリの作成」と言っていたのに、⁠構成管理対象になった」とはどういうことだ?と思われるかもしれません。

Mercurialの「リポジトリ」は、⁠構成管理情報」の格納領域(CVS等ではサーバ側で管理される)と、構成管理対象ファイルの改変作業を行う「ワーキングディレクトリ」が、一体となったものを指します。言い換えれば、⁠リポジトリ」として作成したものであっても、構成管理情報の格納領域(".hg"ディレクトリ配下)を丸々削除してしまえば、それはもはや「リポジトリ」としては機能しません。

図1 リポジトリ構成図
図1 リポジトリ構成図

作成された「リポジトリ」は、すぐにでも「ワーキングディレクトリ」において編集作業を始めることができます。どうです? CVSやSubversionと比べて、非常に簡単なのではないでしょうか?

ちなみに、CVSのチュートリアル等にあるような、ある程度の成果物が既に存在する場合のリポジトリ作成は、以下の手順で行います。

コマンド4
% cd myproject
% hg init
% hg commit -A -m 'initial content'
adding dir1/dir2/dir3/night.txt
adding dir1/dir2/evening.txt
adding dir1/morning.txt
adding hello.txt
....
%

"hg commit"に対する"-A"オプションは、ワーキングディレクトリ中の全てのファイルを、自動的に構成管理対象に加えます。"'initial content'"部分はいわゆるコミットメッセージですから、各自で好きなように変更してください。

基本操作

Mercurialでは、先述したログ表示(log)と同様に、ワーキングディレクトリの状態表示(status⁠⁠、特定時点との差分表示(diff⁠⁠、構成管理対象への追加(add⁠⁠、変更の確定(commit)といった基本機能は、CVSやSubversionと同名のサブコマンド実行により行うことができます。

コマンド5
% echo "hello" > hello.txt
% hg status
? hello.txt
% hg diff
% hg add hello.txt
% hg status
A hello.txt
% hg diff
diff -r 000000000000 hello.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hello.txt	Mon May 05 21:15:32 2008 +0900
@@ -0,0 +1,1 @@
+hello
% hg commit -m 'add hello.txt'
% hg status
% hg diff
% 

紙面(磁性面?)の都合もありますので、 個々のコマンドの詳細に関しては、 "hg helpコマンド名"で表示される Mercurialのオンラインヘルプで確認してください。 特に、"hg status"で表示される各ファイルの状態表示マーク ⁠"?"や"A")は重要ですので、 "hg help status"は必ず確認しておくことをお勧めします。

Mercurialのオンラインヘルプ機能は、 利用可能なサブコマンドの一覧を"hg help"で表示することもできます。 いずれも英語表示ですが、 CVSやSubversionの利用経験から構成管理の用語に馴染みがあれば、 比較的理解しやすいのではないでしょうか。

チェンジセット

MercurialはSubversionと同様に、 複数のファイルに対する一連の変更を一括して記録する、 チェンジセット指向の構成管理ツールです。 通常の"hg log"では表示されませんが、 "-v"付きで実行した場合には、 各チェンジセットで変更されたファイルの一覧も表示されます。

コマンド6
% hg log
changeset:   0:a044b949325f
tag:         tip
user:        fujiwara
date:        Mon May 05 21:16:24 2008 +0900
summary:     add hello.txt

% hg log -v
changeset:   0:a044b949325f
tag:         tip
user:        fujiwara
date:        Mon May 05 21:16:24 2008 +0900
files:       hello.txt
description:
add hello.txt


% 

Mercurialはファイルのみを構成管理します(厳密には、⁠ファイルの内容を、パス名と共に管理する」と言えます⁠⁠。ディレクトリの追加を指示する必要が無い一方で、空のディレクトリを扱えない、という点で、CVSやSubversionと異なります。

Mercurialで特定のチェンジセットを指す場合、通常は以下の3種類の値のいずれかを使用します。

  • リビジョン番号:"changeset:"値における(":")の左側
  • 12桁ハッシュ値:"changeset:"値における(":")の右側
  • 40桁ハッシュ値:"hg --debug log" 時のハッシュ値位置

先の"hg log"出力における最新のチェンジセットを指す場合、"0"でも"a044b949325f"でも指定可能です(通常の構成管理で40桁ハッシュ値を使う機会・必要性はあまり無いでしょう⁠⁠。例えば、特定のチェンジセットに関する履歴だけを見たい場合は、"hg log -r 0"ないし"hg log -r a044b949325f"といったように使用します。

ちなみに、チェンジセットに与えられるハッシュ値は、変更内容+日時情報等の組み合わせから算出されるため、全く同じ内容・手順で"hg commit"に至ったとしても、同じ値が割り振られるとは限りませんので、本連載でのサンプル出力を参照する際には注意してください。

新しいツールやシステムを使い始める際に、何が不安かといえば、誤解や誤操作によって、それまでの諸々の作業に対して取り返しの付かない影響を与えるのではないか、というのが一番の不安なのではないでしょうか。

そこで、より分散リポジトリらしい内容に進む前に、まずは間違いを取り消す方法を説明しておこうと思います。

ワーキングディレクトリへの操作の取り消し

"hg commit"により変更を確定する前の操作、即ちワーキングディレクトリに対する操作は、"hg revert"コマンドにより取り消すことができます。

以下は、"hg add"によるファイルの追加指示の取り消し例です。

コマンド7
% echo "good by" > goodby.txt
% hg add goodby.txt
% hg status -A
A goodby.txt
% hg revert goodby.txt
% hg status -A
? goodby.txt
%

以下は、"hg remove"によるファイルの削除指示の取り消し例です。

コマンド8
% hg remove hello.txt
% ls hello.txt
ls: cannot access hello.txt: No such file or directory
% hg status -A
R hello.txt
% hg revert hello.txt
% ls hello.txt
hello.txt
% hg status -A
C hello.txt
% 

以下は、構成管理下にあるファイルの内容変更の取り消し例です("hg revert"により作成される"hello.txt.orig"は、"hg revert"直前の内容を保持するバックアップファイルです⁠⁠。

コマンド9
% echo 'good by' >> hello.txt
% hg status -A
M hello.txt
% hg diff
diff -r a044b949325f hello.txt
--- a/hello.txt	Mon May 05 21:16:24 2008 +0900
+++ b/hello.txt	Mon May 05 21:26:19 2008 +0900
@@ -1,1 +1,2 @@ hello
 hello
+good by
% hg revert hello.txt
% hg status -A
C hello.txt
? hello.txt.orig
% hg diff
% 

以下は、手動でのファイル削除の取り消し例です。

コマンド10
% rm hello.txt
% hg status -A
! hello.txt
% hg revert hello.txt
% ls hello.txt
hello.txt
% hg status -A
C hello.txt
% 

ご覧のように、"hg revert"により取り消すことができます。ちなみに、"hg status"に対する"-A"オプション指定は、⁠改変無し="C"lean」なファイルに関する状態表示も行うように指示するものです。

リポジトリへの操作の取り消し

間違って実行してしまった"hg commit"は、 "hg rollback"により取り消すことができますが、 残念なことに1回分しか取り消すことができません。

「一回分しか取り消せない⁠⁠ ことを「多い」と見るか「少ない」と見るかは人それぞれでしょうが、 一旦"commit"してしまったら取り返しがつかないCVSと比較すれば、 心理的な余裕は断然違ってくるでしょう。

コマンド11
% echo "good by" >> goodby.txt
% hg add goodby.txt
% hg status
A goodby.txt
% hg commit -m 'goodby'
% hg status
% hg rollback
rolling back last transaction
% hg status
A goodby.txt
% 

ファイルの複製・改名

Mercurialでは、 "hg copy"を使うことで、 ファイルを複製することができます。

コマンド12
% hg copy hello.txt goodby_hg_copy.txt
% hg commit -m 'copy hello to goodby'
% hg log goodby_hg_copy.txt
changeset:   1:b3fdaab39142
tag:         tip
user:        fujiwara
date:        Mon May 05 21:30:58 2008 +0900
summary:     copy hello to goodby

% hg log -f goodby_hg_copy.txt
changeset:   1:b3fdaab39142
tag:         tip
user:        fujiwara
date:        Mon May 05 21:30:58 2008 +0900
summary:     copy hello to goodby

changeset:   0:a044b949325f
user:        fujiwara
date:        Mon May 05 21:16:24 2008 +0900
summary:     add hello.txt

% 

"hg log"コマンドに"-f"(ないし"--follow")オプションを指定することで、複製元ファイルの履歴情報(上記例では"hello.txt"由来の"a044b949325f")も表示させることができます。

(1)"hg copy"による複製が複製元の履歴情報を引き継ぐことと、⁠2)"hg remove"による削除でも履歴情報はリポジトリに保持されたまま、ということを利用し、これら2つを組み合わせることで、実質的に「改名」として機能する"hg rename"というコマンドも提供されています。

"hg revert"や"hg rollback"による取り消しと違い、履歴情報がリポジトリに残ってしまいますが、一種の「取り消し」とも言える「改名」ができるということも、心理的な余裕に繋がるのではないでしょうか?なお、心理的余裕のせいで命名がぞんざいになる、という弊害に関しては、各自で対応してください。

それでは「分散」への第一歩として、ローカルホストにおけるリポジトリの複製をしてみましょう。

「分散リポジトリ」の解説を期待していたのに、⁠ローカルホストにおける複製」と聞いて、拍子抜けしている方も居られるかもしれません。しかし、リポジトリ間の連携方式が統一されているMercurialを利用する場合、連携対象リポジトリがローカルホスト上にあるのか、それともネットワークの向こう側にあるのかは、さして大きな違いでは無いのです。

複製方法あれこれ

一番簡単なリポジトリの複製方法は、複製元リポジトリを指定して"hg clone"を実行することです。

コマンド13
% ls -F
myrepo/
% hg clone myrepo myrepo2
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
% ls -F
myrepo/   myrepo2/
% 

別な複製方法として、他のリポジトリから"hg pull"により構成管理情報を取り込む方法があります。ただし、"hg pull"の実行に先立って、取り込み先リポジトリを初期化しておく必要があります。

コマンド14
% hg init myrepo3
% ls -F
myrepo/   myrepo2/  myrepo3/
% cd myrepo3
% hg pull ../myrepo
pulling from ../myrepo
requesting all changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
(run 'hg update' to get a working copy)
% 

察しの良い方は「pullがあるなら~」と、薄々予想されていることと思いますが、 予想通り"hg push"によって構成管理情報を「送りつける」ことでの複製もできます。

コマンド15
% hg init myrepo4
% ls -F
myrepo/   myrepo2/  myrepo3/  myrepo4/
% cd myrepo
% hg push ../myrepo4
pushing to ../myrepo4
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
% 

各リポジトリにおいて"hg log"出力を確認することで、それぞれがmyrepoの複製であることを確認できる筈です。

ちなみに、"hg init"で初期化したてのリポジトリに対して"hg pull"/"hg push"により複製を行った場合、ワーキングディレクトリは空のままです。最新の成果を反映させる場合は"hg update"を実行してください。

「分散」「自動伝播」

リポジトリの複製ができたところで、早速myrepoに移動して変更作業をしてみましょう。

コマンド16
% cd myrepo
% echo 'good morning' > morning.txt
% hg add morning.txt
% hg commit -m 'add morning'
% hg log
changeset:   1:b85af333a085
tag:         tip
user:        fujiwara
date:        Mon May 05 21:57:35 2008 +0900
summary:     add morning

changeset:   0:a044b949325f
user:        fujiwara
date:        Mon May 05 21:16:24 2008 +0900
summary:     add hello.txt

% 

"hg commit"によりmyrepoで変更を確定したなら、myrepoから複製した他のリポジトリに移動して、"hg log"出力を確認してみてください。

"hg clone"、"hg pull"のいずれで複製したリポジトリに対しても、複製元であるmyrepoでの最新の"hg commit"結果(上記例での"b85af333a085")が反映されていないことがお分かりでしょう。逆方向(⁠⁠複製先」「複製元⁠⁠)の反映に関しても同様です。

「分散」などという大仰な形容が付くと、個々が自立的に連携して云々といった、いかにも複雑なものを思い浮かべる方も居ることでしょう。しかし、ことMercurial における「分散」に関しては、⁠複数のリポジトリが存在し得る」⁠各リポジトリは独立して機能することができる」という程度の意味しか無いのです。何の設定も無しに、各リポジトリ間で構成管理情報が自動的に伝播することはありません。

さて、myrepoおよびmyrepoの3つの複製で、それぞれ異なる変更内容を"hg commit"により確定したと仮定します。その際の各リポジトリが保持する構成管理情報を模式的に表すと、以下のようになります。

図2 成果の分散
図2 成果の分散

矩形内の数字は各リポジトリごとのリビジョン番号を、矩形の色はそのチェンジセットがcommitされたリポジトリを表しています。リビジョン番号が同じでも、色=commitされたリポジトリが異なれば、そのチェンジセットは異なるものと考えてください。

この状況はまさに、複数の開発者のそれぞれが、自分のリポジトリにおいて作業を行った状況に相当します。

次回は、上記のように分散した状態にある成果の、集約ならびに統合について説明したいと思います。

おすすめ記事

記事・ニュース一覧