ノード: Basic Concepts, 次: , 上: An Overview of CVS



Basic Concepts

CVS や他のバージョン管理システムを使ったことがない場合、基本的な仮定がわ からないために足を取られてしまうのは目に見えています。CVS を使い始める時 に最初に混乱するのはだいたいの場合、CVS の使用目的が2つあって(レコード保 持と共同作業)、その2つが明らかに関連がないから、のように思えます。 結果 的にその2つの機能は密接に結びついてしまっているのですが。

レコード保持は必須の機能です。プログラムの現在の状態を、以前は同じところ がどのようであったか比べたいと思う人が多いからです。例えば、新しい機能を 実装しようとすると通常、開発者はプログラムを全く動かない壊れた状態にして しまい、その機能を大概実装し終わる頃までは壊れたままになるものです。そう いう時に限って、以前リリースしたバージョンのバグレポートがやってきます。 そのバグ(今いじっているバージョンのソースにも多分存在するんでしょうね)を どうにかするためには、そのプログラムを使える状態にまで戻してやらなければ ならないのです。

ソースコードの履歴を CVS で管理していれば、その状態を元に戻すのに何の苦 労も要りません。実際、開発者は単に「3週間前の状態のそのプログラムをよこ したまえ」、あるいは「最近のリリース時の状態のプログラムをよこしたまえ」 と言いさえすればいいのです。あなたがもし、履歴へのアクセスをこういう風に 便利な方法でやったことがないなら、これを使うようになった時そのあまりの素 早さに驚くと思います。わたしも今コーディング中のプロジェクトでリビジョン 管理をいつも使っていて、何度も救われました。

共同作業を容易にするために何が必要か理解するためには、ひとつのプロジェク ト上で多数の人々が働けるよう CVS が提供している機構について、詳しく見て いく必要があるでしょう。まあでもその前に、CVS が提供していない(または少 なくとも支援していない)機能をちょっと見ましょうか。それはファイルのロッ クです。他のバージョン管理システムを使ったことがあるなら、「ロック-更新- ロック解除」開発モデルはおなじみだと思います。開発者はまず編集したいファ イルの排他的書込みアクセス(ロック)を取得し、次にそれを変更、そしてロック を解除して他の開発者がそのファイルにアクセスできるようにします。既に誰か がファイルをロックしていれば、あなたがそのファイルを変更する前にまずロッ クを「解除」してもらわなければなりません。(いくつかの実装ではロックを 「盗む」ことができますが、これは時々盗まれた側の悲鳴が上がることになりま すね、よくない慣習です)

このようなシステムは、開発者がお互いをよく知っており、任意の時刻に誰が何 をしようとしているか知っており、アクセスの競合が起こって誰かが作業できな い時には素早く連絡できるような状況であればうまく動きます。しかし開発グルー プが大きくなり、散らばってくると、ロックのことばかりが時間を取り始め、コー ディングする時間を削っていきます。こうなると混乱が定常状態となり、人々か ら本来の仕事をやる気を削いでしまいます。

CVS はもう少し成熟したアプローチを取ります。衝突しないよう開発者自身に調 整させるのではなくて、CVS は開発者が同時に編集できるようにし、変更全てを 統合する仕事を引き受け、衝突を追跡します。この処理には「コピー変更マージ」 モデルを用い、次のように動作します:

  1. 開発者Aは CVS から作業コピー(プロジェクトを構成するファイルを含むディレ クトリツリー)を取得します。これは作業コピーを「チェックアウト」するとも 言います。図書館から本を借り出す(チェックアウト)ようだからです。
  2. 開発者Aは自分の作業コピーを自由に編集します。その頃、別の開発者は各自の 作業コピーにて忙しく仕事をしています。それぞれに別のコピーを持っているの で衝突はありません。それはあたかも、開発者全員が図書館の同じ本のコピーを それぞれ持っていて、それぞれ独立にそれの余白にコメントを書き込んだり、あ るページを書き換えたりしている様子のようです。
  3. 開発者Aは変更を終え、その変更の性質と目的を説明する「ログメッセージ」と ともに CVS へ変更をコミットします。これは、本の何を変更したか、及びその 理由を図書館に知らせることにたとえられると思います。図書館はこれらの変更 を「マスタ」コピーへ受け入れ、それを永久に記録します。
  4. 一方、他の開発者は最近マスタコピーが変更されたかどうかを図書館に問い合わ せることができます。変更があれば、CVS は自動的に作業コピーをアップデート します。(ここが魅力のある素晴らしいところです、あなたもここを評価すると いいなあ。実際の本もこういう風になっていたら世界はどんなに違うでしょうね!)

CVS のもとでは、あるプロジェクト上の全ての開発者が平等です。いつアップデー トするか、いつコミットするかを決定するのは主に個人の好みまたはプロジェク トのポリシーです。コーディングプロジェクトでよく使われるやり方の一つは、 大きな作業を始める前にアップデートを行い、変更が完了してテストしたときだ けコミットするというもので、こうするとマスタコピーはいつも「動く」状態に 保たれます。

たぶんあなたは、「開発者AとBが、それぞれの作業コピーの同じところで違う変 更を施し、両者が変更をコミットしたらどうなるの?」と思っているんじゃない かと思います。これは コンフリクト (conflict, 衝突) と呼ばれるもの で、開発者Bは変更をコミットしようとした時点で CVS からコンフリクトを知ら されます。開発者Bは次に進む前に、CVS から、コンフリクトを検出したことと、 作業コピーのコンフリクトの起こった箇所にコンフリクトマーカー(見てすぐに 分かるテキストのフラグ)を挿入したことを知らされます。そこには両者の変更 が示されており、比較しやすいようになっています。開発者Bはそれを全て整頓 して、コンフリクトを解消した新しいリビジョンをコミットしなければなりませ ん。開発者2人はこの問題を解決するために話す必要があるでしょう。CVS はコ ンフリクトが存在することを開発者に警告するだけです。 実際に解消するのは 人間の役目です。

マスタコピーっていうのは何なのかって? 公式の CVS の用語では、それはプロ ジェクトのリポジトリと呼びます。リポジトリというのは単に、中央のサーバに あるファイルツリーです。その構造の詳しいところはまあ置いておいて(それは Repository Administration を見てね)、チェックアウト-コミット-アッ プデートのサイクルの要件を満たすためにリポジトリが何をしなければならない かを見ていきましょう。次のシナリオについて考えてみて下さい:

  1. 開発者が2人(AとBとします)、プロジェクトの作業コピーを同時にチェックアウ トしたとします。プロジェクトは開始したばかりで、誰も変更をコミットしてお らず、ファイルは全部オリジナルの状態のままです。
  2. 開発者Aはすぐに作業を始め、変更のひとまとまりをコミットします。
  3. その頃、開発者Bはテレビを見ています。
  4. 開発者Aはまるで明日がないかのようにハッキングしまくり、2回目のコミットを 実行します。この時点で、リポジトリの履歴にはオリジナル、次にAの初回変更、 その次に今回の変更が記録されています。
  5. その頃、開発者Bはテレビゲームをしています。
  6. ここで突然、開発者Cがプロジェクトに加わり、リポジトリから作業コピーをチェッ クアウトします。開発者Cの作業コピーにはAの2回分の変更が反映されています。 チェックアウトした時にはその変更はもうリポジトリにあったからです。
  7. 開発者Aは何かに憑かれたかのようにコーディングを続け、完了して3回目のコミッ トを行います。
  8. 開発者Bは、例の狂ったような活動にも気づかないまま(幸せなヤツだ)、ついに 「そろそろ始めるか」と決めたようです。作業コピーをわざわざアップデートす るような面倒なことはやらずに、すぐファイルを編集し始めます。そのなかには Aが作業したファイルもいくつかあるかもしれません。そして開発者Bは最初の変 更をコミットします。

この時点で、次のうちいずれかになります。A が編集したファイルをBが一切編 集しなかったとしたら、コミットは成功します。しかし、B のファイルがリポジ トリの最新に追いついていなくて、しかも B がそれらのファイルを編集してい ることを CVS が認識したら、CVS は B に対し、コミットする前にアップデート しなくてはならない、と知らせます。

B がアップデートをかけると、CVS は A の変更をBの作業コピーにマージします。 Aの作業分は、Bのまだコミットしていない作業分とコンフリクトするものもある し、しないものもあるでしょう。コンフリクトしない分については B の作業コ ピーに適用されてそれで終わりです。しかしコンフリクトしている分については、 コミットする前に B がコンフリクトを解消しなければなりません。

ここで開発者 C がアップデートを行ったとすると、リポジトリから変更をいろ いろと受け取ることになるでしょう。A の3回目のコミット分と、B の初回コミッ トの成功した分です(ホントは2回目にコミットしようとした時のやつで すね、初回にコンフリクトがあって失敗してるとしたら)。

いろいろな程度に最新に同期していない作業コピーを持っている開発者に対し、 CVS が正しい順序で変更を提供するためには、リポジトリはプロジェクトの最初 から全てのコミットを保存しておく必要があります。実際には、CVS リポジトリ は連続的に diff を取ってそれを保存しています。ですから、とても古い作業コ ピーがあったとしても、それとリポジトリの現状の違いを計算できますし、実際 その作業コピーを最新にすることもできます。これにより、開発者は任意の時点 のプロジェクト履歴を見ることができ、非常に古い作業コピーを生き返らせるこ とができるのです。

厳密にはリポジトリは別の手段で同様の結果を出せたかもしれないですが、実際、 diff を保存するというのは必要な機能を実装するにはシンプルで直感的な方法 です。

この処理により、patch をうまく使えば、CVS はいつのファイルツリーでも再構 築できて、ある作業コピーの状態を任意の別の状態にすることができる、という おまけもついています。任意の特定の時刻のプロジェクトの状態をチェックアウ トすることができる、ということです。他の人の作業コピーに影響を与えずに、 任意の2つの状態の違いを diff のフォーマットで見ることもできます。

つまり、プロジェクト履歴にアクセスしやすくするために必要な機能そのものが、 分散していて調整しきれないけれど能力のある開発者チームがプロジェクトで共 同作業するためにも役立っているというわけです。

今はリポジトリのセットアップやユーザアクセス管理、CVS 特有のファイル形式 の詳しいところは省いていきます(それはRepository Administrationで述 べます)。ここでは作業コピーを変更するときの方法に集中しましょう。

まずは用語だけさっと説明しますね: