分岐、そしてマージ

CVS について説明した。CVS は GNUpatch と GNUdiff をうまく組 み合わせて統合したソフトで、基本的な命令 get と commit を持ってい るのだった。get を使って作業コピーと呼ばれる場所に、バージョン管理 されたデータを読み込んでは修正し、一段落したら commit して、元のデー タに差分を追加していくのだった。実際にやっていることは要するに GNUpatch や GNUdiff と変わらないのだが、使い勝手はずっと良くなって いるし、GNUpatch や GNUdiff や差分の仕組みのことを知らない人でも気 軽にバージョン管理の恩恵にあずかれると、こういう話だった。利用者が 知っていなくてはならないのは、差分とか、特殊なファイル形式とか、繁 雑な GNUdiff/GNUpatch の操作ではなく、リポジトリ、作業コピー、 get/commit のような、もっと次元の高い概念だけでいいのだった。さら に CVS は複数の人間が get / commit する場合でも、お互いの変更がか ぶったりしないように調整したり、同時に複数の人が commit しないよう に、うまく交通整理してくれると、こういうわけだった。合言葉は「スイッ チ・ポン」だ。これが世界を救うのだ。

ここでは最後の、複数の人間の共同作業、というところをもう少し 堀下げて考えてみたい。ある一つのデータをみんなで協力して改良したり 修正している場合には get/commit だけで十分だ。たとえばいまずっと問 題にしてきた、国家間で利用されるある重要なドキュメントに対して、帰 りがけに A が 10243行目の誤字に気がついたとする。A は時計とにらめっ こしながらCVS に頼んで作業コピーに最新リビジョンの内容を get する。 作業コピーの上で10243 行目を修正する。そして CVS に頼んで commit する。ばっちりだ。でももしかすると B もちょうどそのとき同じ間違い に気づいてやっぱり自分の作業コピー上で10243 行目を修正してコミット してしまっていたとする。そんな時は CVS は、その修正はもう B がやっ てるよと教えてくれる。そして commit は失敗するが、もちろんその失敗 は意図したものだし、手柄が A じゃなく B のものになっただけで誰も困 らない。

しかし、もう少し複雑な状況を考えてみる。いまは、たった一つの ドキュメントをめぐっての作業フローだった。でも、以下のようなことも、 よくおこる。基本的にはこのドキュメントと同一の内容を持っているのだ が、内容の一部がほんの少し違っているようなものが欲しくて、しかもそ れはそれで独立した文書としてずっと管理していきたい、という場合だ。 たとえば、この国家間ドキュメントには、なんらかの非常に重要な機密事 項も含まれていて、各国の首脳級レベルの人間以外には配布したくない、 と。でもそのような部分は非常にわずかで、それ以外の部分については各 地に散らばっているエージェントに配布したい、と。極秘版と、一般大衆 版のふたつのバージョンが欲しい、と。

そんな重要な文章の機密内容を、俺や Hally や Sally 程度の人間 が編集できるもんかと言うかも知れない。ではもう少し現実的な例を出そ う。そのためには君にプログラマーの役を演じてもらう必要があるが、も 実際にそんな知識が必要なわけではない。学芸会に出てくる南の国の王様 みたいにそのまま椅子にふんぞり返っていてくれればいい。いま、ある企 業が新しいソフトウェアを開発している。このソフトウェアは有料なのだ が、よくあるように、評価限定版というのがあって無料でダウンロードし て利用できる。評価限定版は、製品版と、うり二つだ。ただし、30日たつ とすべての機能が利用できなくなってしまう。そこだけが製品版との違い だ。評価版 = 製品版 + 30日タイマー、と書き表すことができる。

図 1.11. 評価版 = 製品版 + タイマー

評価版 = 製品版 + タイマー

ところで、ソフトウェアには間違いがつきものだ。これをバグと言 う。ソフトウェアが完成したらバグも全部なくなっていると思うかも知れ ないが、そんなことはない。出荷したあともいろいろなバグが見つかるし、 そのバグは修正していかなくてはならない。もちろん大きなバグ、利用す るのに致命的なバグというのはなくなっている。でなければそんなソフト 買う人なんていないだろう。この細かいバグには作り手もユーザもいつも 悩まされる。作り手は新しく何かを作るではなく、既に作ったものを保守 しているわけだから、やる気も失せてしまっている。それにバグを直した つもりが、別のバグを生んでしまうことがある。笑うかも知れないが、こ れは本当によくおこることなのだ。逆に、ユーザはしみ一つない動作をす ると期待してソフトを買ったのに実際には細かい動作が意図したように動 かない。評価版が用意されているのにはこんな理由がある。もし評価版を 使って問題があれば、買わなければいい。あるいはここを直してくれたら 買うんだが、とその会社に E-mail してあげてもいい。

ながながと書いてしまったが、評価版 = 製品版 + 30日タイマー、 となっていた。評価版に新しいバグが発見されたとする。すると、それは 製品版と共通の部分に含まれているか、30日タイマーの部分に含まれてい るかのどっちかだ。共通部分に含まれているなら、評価版のバグを修正し たら、製品版にも反映させなくてはならない。たとえばプログラム開始時 に「ようこそ、このソフトウェアへ」という文言が間違って「そうこよ、 このソフトウェアへ」と表示されてしまったら、これは共通部分に含まれ ているバグだ。でも 30日タイマーの部分なら、製品版の方には反映させ てはいけない。たとえば 30日ではなくて、60日たって使えなくなったら、 これはタイマーのバグだ。評価版と製品版は、とてもよく似ている。そし てほんの少し、つまりタイマーの部分だけ、ちょっとだけ違っている。そ してそのちょっとだけ違っている部分を保ちながら、両者を厳密にそれぞ れ管理していかなくてはならない。もし 30日タイマーの部分を不用意に 扱って、そこに別のバグが生まれてしまったら、タイマーは働かず、評価 版はいつまでも使えるようになってしまうかも知れない。これでは会社が もたない。

CVS では、こういう状況がうまく扱える。これを分岐、と言う。そ して分岐を作ることをブランチ化すると言う。英語では動詞も名詞も branch という一つの言葉で表せるので都合が良いが、日本語の場合だと 場合におうじて分岐と言ったりブランチと言ったりする。今の例では、お そらく製品版の方が先にあったはずで、そのためのリビジョンが CVS リ ポジトリに並んでたはずだ:

図 1.12. リポジトリ内に一列に並んだリビジョン

リポジトリ内に一列に並んだリビジョン

話が違うんじゃないかと思う人もいるかも知れない。CVS のリポジ トリは初期リビジョン+差分の集まりだと説明したばかりじゃなかったの か、と。絵が縄文式に戻っているぜ、と。その通りなのだが、ここから先 は実際のリポジトリの内部構造は忘れてしまうことにする。そういうコマ ゴマした詳細を隠すためにできたのが CVS だし、ここからの議論には内 部構成は関係しないからだ。利用者から見れば、リポジトリにあるものは 時間と共に増えていく一方のリビジョンの集まりだ。上の図ではそれぞれ のリビジョンを数字だけで表している。これで十分なのだ。

ここから、評価版のための分岐を作る。このためのコマンドは紹介 しないが、やはりスイッチ・ポンで分岐できる。分岐した後は、CVS リポ ジトリの中はこうなる:

図 1.13. 分岐後のリポジトリの様子

分岐後のリポジトリの様子

二つの横に伸びた線のことを、開発ライン、と言う。そして、最初 からあった製品版の開発ラインのことを幹といい、後から分岐してできた 評価版の方の開発ラインを枝、と言う。もちろんこれは開発ラインが分岐 している様子を一本の木にたとえたものだ。幹は英語で trunk, 枝は branch で、みき、とか、えだという言葉がおさまり悪い時には、状況に 応じて、トランク、ブランチ、とそのまま書いたりすることもある。こん な風に CVS リポジトリの中にはふたつ以上の開発ラインを分岐させるこ とができる。いままでずっと説明してきた一本の開発ラインはその特別な ケースになっていることがわかると思う。注意して欲しいのは、トランク 内にある数字で示されたリビジョンとブランチ内の同じ数字で示されたリ ビジョンは何の関係もないということだ。数字はそれぞれの開発ライン中 でのリビジョンの番号を示しているだけで両者は独立した別のデータだ。 図中トランクのリビジョン2 からブランチのリビジョン 0 に矢印が伸び ているが、ここで分岐がおきたことを示している。だからこの二つのリビ ジョンの内容は全く同じものだ[2]

枝と枝との間では、両者に共通の修正点をお互いに取り込むことが できる。これをマージと言う。もちろんすでに説明したように、タイマー のバグの修正を製品版にとりこんじゃいけない。とりこむのはメッセージ の間違いの修正だけだ。たとえばこんな感じ:

図 1.14. 修正点の選択的なとりこみ

修正点の選択的なとりこみ

詳しく説明すると、トランク側の、2->3, 3->4, 5->6, 6->7 で追 加した修正はブランチ側に取り込まれていない。だから具体的にどういう バグかはわからないが、とにかく製品版にだけ関係したプログラムの間違 いだったのだろう。逆にブランチ側の 0->1, 2->3 はトランク側には反映 されていないので、評価版のみのバグ、おそらくタイマーに関係したバグ の修正だったのだろう。またブランチ側の 3->4 の差分はトランク側7 で 取り込まれている。だからこれは両方の開発ラインで共通のメッセージの 間違いのようなものだったのだろう。トランク側 4->5 についても同様だ が、最初に発見したのはトランク側開発チームで、それをブランチ側がと りこんでいる。こうして トランク と ブランチ の開発ラインは、共通の 修正点については同期して、片方にしか関係しない修正点についてはきち んとその違いを保存したまま、時間にそってしずしずと行進を、いや、更 新をつづけていくのだ。

さあ、CVS について大分説明してきた。細かい話は別にして、以上 が CVS の持っている機能の大部分だと考えてもらってよい。君は言うか も知れない。ブラボー。すばらしい。で、君はいったい何が不満なのだ、 と。

私の答えはこうだ:CVS の分岐概念には、根本的な欠陥がある。



[2] 厳密に言うとブランチ側 リビジョン0 には「自分はトランクのリビジョン2から分岐されたリビジョ ンです」という内容を示すごく小さな追加の制御情報を持っているかも知 れない。後述するが、実際 GNU arch ではブランチ側にそのような情報を 持っているので、ツリーの内容はそのぶんだけわずかに異なっている。