チェンジセット

第1章の議論を思い出してほしい。私はバージョン管理手法の最も 原始的な形態としてのバックアップを取り上げ、それからテキストファイ ルに対する差分管理について述べた。バックアップをとるかわりにファイ ルの差分をつぎつぎと保存していけば十分であるという議論だった。チェ ンジセットはこのひとつのファイルに対する差分を、ディレクトリツリー 全体に対する差分へと拡張したものだ。だからチェンジセットのことをパッ チと呼ぶ人もいる。ちょっと考えると、これはわざわざ鳴り物入りで言う ほどのことではないような気もする。ファイルが N 個あるのなら、ファ イルごとに管理した N 個の独立した差分を考えれば十分ではないか、と。 GNUdiff を使ったことのある人なら、-r オプションつきで実行すれば、N 個の独立した差分を一つのファイルとして表現することすらできると思う かも知れない。しかしここに罠がある。

まず、パッチファイルには、ファイルの生成、消滅を厳密に扱う方 法がない。GNUdiff を使って同等のことを表現しようとすると、ファイル の生成は、ゼロバイトのファイルと、生成後のファイルの差分を作る形で 表すしかないし、消滅の場合には、消滅前のファイルの内容と、ゼロバイ トのファイルのファイルの差分を計算する形で表すより他ない。-N とい うオプションはこれを実行してくれる。しかしこれでは、その時点でファ イルが生成されたことと、実はゼロバイトで存在していたことを区別する ことができない。消滅の場合も話しが逆になるだけで同様である。

次に、パッチファイルはファイルのパーミッションの変化について 記録する方法がない。あるシェルスクリプト foo.sh に、うっかり実行権 限をつけるのを忘れたとする。あるリビジョンで内容には一切変更を加え ず chmod +x foo.sh のようなことをやって実行ビットを立てたとする。 パッチファイルにはこのような変化を記録する方法がないのだ。他にもま だある。ファイル名称の変更だ。あるファイル、./foo.h を、./include/foo.h に移動したとする。パッチファイルではこれを表現できない。あるファイ ル ./foo.h がある日突然ゼロバイトになり、./include/foo.h というゼ ロバイトのファイルが、突然 ./foo.h の内容になったようにしか表現で きない。これは都合がわるい。すでに述べたように、我々はファイル名の 変更を追うための強力なしくみ、インベントリを手にしているのだ。これ を生かさない手はない。しかしパッチファイルにはそれができない。

そんなこんなで、パッチの拡張であるチェンジセットのデータ構造 が厳密に定義された。チェンジセットはパッチが一つのファイルであるの に対して決まった名前のファイルと構造をもつディレクトリとして定義さ れる。これはチェンジセットを扱う GNU arch の内部ロジック、 dopatch()/mkpatch()が、このデータ構造中でランダムアクセス処理が発 生するためだ。パッチファイルはただのファイルなのでもちろんバイト列 が並んだシリアル形式のデータ構造だ。だからランダムアクセスには向か ない。しかし、これは単なる実装上の問題でしかない。チェンジセットが ディレクトリであることには本質的な意味は何も無い。

チェンジセットの厳密なデータ構造は補遺に示した。興味のある方 はご覧いただきたい。