写真 青い空と雲と建物と木々の緑

nishimotzの日記

  • 分散バージョン管理の意義

    いくつかのシステムに Mercurial をインストールして使い方を勉強しつつ、なぜ「バージョン管理システムを使うのか」について改めて考えました。

    まず「バージョン管理システムはバックアップツールだ」という主張は一見わかりやすいのですが、どうも違和感があるので、さらに考えた末、以前から考えてきたこと(数年前にソフトバンクのCVS本に書いたこと)と重複するのですが

    • バージョン管理システムは「無駄なバックアップを安全に消去するツール」である
    • バージョン管理システムは「リファクタリングを安全に行うツール」である

    と思えてきました。

    まず「バックアップの消去」について説明したいと思います。

    Windows のユーティリティに undup という重複ファイル削除ソフトがあるらしいのですが、バージョン管理システムは「賢いundup」ではないかと思うのです。

    以前と比べてバックアップを取ることは簡単になりました。モバイルPCも安価になり手軽になり、複数のマシンにコピーして作業することも日常的になりました。USBメモリも外付けドライブもネットワークストレージも手軽に使えるようになり、ついあちこちに気軽にバックアップコピーを取ってしまいがちです。

    しかし、本当に難しいのは「バックアップを取ること」ではなく「バックアップを捨てること」ではないでしょうか? あちこちにばらまかれてしまった重複ファイルを安全に削除するためにはどうしたらいいでしょうか? 完全に同じファイルなら安全に消すことができますが、もし改変があるなら、それを保存するべきかどうかを簡単に判断できるでしょうか?

    消去すべきファイルは、重複ファイルだけではありません。

    不要なファイルを簡単に見分けるための工夫として、ここ数年、私はファイル名の付け方の規則として

    • 一時ファイルには、先頭文字がアンダースコア(_)で始まるファイル名をつける

    ということを教わって実践しています。しかし、いろいろなソフトウェアの事情があり、この規則を徹底できない場合があったとすると、「保存するべきファイルか、一時的なファイルであるか」という区別は、ファイルそのものに記憶させるのではなく、メタ情報として外部に保存する必要が出てきます。

    バージョン管理システムのリポジトリとはまず「バックアップをすべきファイルかどうかを表す属性情報である」と捉えてはどうでしょうか。重複ファイルも一時ファイルも簡単に判断できる手段を提供するのがバージョン管理システムだと思うのです。

    もう一つの「リファクタリングの道具としてのバージョン管理」については、すでに多くが語られていると思うのですが、「プログラムを、機能を変えずに、構造を改良する」という作業のためには、「確実かつ効率的に過去の状態を参照できる(元の状態に戻せる)」という作業環境が求められます。ファイル群を丸ごとコピーしてしまうと、効率的に差分を表示することが難しくなります。

    バックアップとリファクタリングは目的が違います。バックアップは物理的に別のデバイスに保存する必要があるため、ネットワークを介した通信機能は必要です。しかし、リファクタリングはネットワークから切断された状態でも行われるべき作業なので、リポジトリがローカルに存在しないのはとても不便です。

    このように考えてみると Mercurial の分散バージョン管理という仕組みは、昨今のコンピュータ利用の現状にふさわしい枠組みではないか、と思います。

    ロック・アンロックをベースにしたRCSが大人数での分散作業に向いていない、ということで、コピー・マージ方式を導入したCVSが数年前に広く普及しました。それと同時に「伽藍とバザール」のモデル比較が話題になり、CVSはバザール型のオープンソース開発にふさわしいツールである、と考えられた時代がありました。しかしいま振り返ると、CVSは「バザール開発のツール」というよりも「みんなで分担してひとつの大きなものを作る」ための共同作業ツールであったように思えます。

    しかし現実に求められている問題の多くは「みんながそれぞれ自分のための派生物を作る」ことであり、さらに「派生物の差分を取捨選択して共有する」ことだったりします。そのためには、みんなが別々にリポジトリを持つ必要があり、しかもリポジトリ同士でマージを行うメカニズムがやはり必要なのだろう、と思います。

  • Mercurial によるバージョン管理

    巷で話題の「分散」バージョン管理システム Mercurial を使ってみました。

    遠い昔にCVSの本を書いたときには「オープンソース」という言葉が世の中に広まりはじめたばかりでした。

    でも私の興味はオープンソースそのものではなくて「個人が安全にソフトウェアを開発する」あるいは「グループが効率的に共同作業をする」ということでした。CVSよりもSubversion(svn)の方が優れているらしい、とは聞き及びつつも、道具にこだわるよりも、何かを作ることのほうが大事だと思ってきました。

    最近やっと Eclipse 経由で(というよりも3rdRail経由で)Subversion を使うようになりましたが、Eclipse からいじっている限り大きな違いはなく、大きな感動もありませんでした。むしろ、日々のコミット作業のインタフェースよりもリポジトリのビューワーの方がよっぽど大事だと思うのに、cvsweb はどうなっちゃったんだろう、などと思いました。

    そんなことを思いながら、Mercurial を触りはじめて、逆に cvs/svn が「グループが効率的に共同作業をする」というところに重きを置きすぎて「めんどくさい」ツールになってしまったことに気づきました。ある状況で「CVS よりも RCS の方が適しているのでは?」と思ったこともあるのですが、かつて RCS が適していた場面をカバーしてくれる(そしてそれ以上のことを実現してくれる)のが Mercurial だと思います。

    バージョン管理システムを「これはバックアップツールの一種です」と説明している本やサイトも多いのですが、そもそも「バックアップ」と「バージョン管理」は分けて考えた方がいいのではないか、という気もしてきました。(例えば pdumpfs はよいバックアップツールだと思いますが、バージョン管理ツールだと言い張るのは無理ではないかと。。。)

    「個人が安全に開発する」というところを重視するなら「作業中のファイルをいったん import して checkout しなおす」という「謎の御点前」が不要なだけでも Mercurial はずいぶん手軽だなあ、と感じました。

    とはいえ、Mercurial においても”hg init” して “hg add” して “hg ci” しなくてはいけないので、手間の問題というよりも「安全な場所を決めてリポジトリを作る」という作業そのものの精神的な抵抗がないことのメリットなのかも知れませんが。

    もう一つ評価に値するのは、リポジトリのビューワーが内蔵されていることです。”hg serve” を実行して、ウェブブラウザで localhost:8080 を開けば、cvsweb ライクなインタフェースがすでに備わっている、ということがわかりました(ポート番号はオプションで変えられるとのこと)。

    そんなわけで、私がいま試している Mercurial 1.0 の Windows 版バイナリはこちらからどうぞ:

    Mercurial なのにコマンドが hg なのは「水銀」の元素記号だからだそうです。

    そして hg は Python で実装されているとのこと。Python は個人的にコーディングスタイルが好きではない(制御構造を可視化すること=プログラムを読みやすくすること、とは限らないですよね?)のですが、Google に限らずいたるところで重要な言語になってきたなあ、と思います。

  • CodeGear C++Builder の property

    気になったので CodeGear C++Builder 2007 で試してみました。

    // THoge.hpp
    #ifndef THogeHPP
    #define THogeHPP
    class THoge
    {
    private:
    int f_count;
    void set_count(int n);
    int get_count();
    public:
    THoge();
    ~THoge();
    void doSomething();
    // __property int count = {read = f_count, write = f_count};
    __property int count = {read = get_count, write = set_count};
    };
    #endif
    
    // THoge.cpp
    #include "THoge.hpp"
    THoge::THoge()
    {
    f_count = 0;
    }
    THoge::~THoge()
    {
    //
    }
    void THoge::doSomething()
    {
    f_count ++;
    }
    void THoge::set_count(int n)
    {
    f_count = n;
    }
    int THoge::get_count()
    {
    return f_count;
    }
    
    // Unit1.h
    // (omitted)
    private:	// ユーザー宣言
    THoge *hoge;
    // (omitted)
    
    // Unit1.cpp
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
    {
    hoge = new THoge();
    hoge->count = 100;
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    hoge->doSomething();
    int c = hoge->count;
    AnsiString s;
    s.sprintf("hoge %d", c);
    Edit1->Text = s;
    }
    

    というようなものがちゃんと動きました。

    __property という予約語は自分で勝手に作ったクラスで使える、ということのようです。Visual C++ の CLI でも property が使えるそうですが、CodeGear だと .NET じゃない Win32 でこれが使えるわけですね。

    THoge クラスで予約語 __published: を使おうとしたらコンパイルエラーになったので、こちらは VCL 関連のクラスを継承していないとダメみたいです。

    CodeGear RAD Studio の Help にはほとんど書かれていない C++ の property 拡張ですが、ここに説明がありました: