Pythonで分散バージョン管理

Mercurialの紹介

tags:python, module, scm, tips, develop, tool
created:2007-02-05T13:43:01

Pythonで分散バージョン管理にトライ!(修正) Pythonで作られたSCMツール「Mercurial」の紹介

SCMとは?

「Software Configuration Management System」の略で、 邦訳は「ソフトウェア構成管理システム」がふつーなんですが、 一般には「バージョン管理ツール」とかの方が通りが良いでしょう。

警告

サプライチェーンマネジメントの略語としても有名なので注意! 3頭文字略語は勘違いの元かもね。

主な機能として、 管理対象ソフトウェアのソースコード一群の変更を記録しておき、 時系列順に変更履歴をたどれるようにしたシステムなんです。

具体的な対象はソースコードファイルが基本です。

ソースコードの変更のあった行を抽出したり出来ます。

最近のバージョン管理ツールは あらゆるファイルの履歴を追えるようになっています。

ワードやエクセル、バイナリファイルなどもとりあえず バージョン別に記録されたものを取り出せます。 (部分的な変更を抽出まではできないことが多いです。)

仕組みとしては

「管理対象ファイル群」とは別に 「リポジトリ」と呼ぶ履歴情報を含んだ データベースのようなものを持っています。

管理対象ファイル群に修正が加えられると、 その変更部分にリビジョンナンバーがつけられ 「リポジトリ」に追記していきます。

過去の任意リビジョンを再構築して取り出すことができるので、 今日行った修正がとんでもないトラブルを引き起こした時に とりあえず昨日のリビジョンに戻しておいて 最新のリビジョンのデバッグを平行して行う、 といった作業の進め方ができるようになります。

面倒そうにみえる版管理に用事はなくとも一番助かったと思えるのは

  • 間違って削除してしまった時
  • 別名で保存しなきゃいけなかったのに同名のまま上書き保存してしまった時

これらの場合においてもすぐに以前の版のファイルを復元できます。

個人レベルにおいても仕事レベルにおいても こういったファイル紛失事故を回避するためだけに SCMを導入するというのも十分に価値があると思います。

いろいろあるバージョン管理ツール

バージョン管理ソフトウェアで有名なモノには

  • Visual SourceSafe
  • CVS
  • Subversion

などがあります。 これらメジャー系の多くは専用サーバーをLAN内に立ち上げるのを前提としているものが多いです。

当然、ローカルにも管理情報を置いているので、 サーバー側とクライアント側とで同期の手続きが非常に煩雑になりがちです。

ローカルオンリー利用も可能になってはいますが、 管理対象に対応する記録ファイルはやっぱり サーバーサイド、クライアントサイドの2重管理体制ですので、 仕組みとしての煩雑さは残っています。

まだマイナーなもののメリットもあることで注目されているのが

などです。 これらマイナー系のものには共通の特徴があります。

それは 仕組みがシンプル ということに尽きます。 変更履歴情報や管理情報を管理対象の近くにまとめて配置し、 内部処理が非常にシンプルになっています。

「内部処理が複雑だろうがシンプルだろうが使う側には関係ない」 というのは当然な意見です。

「Subversion」を採用しているオープンソースホストも増えているし 特殊なメリットが必要でなければ当然「Subversion」がオススメです。

以下のサイトでは集中型と分散型の違いについて書かれています。 (参考リンク)

http://subversion.bluegate.org/wheeler.html

ミスマッチ問題

リポジトリとチェックアウトフォルダと2箇所に 管理情報を置くタイプの最大の問題点。 それが、2元管理されている情報の「ミスマッチ」問題です。

マニュアルに載っている通りの操作では別段起こらないので まったく遭遇したことがないという人もいるでしょうが、 ちょっとイレギュラーな操作をすると、 リポジトリ上の情報とチェックアウトフォルダ上の情報との間で 整合できていない状態というのが作れてしまいます。

その状態が意外とぽろぽろ発生するということは、 「TortoisSVN」のコンテキストメニュー中に 「問題の解消」という項目があることからも伺えます。

「VisualSourceSafe」も「CVS」も「Subversion」も 構造上みんなこのトラブルに泣かされる事はありあえます。

ただし、Subversionはそれらの反省から後発で作りこまれている事もあり 他の2者よりもずっと致命的な状態にはなりにくいです。

うまく稼動している間は気分良く使えるのですが、 ミスマッチが見つかった時の正しいリカバリーを覚えておかないと、 うっかり変更履歴を紛失しちゃったという事になってしまいます。

致命的な状況を予防するために 「クリーンアップ->変更チェック->追加->コミット->更新」 などという煩雑な手順を 義務付けしたりしなければならなくなったりします。 また、これらの一巡には意外と時間が必要になります。 ファイル数の増加分が数倍になって跳ね返ってきます。

一元管理方式の場合これらの問題に悩まされずにすみますね。

いろんなSCMの機能比較(参考リンク)

http://better-scm.berlios.de/comparison/comparison.html

Mercurialの特徴

日本語ドキュメントがある

すばらしいですね。

http://www.selenic.com/mercurial/wiki/index.cgi/JapaneseTutorial

分散管理型
Subversionは「svk」という追加機能で実現していました。 darcsBazaarMercurial は分散管理が基本になっています。
Pythonモジュールとして構築されている
つまり、Pythonアプリの中で組み込んで使うことが容易です。 Bazaar もそうですが、強力なライブラリと連携した拡張や Webとの連携がしやすいです。
Webインターフェースが付属
CGIサーバーソフトウェアがパッケージに含まれているので、 Webインターフェースを通じたリポジトリ公開がすぐに始められます。
インストールが簡単
バイナリリリースもあり、利用開始は非常に簡単。
任意の2リビジョン間差分を抽出・取り込みができる
これのおかげでEMailしかできない環境でも変更履歴の共有ができます。
シンプルな構造ゆえに操作が高速
Mercurialでは分散リポジトリを基本としており チェックアウトファイル管理とリポジトリが一体化しているので 操作対象が少なく効率的に管理できるようになっています。

つまり、「Subversion」で

  • 分散管理
  • Webインターフェース
  • Pythonでがっつりコントロール

といったことをしたい時、それぞれ 対応したプロダクトを引っ張ってこなければならない。

Mercurial はそれらがオールインワンパッケージであり、 Subversionより軽量かつ高速なのです。

注釈

CネイティブでつくられたSubversionより9割以上Pythonでつくられた Mercurialのほうが高速に同期が取れるという不思議。

ピンポイントなチューニングとアルゴリズムだけで 十分実用的なものが実現できるという好例かもしれませんね。

ソースは50本のPythonソースと3本のCコードのみでできており、 コードの読み物としても手ごろかも。

Mercurialの仕組み

概念図:

digraph mercurial {
subgraph cluster0 {
  label=公開フォルダ
  リポジトリA;
  コンテンツA;
}
subgraph cluster1 {
  label=ローカルフォルダ
  リポジトリB;
  コンテンツB;
}
コンテンツA->リポジトリA [label = "commit"];
リポジトリA->コンテンツA [label = "update"];
コンテンツB->リポジトリB [label = "commit"];
リポジトリB->コンテンツB [label = "update"];
リポジトリA->リポジトリB [label = "pull"];
リポジトリB->リポジトリA [label = "push"];
}

リポジトリはコンテンツのそばに作成される 「.hg」というフォルダに収まっています。

公開フォルダとローカルフォルダに特別な違いはありませんので 役割が入れ替わっても同じように操作が可能なところが 「分散型SCM」の特徴です。

「commit」「update」は単なるローカルファイル操作ですので非常に高速です。 pullやpushではリポジトリ間の差分情報だけを汎用プロトコルで交換します。 (オリジナルプロトコルではありません。)

pullはローカル側リポジトリと公開側リポジトリの差分をダウンロードし、 ローカル側リポジトリに差分パッチを適用します。

pushはその逆方向の操作をローカル側から依頼します。

push、pullは可能ならローカルファイルアクセスを利用しますが、 それが不可能ならばHTTPまたはSSHを用います。

スタティックHTTPファイルダウンロードも可能なので .hgフォルダを含むファイル群をそのままHTTPサーバーに置くだけでも pullだけならできます。

注釈

ただし置くだけ公開はpullに時間がかかります。 付属のhgweb.cgiを利用して公開すれば、 HTTPで差分のみを引っ張るようになります。

インストール

2007/1月時点での最新版は0.9.3です。

ソースリリース:
http://www.selenic.com/mercurial/release/
バイナリリリース:
http://www.selenic.com/mercurial/wiki/index.cgi/BinaryPackages
WindowsまたはMac向けインストーラー:
http://mercurial.berkwood.com/

一番簡単なのはインストーラーをダウンロードして実行してください。

ソースリリースの場合、ダウンロード&解凍して以下のコマンドを実行してください。

setup.py install

途中、C拡張モジュールのコンパイル&ビルドを行います。 Windows版Pythonの標準リリースではVisualC++を探して利用します。

Mingw32ベースのコンパイラを利用する場合、 パスを通しておいて以下のようにコマンドを指定してください。

setup.py build -c mingw32 install

Webサーバーのテスト

「C:\cgi-bin\repo」にリポジトリを作成し、公開する場合の手順を以下に示します。

「C:\cgi-bin\hgweb.py」を以下の内容で作成。
import os
os.environ["HGENCODING"] = "UTF-8"

from mercurial.hgweb.hgweb_mod import hgweb
from mercurial.hgweb.request import wsgiapplication
import mercurial.hgweb.wsgicgi as wsgicgi

def make_web_app():
    return hgweb("C:/cgi-bin/repo", "Sample Repository")

wsgicgi.launch(wsgiapplication(make_web_app))

以下の手順でリポジトリの作成及びテストファイルの作成と最初のコミット:

C:\cgi-bin>md repo

C:\cgi-bin>cd repo
C:\cgi-bin\repo>hg init

C:\cgi-bin\repo>copy con test.txt
Hello HG!
^Z
        1 個のファイルをコピーしました。

C:\cgi-bin\repo>hg add
adding test.txt

C:\cgi-bin\repo>hg commit -u NoboNobo -m "test added"

C:\cgi-bin\repo>
C:\cgi-bin\repo>cd ..\..
C:\>python -m CGIHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...

以上の状態になったら

http://localhost:8000/cgi-bin/hgweb.py

を開いてみよう!

../../_images/mercurial_hgweb.png

こんな感じです。

注釈

良く調べると、「hg serve」なんて便利なコマンドが! 単体をウェブに公開するのはこのコマンド一発でOKなのですよ!

「manifest」を開けば管理対象ファイルリストが見れます。

mercurialのアーカイブには複数のリポジトリを公開する 「hgwebdir.cgi」というサンプルもあります。 Apacheで公開するなら以下のファイルを作成すると、 外部からpull/pushできるようになります。 参考としてこのサイトで使っている設定を以下に示します。

「/repos/hgwebdir.cgi」という配置の場合、 リポジトリフォルダそのものは公開せず、 「/repos」だけ公開すれば大丈夫。

cgiスクリプトのそばに「/repos/.htaccess」:

RewriteEngine On
RewriteBase /repos
RewriteRule ^$ hgwebdir.cgi  [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) hgwebdir.cgi/$1  [QSA,L]
Satisfy all
DirectoryIndex hgwebdir.cgi .ht

cgiスクリプトのそばに「/repos/hgweb.config」:

[paths]
samples = /.../samples
wxpython = /.../wxpython
ogre= /.../ogre

各リポジトリフォルダごとに「/.../samples/.hg/hgrc」:

[web]
allow_push=私のID
push_ssl=false
contact=メール@アドレス
description=Sample Repository

allow_pushはコミッタユーザー名です。 その名称のBASIC認証ユーザーでなければpushできないということです。 「*」を指定するとアノニマスユーザーがコミットできるようになります。

本サイトで稼動させていますので 以下のリンクを参照してみてください。

http://python.matrix.jp/repos/

Pythonでがっつりコントロール

from mercurial.commands import dispatch
ret = dispatch(['commit', '-u', 'NoboNobo', '-m', 'Hello'])

たったこれだけです。 コマンドラインの「hg」を取り除いた 引数リストを渡すだけでいいのです。

どうです? 簡単 でしょ?

まとめ

ファイル同期ツールとしてだけでもかなり便利です。 本サイトのコンテンツをローカルとサーバーとの間で 同期させるのにも利用しています。

以前サイトの更新には、 「更新ファイルをZIPアーカイブ」にして送りあっていたんですが、 Mercurial にしてから体感的に数倍は早くなった気がします。

あまりにもリモートリポジトリとの同期があっさり瞬時に行えるので、 ホントにちゃんと同期取れたか心配になってしまいましたが、 ちゃんとできていました。

同期の際に参照するファイルの数や プロトコルに載せるデータが非常にうまく整理されている ということでしょう。

唯一の難点はTortoisSVNのような便利なシェル拡張がまだないことでしょうか。 (こっそり簡易版を作成中・・・このサイトのどこかに置いています。)

darcsBazaar もほぼ同様の機能を持っているようですが、 darcs はウインドウズで動かない? Bazaar はプラグインが豊富みたいです。

軽快さは

SVN <<< Bazaar < Mercurial

こんな印象を受けました。

追記

使っていると、

  • 「日本語ファイル名を追加できない」
  • 「大文字小文字だけを名前変更にてアップデートできなくなる」

といった問題がありました。

「日本語ファイル名を追加できない」

この問題は根が深いようです。 Mercurialの基本ポリシーはエンコードに触れないということで、 参照マシン、コミットマシンがWindows日本語環境のみであれば大丈夫ですが、 他の環境が混ざっていると解決が難しい。

「mercurial 日本語」でぐぐってみよう。

「大文字小文字だけを名前変更にてアップデートできなくなる」

Windowsでは文字のケースを区別しないがために起こる問題。

当然Windows+Mercurialでの問題です。

Windows+SVNでも同様みたいです。 http://www.naney.org/diki/d/2004-08-02-Subversion.html

これは変な名前変更はするなってことでよろしいでしょうか。