Mercurial に入門してみる

どうも、お久しぶりです。我ながらこのブログの更新頻度の低さはひどいものですね。ここ数回の記事が ICFP ネタ(しかも1年おき)というのもどうかしているとしか思えません。

しかし今日なんとなくここの存在を思い返したので、ちょっとたまには技術的な記事でも書いてみようと思います。


というわけで Mercurial 入門です。といっても裏口から忍びこむ感じなので、ごく普通に Mercurial を使い始めたいという方は別の解説ページにあたることをおすすめします。


Mercurial はご存知の通り分散 SCM の一種です。似たソフトウェアには Git, Bazaar などがあります。では Mercurial はどういう点でそれらと違うのか、と聞かれると、実はあまりよく分かっていなかったりします。というのも恥ずかしながら Git や Bazaar をあまり使ったことがないからです。Git はちょっと使ってみようと思ったことが幾度と無くあるのですが、日常的に使う機会がないこともあり、どうやって動いているのかまだ理解できていません。

しかしながら話を聞く限り Mercurial にしかないであろうと確信している特徴を挙げると、次の 2 つになります。

  • データ構造がシンプル
  • Python で書かれている


データ構造に関しては Mercurial: The Definitive Guide の一節 を読めば裏で何が起こっているかなんとなく想像がついてしまうほどシンプルです。Mercurial を使うすべての人が読むべき文書だと思います。

二つ目の Python で書かれているということに関してはあまりぱっとしないかもしれません。ユーザにとってそれがどの言語で実装されているかが重要であることなんてほとんどありません。あるとしたら処理速度くらいで、その点でいえばスクリプト言語で実装されている Mercurial は不利でしょう。しかし、少なくとも一つアドバンテージがあるのです。それは何かというと、この記事の本題なのですが、スクリプト言語から API 呼び出しが簡単にできることです。


前置きは置いておいて、とりあえず入門らしいことをしてみましょう。やる内容は、

  • 新しい Mercurial リポジトリを作成
  • README という名前のファイルを作ってコミット
  • ファイルがちゃんとリポジトリにコミットされたことを確認する


だけの簡単な作業です。

まず、Mercurial をシステムにインストールしておきます。Debian/Ubuntu なら apt で mercurial パッケージをインストールするだけです。そして、python インタプリタを立ち上げます。

[nya@bardiche ~]% python
Python 2.6.6 (r266:84292, Sep 15 2010, 16:22:56)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

そして mercurialPython モジュールをロードします。後ほど使うので、mercurial.ui.ui クラスのインスタンスを作っておきましょう。

>>> import mercurial.hg
>>> import mercurial.ui
>>> ui = mercurial.ui.ui()

まずは Mercurial リポジトリの作成です。場所は /tmp/hgtest にします。

>>> repo = mercurial.hg.repository(ui, '/tmp/hgtest', create=True)

これで /tmp/hgtest にリポジトリが出来ました。とても簡単ですね。とりあえず何かファイルを追加してみましょう。

>>> f = open('/tmp/hgtest/README', 'w')
>>> print >>f, 'Hello, Mercurial!'
>>> f.close()
>>> repo[None].add(['README'])
[]
>>> repo.commit('first commit', user='nya')
')\x9dt:\x94\xd9T\x82J\x10\x9da\xe2fB\xef\xab\x9do\xc6'

これでコミットできました! 最後に出てきたバイナリ文字列はこのコミットのIDです。

実際にコミットされていることを確認してみましょう。まずリポジトリの tip を取ってみます。

>>> head = repo.changelog.tip()
>>> head
')\x9dt:\x94\xd9T\x82J\x10\x9da\xe2fB\xef\xab\x9do\xc6'

さきほど帰ってきたバイナリ文字列と同じですね。これを使ってまずコミットの内容を表すデータである changelog を取ります。

>>> ch = repo.changelog.read(head)
>>> ch
('\x8eB\x0c\xf3+\xb7\x03\x1c\r\xc7\x9a\xe6\xe9\x07\x19w\x82\xde?\xd1', 'nya', (1299683052.0, -32400), ['README'], 'first commit', {'branch': 'default'})

ほとんどの要素の意味は見れば想像が付くと思います。1番目のバイナリ文字列はマニフェストのIDです。マニフェストとはあるリビジョンにおけるファイル名とその内容を関連付けるデータです。読み込んでみましょう。

>>> mf = repo.manifest.read(ch[0])
>>> mf
{'README': 'Q\x82\xf8\xe0\xaaIn\x87r\xf8\x0b>\x07\x86eL3\x8d\xb4\xd3'}

この辞書の値になっているのが、このリビジョンにおけるファイルの内容を示すIDです。これを使うことでファイルの内容を取得することができます。

>>> repo.file('README').read(mf['README'])
'Hello, Mercurial!\n'

ちゃんと書いた内容がコミットされていることが確認できました。

短いですが今回はここまでです。また気が向いたら続きを書くかも?