Copyright © 1999 Karl Fogel <kfogel@red-bean.com>

This document is free software; you can redistribute and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version.

This document is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

This manual describes how to use and administer CVS (Concurrent Versions System). It is part of a larger work entitled Open Source Development With CVS; please see the introduction for details.

This is version 1.12 of this manual.

Table of Contents


Node:Top, Next:, Up:(dir)

Top


Node:Translation, Next:, Previous:Top, Up:Top

日本語訳について

これをお読みのかたへ。

こんにちは。日本語訳を作成中のたけうちかほりです。

このドキュメントは Open Software Development with CVS という本の free chapters を訳している最中のものです。原書については http://cvsbook.red-bean.com/ を御参照ください。

現時点、この訳は校正や用語統一なんて程遠い、読み返しすらしてないよう なひどい訳ですので、書いてあることが正しいかどうか御自分でお確かめく ださいましね。まちがってたら即わたくし take-k@secom-sis.co.jp まで お知らせください。もとの英語はとても素直でわかりやすいものなので、日 本語があやしかったら迷わず原文英語を参照してください。 http://cvsbook.red-bean.com/

にあります。訳を配るときはいままでメールにつけてたんですけれど、だん だん大きくなってきたので仕方なく web に置きました。間違い満載の、迷 惑千万な訳だと思いますが、間違いを見つけたかたはわたくし たけうちか ほり take-k@secom-sis.co.jp までお知らせください。

下訳が済んでいるのは introduction と 2, 4, 6, 8, 9, 10 章です。それ 以外(ライセンス等)は英文のままです。

たけうちかほり <take-k@secom-sis.co.jp>

この日本語下訳の配布条件

原英文の配布条件に準じます。

以下に原文についている許諾文を付けます。GPL については GNU General Public License を参照してください。

Copyright © 1999 Karl Fogel <kfogel@red-bean.com>

This document is free software; you can redistribute and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version.

This document is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.


Node:Introduction, Next:, Previous:Translation, Up:Top

Introduction

この文書は、共同作業及びバージョン管理に CVS (Concurrent Versions System) を使うことについての、無料でオンライン提供された章です。 CVS のインストールと基本コンセプトから、進んだ使い方、管理まで全て をカバーしています。CVS を使う人、使おうとしている人向けです。

これらの章は Open Source Development With CVS (published by The Coriolis Group, ISBN 1-57610-490-7) からの抜粋です。この文書に収容されていない部分 (第1, 3, 5, 7章)には、CVS を使ったオープンソースプロジェクトを実 行する際の問題や考え方について述べてあります。

ここにある無料の章だけでも CVS の本として完結してはいますが、あな たがこれらを気に入って、本を買ってくれればよいなあと思います。 http://www.coriolis.com/bookstore/bookdetail.cfm?id=1576104907 で出版社に直接注文できます。

これらの章は GNU General Public License のもとにリリースされています。フリーソフトウェア についての情報一般は、http://www.gnu.org/ を見て下さい。特 に http://www.gnu.org/philosophy/free-sw.html

この文書についてのコメント、あるいは訂正があれば、bug-cvsb ook@red-bean.com までメールでお知らせ下さい。お知らせや更新情報 は http://cvsbook.red-bean.com/ をご覧ください。


Node:An Overview of CVS, Next:, Previous:Introduction, Up:Top

An Overview of CVS

I can't imagine programming without it... that would be like parachuting without a parachute!

-Brian Fitzpatrick on CVS

この章では CVS の基礎を紹介したあと、日常での CVS の使い方を詳しい案内付 きで見ていきます。概念が順々に示されますので、CVS が初めての人はこの章を 最初から最後まで飛ばさずに読破するのが一番いいでしょう。


Node:Basic Concepts, Next:, Up: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で述 べます)。ここでは作業コピーを変更するときの方法に集中しましょう。

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


Node:A Day With CVS, Next:, Previous:Basic Concepts, Up:An Overview of CVS

A Day With CVS

この節では CVS の基本的な操作を説明したあと、よくある CVS の使い方をカバー するような例を示します。ツアーが進むにつれて、 CVS が内部的にどう動いて いるか見ていくことにします。

CVS を使うだけなら CVS の実装の細かいところまで全部知っている必要はない のですが、どう動いているか基本的なところを知っていると、したいことを実現 するために一番良い方法を選ぶ際、役に立ちます。動作機構が全部丸見えだとい う点で、CVS は自動車より自転車に似ています。自転車のようにすぐ飛び乗れま すし。でも、ちょっと勉強してギアがどう動いているかわかれば、もっと効率よ く乗れるのです。(CVS の場合、その丸見えなところが熟考の末の設計決定なの か、たまたまそうなだけなのかわからないですが、フリーのプログラムはよくそ うなっています。外から見えるような実装というのはそのシステムが内部的にど う動いているか最初からさらすことになり、ユーザが開発者になって貢献してく れるようになりやすいという利点があります。)

ツアーの各パートは、それ以前のパートで得た知識を使うことになります。初め て読むかたは最初から始めて、飛ばさずに順番に読んでいくことをお勧めします。 下のメニューは繰り返し読む時の便宜のためにあるので、前のほうの章が分かっ ていないあいだに興味のある章へ飛ぶのに使ったりしないほうがいいと思います。


Node:Conventions Used In This Tour, Next:, Up:A Day With CVS

Conventions Used In This Tour

ツアーの舞台は Unix 環境です。CVS は Windows や Macintosh でも動きますし、 Ice Engineering の Tim Endres によって書かれた Java のクライアントもあり ますから Java の動くところならどこででも動きます。しかしここでは、現時点 及び潜在的な CVS ユーザの大部分が Unix のコマンドライン環境で作業してい ると仮定します、少々乱暴かもしれませんが。あなたがもしそうでなかったとし ても、ツアーのなかの例は簡単に他のインタフェースに読み替えることができる と思います。コンセプトさえ理解すればどんな CVS フロントエンドでも使いこ なせると思いますよ。(信じてください、わたしは何度もやってきたんです)

ツアー中の例はプログラミングのプロジェクトを追跡するために CVS を使う人 を対象に書きましたが、CVS の操作はソースコードだけでなくテキストドキュメ ントを扱う際にも適用できます。

また、すでに CVS がインストールされていて(フリーの Unix システムにはたい がいデフォルトで入っているので、あなたの知らないうちにインストールされて いることが多いでしょう)、リポジトリにアクセスできると仮定しています。環 境が整っていなくても、読むだけでも学ぶことは多いと思います。 Repository Administration を読めば CVS のインストールとリポジトリ のセットアップについて勉強できます。

CVS がインストール済みとして、オンラインマニュアルを探してみて下さい。著 者の Per Cederqvist にちなんで「Cederqvist」として親しまれているマニュア ルはソースディストリビューションに付属していて、普通だいたい最新のリファ レンスがあります。Texinfo 形式で書かれていて、Unix では Info ドキュメン トの構造のが読めると思います。コマンドラインの info プログラムで読めます し、

floss$ info cvs

Emacs のなかで Ctrl+H のあとに "i" をタイプしても読めます。どっちも動か ない場合はあなたのまわりの Unix グルに相談してください(または Repository Administration を参照してください、インストールについて 書いてあります)。 CVS をよく使うようになりたいなら、Cederqvist に詳しく なりたいと思うに違いありません。


Node:Invoking CVS, Next:, Previous:Conventions Used In This Tour, Up:A Day With CVS

Invoking CVS

CVS はひとつのプログラムですが、様々な動作をします: アップデート、コミッ ト、ブランチ、diff 取り、などなど。CVS を起動する時はどの動作をさせるか 指定します。起動時の形式は次の通りです:

floss$ cvs command

例えば

floss$ cvs update
floss$ cvs diff
floss$ cvs commit

などなど。(まだ上のコマンドを実行しちゃいけませんよ、作業コピーの中じゃ なくちゃ意味ありませんから。すぐに出てきますからガマンしてください)

CVS もコマンドもオプションが書けます。CVS の振舞いに影響のあるオプション (コマンドの動作とは独立)は「グローバルオプション」と呼ばれます。コマンド 用のオプションは「コマンドオプション」と呼ばれます。グローバルオプション は常にコマンドより左側に書かれ、コマンドオプションはコマンドの右側に書か れます。つまり

floss$ cvs -Q update -p

-Q はグローバルオプションで、-p はコマンドオプションですね。(好奇心旺盛 なアナタのために: -Q は「quietly」という意味で、お知らせ出力を抑制し、何 らかの理由でコマンドが完了しなかった場合のエラーメッセージのみを表示しま す。-p は update の結果をファイルではなく標準出力に送るという意味です)


Node:Accessing A Repository, Next:, Previous:Invoking CVS, Up:A Day With CVS

Accessing A Repository

CVS にはアクセスしたいリポジトリを前もって知らせてやらなければなりません。 もうチェックアウトしたんなら関係ないです - 作業コピーはすべて、自分がど のリポジトリからチェックアウトされたものかがわかっていますから、CVS はそ の作業コピーのリポジトリを自動的に推定します。ここではとりあえずまだ作業 コピーを作っていないと仮定しましょう、そうすると CVS に明示的にどこを見 に行けばいいか指定する必要があるのです。これはグローバルオプション -d で 指定できます(-d は directory を意味します、歴史的経緯があってこの略称な のですが、「repository」の -r のほうがよかったと思いますよね)。そのあと にリポジトリのパスを書きます。リポジトリがローカルの /usr/local/cvs (標 準的な場所です)にあるとすると、こうです:

floss$ cvs -d /usr/local/cvs command

しかし、リポジトリはネットワーク越しの別のマシン上にあることが多いです。 CVS ではネットワーク経由でアクセスする方法を選択できます。どれを使えばい いかはリポジトリマシン(以降「サーバ」と呼びます)がどの程度セキュリティを 必要としているかによります。サーバのいろいろなリモートアクセス方法を設定 するについては Repository Administration に述べてあります。 ここで はクライアント側についてだけ話しましょう。

幸い、リモートアクセスを起動するにはすべて共通の文法を使います。ローカル のリポジトリでなくリモートのリポジトリを指定するには、長めのリポジトリパ スを使えばよいのです。まずアクセス方法の名前をコロンで囲んだものを書き、 次にユーザ名とサーバ名を @ でつなげて書きます。またコロンを書き、最後に サーバ上のリポジトリのパスを書きます。

pserver (password-authenticated server) アクセスについてみてみましょ う。

floss$ cvs -d :pserver:jrandom@cvs.foobar.com:/usr/local/cvs login
(Logging in to jrandom@cvs.foobar.com)
CVS password: (enter your CVS password here)
floss$

-d に続く長いリポジトリパスは、「pserver アクセスを使って、ユーザ名 jrandom、サーバは cvs.foobar.com で /usr/local/cvs というリポジトリを持っ ているからね」ということをCVS に知らせています。ホスト名は別に "cvs.something.com" である必要はありません、ただの慣習です。but it could just as easily have been:

floss$ cvs -d :pserver:jrandom@fish.foobar.org:/usr/local/cvs command

このコマンドはログインを実行し、あなたがこのリポジトリで作業する権限があ るかどうか確認します。パスワードプロンプトを出し、次にパスワードが正しい かどうかサーバにたずねます。Unix の慣習に従い、ログインが成功したら何の メッセージもなしに終わります。失敗したら(たとえばパスワードが間違ってい るなどの理由で)、エラーメッセージを表示します。

ある CVS サーバに対しては、ログインは一度しかする必要がありません。ログ インが成功すると、CVS はホームディレクトリの .cvspass というファイルにパ スワードを保存します。pserver メソッドを経由してリポジトリにアクセスする 際にはそのファイルからパスワードを持ってくるので、初回 CVS サーバにアク セスする時のみログインすればよいのです(各クライアント毎)。もちろんパスワー ドが変更になった時にはいつでも再度 cvs login を走らせることができます。

Note: pserver はこのような初回ログインが必要な唯一のアクセス方法です。 他の方法は普通の CVS コマンドを即実行することができます。

いったん .cvspass に認証情報を保存すれば、他の CVS コマンドも同じ文法で 動きます:

floss$ cvs -d :pserver:jrandom@cvs.foobar.com:/usr/local/cvs command

Windows で pserver を使うには手順をもう一つ踏みます。Windows ではホーム ディレクトリの概念がないため、CVS はどこに .cvspass を置いてよいか分かり ません。場所を教えてあげましょう。C: ドライブのルートを指定するのが普通 です:

C:\WINDOWS> set HOME=C:
C:\WINDOWS> cvs -d :pserver:jrandom@cvs.foobar.com:/usr/local/cvs login
(Logging in to jrandom@cvs.foobar.com)
CVS password: (enter password here)
C:\WINDOWS>

ファイルシステム中のどこでも構いません。ネットワークドライブを使うのは避 けたいと思うかもしれませんね、そのドライブにアクセスできる人に .cvspass を見られてしまいますから。

CVS では pserver のほかに ext (外部接続プログラム利用)、kserver(ケルベロ スセキュリティシステムバージョン4利用)、gserver(GSSAPI(Generic Security Services API、ケルベロスバージョン5以上を扱う)利用)の各方法をサポートし ています。これらは pserver と同じように使えますが、それぞれに特質があり ます。

このなかでは ext が最もよく使われている方法でしょう。サーバに rsh か ssh でログインできるなら、ext が使えます。次のようにテストでき ます:

floss$ rsh -l jrandom cvs.foobar.com
Password: enter your login password here

はい、rsh でログインログアウトができるとします。クライアントマシンに戻っ て次をどうぞ:

floss$ CVS_RSH=rsh; export CVS_RSH
floss$ cvs -d :ext:jrandom@cvs.foobar.com:/usr/local/cvs command

最初の行は(Unix のボーンシェルで書いてあります)、CVS_RSH 環境変数に rsh を設定して、接続に rsh を使うことを CVS に指示します。2番目の行は任意の CVS コマンドが書けます。パスワードを入力するよう促され、CVS はサーバにロ グインします。

Cシェルを使っている人はこれをやってみて下さい:

floss% setenv CVS_RSH rsh

and for Windows, try this:

C:\WINDOWS> set CVS_RSH=rsh

これ以降ツアーではボーンシェルで書きますので、あなたの環境に合わせて読み 替えて下さい。

rsh のかわりに ssh (セキュアシェル)を使う場合、 CVS_RSH を適切に設定する だけです:

floss$ CVS_RSH=ssh; export CVS_RSH

設定する値が ssh にもかかわらず変数名が CVS_RSH だというのを見過ごさない ように。歴史的な理由でこうなっているのです(Unix ではこれさえ言えば何でも 許されるんですよネ)。CVS_RSH には、リモートサーバにログインできて、コマ ンドを走らせることができて、出力を受け取ることができるプログラムなら何で も指定できます。rsh 以降、この手のプログラムはほかにもありますが、ssh が 最もポピュラーです。注意点として、このプログラムはデータストリームを書き 換えてはならないということが挙げられます。この点で Windows NT の rsh は 不合格です。DOS と Unix の改行コードを変換してしまうからです。 Windows 用のほかの rsh を使うか、その他のアクセス方法を使って下さい。

gserver と kserver は他に比べてあまり使われませんのでここでは説明しませ ん。今までに説明した方法とよく似ています。詳しくは Cederqvist を参照のこ と。

ひとつのリポジトリしか使わないのなら毎回 -d リポジトリ とか打つのはイヤ でしょう、CVSROOT 環境変数を設定してください(これも CVSREPOS という名前 のほうがよかったと思いますが、今となってはもう遅いです):

floss$ CVSROOT=/usr/local/cvs
floss$ export CVSROOT
floss$ echo $CVSROOT
/usr/local/cvs
floss$

またはこんな感じです:

floss$ CVSROOT=:pserver:jrandom@cvs.foobar.com:/usr/local/cvs
floss$ export CVSROOT
floss$ echo $CVSROOT
:pserver:jrandom@cvs.foobar.com:/usr/local/cvs
floss$

以降では CVSROOT にリポジトリの場所を指定していると仮定しますので、例に は -d オプションは書きません。いろいろなリポジトリを使う場合は、CVSROOT を設定せずに -d リポジトリ と指定して下さい。


Node:Starting A New Project, Next:, Previous:Accessing A Repository, Up:A Day With CVS

Starting A New Project

既に CVS の管理下にあるプロジェクト(もうそのプロジェクトがリポジトリのど こかにあるということです)で作業するために CVS を勉強中のあなたはきっと、 この節を飛ばして次の「Checking Out A Working Copy」を読みたいだろうと思 います。この節は、ソースコードがあって、それを CVS の管理下に置きたいあ なたにぴったりです。既にリポジトリにアクセスできると仮定して進めます、リ ポジトリ自体の設定をするには Repository Administration を参照して 下さい。

CVS に新しいプロジェクトを入れるのは インポート(import) といいます。 CVS コマンドは、今あなたが考えた通り、こうです:

floss$ cvs import

コマンドが成功するためにはもう少しオプションが必要ですけれど(あと、正し い場所で実行する必要があります)。さて、まずあなたのプロジェクトのトップ レベルディレクトリに移って下さい:

floss$ cd myproj
floss$ ls
README.txt  a-subdir/   b-subdir/   hello.c
floss$

プロジェクトには、トップレベルにファイルが2つ - README.txt と hello.c - と、サブディレクトリが2つ - a-subdir と b-subdir - と、それらの下 のファイルがいくつか(例には示されていませんが)あります。プロジェクトを インポートする時、CVS はカレントディレクトリから始めて、ツリーのなかの 全てをインポートします。ですから、プロジェクトのパーツになるファイルだ けがツリーのなかにあることを確認して下さい。バックアップファイルとか走 り書きのファイルとかは全部掃除しておいてください。

import コマンドの一般的な書き方はこうです:

floss$ cvs import -m "log msg" projname vendortag releasetag

-m フラグ(message)にはそのインポートを説明する短いメッセージを指定します。 プロジェクト全体の最初のログメッセージになります。以降のコミット毎にそれ ぞれログメッセージが追加されます。これらのメッセージは必須です。- m フラ グを指定しない場合 CVS は自動的にエディタを立上げて(EDITOR 環境変数を見 ます)、ログメッセージをタイプさせられます。ログメッセージを保存してエディ タを抜けてから import は続行します。

次の引数はプロジェクトの名前です(ここでは "myproj" を使います)。チェック アウトする時に、この名前でもってリポジトリからプロジェクトをチェックアウ トします。(実際に何が起こるかというと、リポジトリの中にこの名前のディレ クトリが作成されるのですが、詳しくは Repository Administration を 参照のこと) カレントディレクトリと同じ名前である必要はありません。まあ、 そうするのが普通みたいですけども。

vendortag と releasetag 引数は CVS の図書管理に必要なのですが、今はきに しないで下さい。あなたが使うにはほとんど関係ありませんから。これらが重要 になる情況について(ほとんどないですが)は Advanced CVS を読んで下さ い。いまはとりあえず、この引数にはユーザ名と "start" を使うことにします。

さて、import を起動する準備ができました:

floss$ cvs import -m "initial import into CVS" myproj jrandom start
N myproj/hello.c
N myproj/README.txt
cvs import: Importing /usr/local/cvs/myproj/a-subdir
N myproj/a-subdir/whatever.c
cvs import: Importing /usr/local/cvs/myproj/a-subdir/subsubdir
N myproj/a-subdir/subsubdir/fish.c
cvs import: Importing /usr/local/cvs/myproj/b-subdir
N myproj/b-subdir/random.c

No conflicts created by this import
floss$

おめでとう! このコマンドを走らせたことで、リポジトリに実際に影響のあるこ とをついになしとげたことになるわけです。

import コマンドの出力を読むと、CVS がファイル名の前に何か1文字を出力して いることに気づきますね。この場合、"N" は「新しいファイル (new file)」と いう意味です。左側に1文字つけてステータスを表すのは、CVS の出力では一般 的なパターンです。あとで、チェックアウトとアップデートのときにも見ること になると思います。

たぶんあなたはこう考えるでしょう、さてプロジェクトをインポートしたわけだ、 すぐ作業を始めてもいいんだよね、と。いえいえ違うんです、ハズレ。カレント ディレクトリはまだ CVS の作業コピーではありません。これがインポートの元 になったのは事実ですが、インポートされただけで CVS の作業コピーにヘンシー ン、するわけではないのです。作業コピーを手に入れるためにはリポジトリから チェックアウトする必要があります。

でも、まずは今のそのプロジェクトツリーを保存しておきたいんじゃないかと思 います。いったん CVS にソースを入れたら、バージョン管理していないコピー を間違えて編集してしまって混乱するのはイヤでしょうからね(そういう変更は プロジェクト履歴に格納されませんから)。現時点以降の編集は全部作業コピー でやりたいだろうと思います。しかしリポジトリにちゃんと入っているかどうか を確認もせずに、インポートしたツリーをいきなり削除するのは不安でしょう。 もちろん 99.999% 確実だとは思うけれども(だって import コマンドはエラーも 返さなかったし)、だからって危ない橋をわざわざ渡らなくてもいいですよね。 注意しすぎても損はない、というのは、どんなプログラマだって知っていること です。こういう風にしてください:

floss$ ls
README.txt  a-subdir/   b-subdir/   hello.c
floss$ cd ..
floss$ ls
myproj/
floss$ mv myproj was_myproj
floss$ ls
was_myproj/
floss$

はい、これでどうでしょう。オリジナルのファイルは保存されているし、もう使 われないバージョンだというのが名前から明らかに分かりますから作業コピーと 間違えることもないでしょう。これでチェックアウトの用意ができました。


Node:Checking Out A Working Copy, Next:, Previous:Starting A New Project, Up:A Day With CVS

Checking Out A Working Copy

プロジェクトをチェックアウトするコマンドは、そう、今あなたが考えているの で合っています:

floss$ cvs checkout myproj
cvs checkout: Updating myproj
U myproj/README.txt
U myproj/hello.c
cvs checkout: Updating myproj/a-subdir
U myproj/a-subdir/whatever.c
cvs checkout: Updating myproj/a-subdir/subsubdir
U myproj/a-subdir/subsubdir/fish.c
cvs checkout: Updating myproj/b-subdir
U myproj/b-subdir/random.c

floss$ ls
myproj/      was_myproj/
floss$ cd myproj
floss$ ls
CVS/        README.txt  a-subdir/   b-subdir/   hello.c
floss$

ほら、初めての作業コピーですよ! 中身はインポートした時と全く同じ、ただし CVS という名前のサブディレクトリができています。CVS がバージョン管理情報 を格納しているのです。実際、プロジェクト中の各ディレクトリがそれぞれ CVS サブディレクトリを持っています:

floss$ ls a-subdir
CVS/        subsubdir/  whatever.c
floss$ ls a-subdir/subsubdir/
CVS/    fish.c
floss$ ls b-subdir
CVS/      random.c

CVS が CVS という名前のサブディレクトリの中にリビジョン情報を格納してい るということは、プロジェクトの中に CVS という名前のサブディレクトリを含 めることができないということです。実用上これが問題になったという話は聞い たことがありません。

ファイルを編集する前に、ブラックボックスの中身を覗いてみましょう:

floss$ cd CVS
floss$ ls
Entries     Repository  Root
floss$ cat Root
/usr/local/cvs
floss$ cat Repository
myproj
floss$

ナゾなことはなにもありませんね。Root ファイルはリポジトリの場所を示し、 Repository ファイルはプロジェクトがリポジトリ内のどこにあるかを示してい ます。ちょっと混乱するかもしれません、説明します。

CVS の用語はもう長いこと混乱しています。「リポジトリ」という語は違う2つ のものを指すのに使われます。ある時はリポジトリのルートディレクトリ(例え ば /usr/locla/cvs)を意味します。これはたくさんのプロジェクトを含んでいま す。Root ファイルはこちらを指しています。しかし他の場合は、リポジトリルー ト内にある、特定のプロジェクトのサブディレクトリ(例えば /u sr/local/cvs/myproj, /usr/local/cvs/yourproj, /usr/local/cvs/fish)を意味 することもあります。CVS サブディレクトリ内の Repository ファイルは後者の 意味をとるわけです。

この本で「リポジトリ」というとき、普通は Root(トップレベルリポジトリ) を 意味しますが、時々はプロジェクトのサブディレクトリという意味で使う時もあ ります。文脈からその意味が読み取れない場合には、文章で明らかにします。 Repository ファイルに書かれているパスは時々、相対パスではなくプロジェク トの絶対パスになっていることがあるので注意して下さい。この場合、Root ファ イルが少し冗長になります:

floss$ cd CVS
floss$ cat Root
:pserver:jrandom@cvs.foobar.com:/usr/local/cvs
floss$ cat Repository
/usr/local/cvs/myproj
floss$

Entries ファイルはプロジェクト内の各ファイルについての情報を保持していま す。1行につき1ファイルで、直下のファイルとサブディレクトリの情報だけが書 いてあります。myproj にある CVS/Entries ファイルを示します:

floss$ cat Entries
/README.txt/1.1.1.1/Sun Apr 18 18:18:22 1999//
/hello.c/1.1.1.1/Sun Apr 18 18:18:22 1999//
D/a-subdir////
D/b-subdir////

各行のフォーマットはこうです:

/filename/revision number/last modification date//

ディレクトリの行は最初に "D" とあります。(CVS はディレクトリの変更履歴は 保存しないので、その行のリビジョン番号とタイムスタンプは空になります)

タイムスタンプは最終更新の日付と時刻を記録します(地方時ではなく Universal Time)。CVS はこれのおかげで、最後のチェックアウト、アップデー ト、またはコミットの時点以降に、あるファイルが更新されたかどうかをすぐ知 らせられるわけです。ファイルシステム中のタイムスタンプが CVS/Entries ファ イル中のタイムスタンプと違っていれば(わざわざリポジトリを見に行かなくと も)、そのファイルが更新されたんだろうというのがわかるのです。

サブディレクトリ中の CVS/* ファイルを見てみましょう。

floss$ cd a-subdir/CVS
floss$ cat Root
/usr/local/cvs
floss$ cat Repository
myproj/a-subdir
floss$ cat Entries
/whatever.c/1.1.1.1/Sun Apr 18 18:18:22 1999//
D/subsubdir////
floss$

ルートリポジトリは変わっていませんが、Repository ファイルにはプロジェク トのこのサブディレクトリの場所が書いてあって、Entries ファイルの内容も違 うのが分かります。

インポートの直後は、プロジェクト中のどのファイルのリビジョン番号も全部 1.1.1.1 です。最初のリビジョン番号はちょっと特殊なので、そのへんはあとに しましょう、変更してコミットしてみてからリビジョン番号についてみていく予 定です。


Node:Version Versus Revision, Next:, Previous:Checking Out A Working Copy, Up:A Day With CVS

Version Versus Revision

CVS が各ファイル用に保持している内部リビジョン番号は、そのファイルが構成 するソフトウェアのバージョン番号とは関係がありません。例えばあなたのプロ ジェクトにファイルが3つあるとして、その内部リビジョン番号は 1999/5/3 の 時点で 1.2, 1.7, 2.48 だとします。その日、そのソフトをパッケージングして SlickoSoft バージョン3としてリリースします。これは純粋にマーケティングの 決定であり、CVS のリビジョンには全く影響しません。CVS のリビジョン番号は お客様には見えないものです(リポジトリを見せない限り)。公に見える番号は 「バージョン3」の「3」だけです。CVS に関してのみ言えば、それをたとえばバー ジョン1729と呼んだって構わないわけです。バージョン番号(リリース番号でも いいですが)は CVS の内部の変更追跡には何の影響もありません。

混乱を避けるため、「リビジョン」という単語は CVS 管理下にあるファイルの 内部リビジョン番号だけを指すために使います。ああ、CVS のことは「バージョ ン管理システム」と呼ぶかもしれません、「リビジョン管理システム」っていう のはなんだかかっこわるいですからね。


Node:Making A Change, Next:, Previous:Version Versus Revision, Up:A Day With CVS

Making A Change

このプロジェクトは特にたいしたことはできません。hello.c の内容を示します:

floss$ cat hello.c
#include <stdio.h>

void
main ()
{
   printf ("Hello, world!\n");
}

プロジェクトをインポートして以来、初の変更を加えましょう。次の行を加えま す:

printf ("Goodbye, world!\n");

Hello, world! のあとにです。お好みのエディタを起動して変更してください:

floss$ emacs hello.c
  ...

今回のこれはしごく単純な変更ですからまあ忘れたりしないと思います。でも、 もっと大きくて複雑なプロジェクトになると、ファイルを編集したあとに他のこ とにジャマされて、数日後に戻ってきた時にはもう何をやったか思い出せないで しょうし、ひょっとすると、何も変更してないと思ってしまうかもしれません。 このときにリポジトリと作業コピーを比べてみて初めて、「CVS は命の恩人です」 という状況になるのです。


Node:Finding Out What You (And Others) Did -- update And diff, Next:, Previous:Making A Change, Up:A Day With CVS

Finding Out What You (And Others) Did - update And diff

前に、リポジトリから作業コピーへ変更を持ってくる方法として、アップデート のことを述べました。これは他の人の変更を取得する方法です。でもアップデー トというのは本当はもう少し複雑なことをしています: 作業コピーの状態全てを、 リポジトリ内のプロジェクトの状態と比較します。チェックアウト時以降、リポ ジトリに何も変更がなくても作業コピーが変更されていれば、アップデートはそ れも表示します:

floss$ cvs update
cvs update: Updating .
M hello.c
cvs update: Updating a-subdir
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir

hello.c の隣にある「M」は、最後のチェックアウト以降このファイルが変更さ れました、そしてその変更はまだリポジトリへはコミットされていません、とい う意味です。

どのファイルを編集したんだったかをただ知りたいなと思うだけのこともあるで しょう。しかしどんな変更を施したのか詳しく見たいときには、diff 形式のフ ルレポートを取得することもできます。diff コマンドは作業ファイル中の変更 されたであろうファイルと、対応するリポジトリ中のファイルを比較し、全ての 相違を表示します:

floss$ cvs diff
cvs diff: Diffing .
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.1.1.1
diff -r1.1.1.1 hello.c
6a7
>   printf ("Goodbye, world!\n");
cvs diff: Diffing a-subdir
cvs diff: Diffing a-subdir/subsubdir
cvs diff: Diffing b-subdir

That's helpful, if a bit obscure, but there's still a lot of cruft in the output. ビギナーの人は最初の数行は無視して構いません。リポジトリ内のファ イル名と、最後にチェックインされたリビジョンの番号が書かれています。 他 の状況では役に立つ情報なんですが(あとで少し詳しく見ていきます)、作業コピー にどんな変更があったかを知りたいだけなら必要のないものです。

diff を読む時にもっと障害になっているのは、CVS がアップデート中に各ディ レクトリに入ったことを知らせている部分です。そのコマンドがどのくらい長く かかったかわかるので、大きいプロジェクトの長いアップデートでなら役に立ち ますが、今回の場合、ただ diff を読みにくくしているだけです。-Q グローバ ルオプションで CVS に静かに仕事しろと言ってみましょう。

floss$ cvs -Q diff
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.1.1.1
diff -r1.1.1.1 hello.c
6a7
>   printf ("Goodbye, world!\n");

いいカンジ、少なくとも cruft はいくつかなくなりました。でも、この diff はまだ見にくいですね。6行目に新しい行が追加されて(7行目になって)、内容は 次のようです:

printf ("Goodbye, world!\n");

diff の最初の「>」は、この行は新しいほうのバージョンにあって、古いほうに はない、ということを示します。

でも、このフォーマットはもう少し読みやすいようにできるんじゃないでしょう か。多くの人はコンテキスト diff のほうが読みやすいというのを知っていると 思います。あれは変更の周りの文脈を数行示してくれますからね。コンテキスト diff は diff コマンドに -c フラグを渡せば生成できます:

floss$ cvs -Q diff -c
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 hello.c
*** hello.c     1999/04/18 18:18:22     1.1.1.1
--- hello.c     1999/04/19 02:17:07
***************
*** 4,7 ****
---4,8 --
  main ()
  {
    printf ("Hello, world!\n");
+   printf ("Goodbye, world!\n");
  }

やっと分かり易くなった! コンテキスト diff を読み慣れていなくてもこの出力 を見れば一目で何が起こったか分かると思います。新しい行が(最初の行の + は 追加行を示します)、Hello, world! と最後の中括弧の間に追加されたのです。

コンテキスト diff を完璧に読みこなす必要はありませんが(それは patch コマ ンドがやることです)、そのフォーマットにちょっと親しむだけの時間を取ったっ て少なくとも損はないでしょう。cruft は飛ばして、最初の2行は

*** hello.c     1999/04/18 18:18:22     1.1.1.1
--- hello.c     1999/04/19 02:17:07

何と何の diff を取ったかを書いてあります。この場合は hello.c のリビジョ ン 1.1.1.1 と、同じファイルの変更されたバージョンです(2行目のほうにはリ ビジョン番号はありませんが、これは作業ファイルだけに施された変更であって リポジトリにはまだコミットされていないからです) 。これ以降 diff 内に出て くるアスタリスクとダッシュの行はセクションを識別しています。行番号範囲を 埋め込んであるアスタリスクの行はオリジナルファイルのセクションを示します。 ダッシュの行、さっきとは違う行範囲が埋め込んであると思いますが、これは変 更されたファイルのセクションを示します。これらのセクションは対比されて 「hunk」というペアになり、一方は古いファイル、他方は新しいファイルになり ます。

今回の diff には hunk がひとつだけあります:

***************
*** 4,7 ****
--- 4,8 --
  main ()
  {
    printf ("Hello, world!\n");
+   printf ("Goodbye, world!\n");
  }

hunk の最初のセクションは空で、オリジナルのファイルからは何も削除されて いないことを意味します。2番目のセクションは、新しいファイルの対応する場 所に1行追加されたことを示します。「+」という印がつけてあります。(diff が ファイルから抜粋をする時は、最初の2カラムは「+」とかの特別なコードのため に空けてあります。そのため、ただ抜粋しているだけの行は空白2つでインデン トされているように見えます。この余分なインデントは diff が適用される時に は削除されます、当たり前ですけど)

行番号範囲は、その hunk がカバーする範囲です(コンテキストを示す行を含む)。 オリジナルファイルではその hunk は4行目から7行目までだったのに対し、新し いファイルでは4行目から8行目になっています(1行追加されましたからね)。オ リジナルファイルから何も削除されていない場合、diff はオリジナルファイル の行を出力する必要がないことに注意して下さい。行範囲と hunk の後半からわ かることです。

わたしの実際のプロジェクトから、他のコンテキスト diff をお見せしましょう:

floss$ cvs -Q diff -c
Index: cvs2cl.pl
===================================================================
RCS file: /usr/local/cvs/kfogel/code/cvs2cl/cvs2cl.pl,v
retrieving revision 1.76
diff -c -r1.76 cvs2cl.pl
*** cvs2cl.pl   1999/04/13 22:29:44     1.76
--- cvs2cl.pl   1999/04/19 05:41:37
***************
*** 212,218 ****
          # can contain uppercase and lowercase letters, digits, '-',
          # and '_'. However, it's not our place to enforce that, so
          # we'll allow anything CVS hands us to be a tag:
!         /^\s([^:]+): ([0=9.]+)$/;
          push (@{$symbolic_names{$2}}, $1);
        }
      }
-- 212,218 --
          # can contain uppercase and lowercase letters, digits, '-',
          # and '_'. However, it's not our place to enforce that, so
          # we'll allow anything CVS hands us to be a tag:
!         /^\s([^:]+): ([\d.]+)$/;
          push (@{$symbolic_names{$2}}, $1);
        }
      }

びっくりマーク(「!」)は、その行が古いファイルと新しいファイルで違うこと を示します。「+」も「-」もないことから、ファイルの行数は変わらなかったこ とが分かります。

同じプロジェクトからもう一つ別のコンテキスト diff を。今回はもう少し複雑 です:

floss$ cvs -Q diff -c
Index: cvs2cl.pl
===================================================================
RCS file: /usr/local/cvs/kfogel/code/cvs2cl/cvs2cl.pl,v
retrieving revision 1.76
diff -c -r1.76 cvs2cl.pl
*** cvs2cl.pl   1999/04/13 22:29:44     1.76
--- cvs2cl.pl   1999/04/19 05:58:51
***************
*** 207,217 ****
}
        else    # we're looking at a tag name, so parse & store it
        {
-         # According to the Cederqvist manual, in node "Tags", "Tag
-         # names must start with an uppercase or lowercase letter and
-         # can contain uppercase and lowercase letters, digits, '-',
-         # and '_'. However, it's not our place to enforce that, so
-         # we'll allow anything CVS hands us to be a tag:
          /^\s([^:]+): ([0-9.]+)$/;
          push (@{$symbolic_names{$2}}, $1);
        }
- 207,212 --
***************
*** 223,228 ****
--- 218,225 --
      if (/^revision (\d\.[0-9.]+)$/) {
        $revision = "$1";
      }
+
+     # This line was added, I admit, solely for the sake of a diff example.

      # If have file name but not time and author, and see date or
      # author, then grab them:

この diff には hunk が2つあります。最初のやつは5行削除です(これらの行は hunk の最初のセクションだけに示されていて、2番目のセクションの行番号は5 行少なくなっています)。途切れていないアスタリスクの行は hunk の区切りで、 2番目の hunk では2行追加されたことが分かります。空行ひとつと無意味なコメ ントが1行ですね。一つ前の hunk を受けて行番号がどう変わっているか、注意 して下さい。オリジナルファイルにおいては2番目の hunk は223行目から228行 目、最初の hunk で5行削除されたので新しいファイルでは218から225行目になっ ています。

おめでとう、これでもうあなたも diff を読むことにかけてはエキスパートです ね。


Node:CVS And Implied Arguments, Next:, Previous:Finding Out What You (And Others) Did -- update And diff, Up:A Day With CVS

CVS And Implied Arguments

今まで述べてきた各 CVS コマンドでは、コマンドラインでファイルの指定をし ていないことに気づいたと思います。例えば

floss$ cvs diff

を走らせましたね、

floss$ cvs diff hello.c

ではなくて。また、

floss$ cvs update

を走らせましたよね、

floss$ cvs update hello.c

ではなくて。ここでの原則は、ファイルを指定しない場合、CVS はそのコマンド で適用できる限りの全てのファイルに対して動作する、ということです。この原 則はカレントディレクトリ以下のサブディレクトリ内のファイルも含みます。 CVS はカレントディレクトリ以下のツリーを自動的に降りていきます。例えば b-subdir/random.c と a-subdir/subsubdir/fish.c を変更したとすると、結果 は次のようになるでしょう:

floss$ cvs update
cvs update: Updating .
M hello.c
cvs update: Updating a-subdir
cvs update: Updating a-subdir/subsubdir
M a-subdir/subsubdir/fish.c
cvs update: Updating b-subdir
M b-subdir/random.c
floss$

いや、こっちのほうがいいかな:

floss$ cvs -q update
M hello.c
M a-subdir/subsubdir/fish.c
M b-subdir/random.c
floss$

-q は -Q のちょっと弱いヤツです。もし -Q を使ったとしたら何も出力されな いでしょう。変更情報は必須でないメッセージだとみなされてしまうからです。 小文字の -q を使うと制限が弱まります。要らないと思うようなメッセージは抑 制されて、確かで役に立ちそうなメッセージは出力されます。

アップデートでファイルを指定することもできます:

floss$ cvs update hello.c b-subdir/random.c
M hello.c
M b-subdir/random.c
floss$

こうすると CVS は指定されたファイルだけを調べて、他のは無視します。

実際のところはファイルを限定しないでコマンドを走らせるほうが普通です。 ほとんどの場合、ディレクトリツリー全体を一度にアップデートしたいことが多 いです。ここでやっているアップデートは、ローカルで変更されたファイルを表 示するだけであることを思い出して下さい。リポジトリにはまだ何の変更も加え られていないですからね。プロジェクトで他の人と一緒に作業している場合には、 適宜アップデートを走らせてリポジトリの変更を自分の作業コピーに取り入れて いくわけですが、その場合にはアップデートしたいファイル名を指定するという のは少しは役に立つでしょう。

同じ原則が CVS のほかのコマンドにもあてはまります。例えば diff ですが、 ひとつのファイルの変更だけ見るということができます。

floss$ cvs diff -c b-subdir/random.c
Index: b-subdir/random.c
===================================================================
RCS file: /usr/local/cvs/myproj/b-subdir/random.c,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 random.c
*** b-subdir/random.c   1999/04/18 18:18:22     1.1.1.1
--- b-subdir/random.c   1999/04/19 06:09:48
***************
*** 1 ****
! /* A completely empty C file. */
--- 1,8 --
! /* Print out a random number. */
!
! #include <stdio.h>
!
! void main ()
! {
!   printf ("a random number\n");
! }

また、全ての変更を一度に見るというのもできます(ちょっと大きい diff だけ ど、席から離れないで):

floss$ cvs -Q diff -c
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 hello.c
*** hello.c     1999/04/18 18:18:22     1.1.1.1
--- hello.c     1999/04/19 02:17:07
***************
*** 4,7 ****
--- 4,8 --
  main ()
  {
    printf ("Hello, world!\n");
+   printf ("Goodbye, world!\n");
  }
Index: a-subdir/subsubdir/fish.c
===================================================================
RCS file: /usr/local/cvs/myproj/a-subdir/subsubdir/fish.c,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 fish.c
*** a-subdir/subsubdir/fish.c   1999/04/18 18:18:22     1.1.1.1
--- a-subdir/subsubdir/fish.c   1999/04/19 06:08:50
***************
*** 1 ****
! /* A completely empty C file. */
--- 1,8 --
! #include <stdio.h>
!
! void main ()
! {
!   while (1) {
!     printf ("fish\n");
!   }
! }
Index: b-subdir/random.c
===================================================================
RCS file: /usr/local/cvs/myproj/b-subdir/random.c,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 random.c
*** b-subdir/random.c   1999/04/18 18:18:22     1.1.1.1
--- b-subdir/random.c   1999/04/19 06:09:48
***************
*** 1 ****
! /* A completely empty C file. */
--- 1,8 --
! /* Print out a random number. */
!
! #include <stdio.h>
!
! void main ()
! {
!   printf ("a random number\n");
! }

とにかく、diff を見てわかるように、このプロジェクトは明らかに prime time の準備ができました。リポジトリに変更をコミットしてみましょう。


Node:Committing, Next:, Previous:CVS And Implied Arguments, Up:A Day With CVS

Committing

commit コマンドは、リポジトリに変更を送ります。ファイルを指定しな ければ、変更の全てがリポジトリに送られます。それが嫌なら、1つかそれ以上 のファイル名を指定してコミットすることもできます(その場合他のファイルは 無視されます)。

ここでは、1つのファイルを指定してコミット、2つのファイルを推測させてコミッ トしてみます:

floss$ cvs commit -m "print goodbye too" hello.c
Checking in hello.c;
/usr/local/cvs/myproj/hello.c,v  <--  hello.c
new revision: 1.2; previous revision: 1.1
done
floss$ cvs commit -m "filled out C code"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in a-subdir/subsubdir/fish.c;
/usr/local/cvs/myproj/a-subdir/subsubdir/fish.c,v  <--  fish.c
new revision: 1.2; previous revision: 1.1
done
Checking in b-subdir/random.c;
/usr/local/cvs/myproj/b-subdir/random.c,v  <--  random.c
new revision: 1.2; previous revision: 1.1
done
floss$

ちょっと時間を取って、出力を注意して読んで下さい。ほとんどが自己説明的で す。リビジョン番号がインクリメントされていることに気づくと思います(思っ た通りだ)。でもオリジナルのリビジョンは 1.1 になっていて、以前 Entries ファイルで見た 1.1.1.1 ではないですね。

ここでこの食い違いについて説明しますが、まああまり重要なことではありませ ん。これは、CVS が 1.1.1.1 に特別な意味を持たせていることに関係していま す。多くの場合、「ファイルはインポート時にリビジョン番号1.1を受け取る」 といっても構わないのですが、初回コミットが起こるまで、Entries ファイルに はリビジョン番号 1.1.1.1 が示されています(その理由は CVS のみぞ知る)。


Node:Revision Numbers, Next:, Previous:Committing, Up:A Day With CVS

Revision Numbers

プロジェクト中の各ファイルはそれぞれリビジョン番号というのを持っています。 ファイルがコミットされるとリビジョン番号の最後のところが1増えます。 従っ て、プロジェクトを構成するいろいろなファイルは、任意の時点でそれぞれ全然 違うリビジョン番号を持つことになります。これはただ、あるファイルは他のファ イルよりも多く変更され(コミットされ)た、ということを意味するだけです。

(あなたはきっと、変更のたびに小数点の右側が変わるとすると、じゃあ左側の 部分は何なんだろう、と思うことでしょう。実際、CVS が左側の数字を自動的に 増やすことはなく、ユーザのリクエストによって増やすことになります。 ほと んど使われない機能なのでこのツアーでは説明しません。)

ここまで使ってきた例のプロジェクトで、3つのファイルの変更をコミットした ばかりです。それらのファイルのリビジョンは今 1.2 ですが、プロジェクトの 他のファイルはまだ 1.1 です。プロジェクトをチェックアウトする時には、各 ファイルのリビジョン番号の一番高いものを取ってくることになります。 qsmith が今 myproj をチェックアウトしたとすると、トップレベルディレクト リのリビジョン番号は次のようになっているでしょう:

paste$ cvs -q -d :pserver:qsmith@cvs.foobar.com:/usr/local/cvs co myproj
U myproj/README.txt
U myproj/hello.c
U myproj/a-subdir/whatever.c
U myproj/a-subdir/subsubdir/fish.c
U myproj/b-subdir/random.c
paste$ cd myproj/CVS
paste$ cat Entries
/README.txt/1.1.1.1/Sun Apr 18 18:18:22 1999//
/hello.c/1.2/Mon Apr 19 06:35:15 1999//
D/a-subdir////
D/b-subdir////
paste$

hello.c ファイル(他のファイルにうもれていますが)は今リビジョン 1.2 で、 README.txt はまだ最初のリビジョンのままです。(リビジョン 1.1.1.1 ですが、 1.1 でもあります)

彼が hello.c に

printf ("between hello and goodbye\n");

このような行を付け加えてコミットしたとすると、リビジョン番号はもう一度イ ンクリメントされます:

paste$ cvs ci -m "added new middle line"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in hello.c;
/usr/local/cvs/myproj/hello.c,v  <--  hello.c
new revision: 1.3; previous revision: 1.2
done
paste$

hello.c はリビジョン 1.3 になりました。fish.c と random.c はリビジョン 1.2 のままで、その他のファイルは全部リビジョン 1.1 です。

cvs commit のかわりに cvs ci というコマンドを使ったことに注意して下さい。 CVS のコマンドはほとんど、タイプしやすいように短い形式を持っています。 checkout, update, commit の省略形はそれぞれ、co, up, ci です。省略形の一 覧を見たければ cvs --help-synonyms を走らせてみましょう。

Entries ファイルを見るのがリビジョン番号を知るための唯一の方法ではありま せん。 status コマンドも使えます。

paste$ cvs status hello.c
===================================================================
File: hello.c           Status: Up-to-date

   Working revision:    1.3     Tue Apr 20 02:34:42 1999
   Repository revision: 1.3     /usr/local/cvs/myproj/hello.c,v
   Sticky Tag:          (none)
   Sticky Date:         (none)
   Sticky Options:      (none)

ファイル名を指定しないで起動すると、プロジェクト内の全ファイルのステータ スを表示します:

paste$ cvs status
cvs status: Examining.
===================================================================
File: README.txt        Status: Up-to-date

   Working revision:    1.1.1.1 Sun Apr 18 18:18:22 1999
   Repository revision: 1.1.1.1 /usr/local/cvs/myproj/README.txt,v
   Sticky Tag:          (none)
   Sticky Date:         (none)
   Sticky Options:      (none)

===================================================================
File: hello.c           Status: Up-to-date

   Working revision:    1.3     Tue Apr 20 02:34:42 1999
   Repository revision: 1.3     /usr/local/cvs/myproj/hello.c,v
   Sticky Tag:          (none)
   Sticky Date:         (none)
   Sticky Options:      (none)

cvs status: Examining a-subdir
===================================================================
File: whatever.c        Status: Up-to-date

   Working revision:    1.1.1.1 Sun Apr 18 18:18:22 1999
   Repository revision: 1.1.1.1 /usr/local/cvs/myproj/a-subdir/whatever.c,v
   Sticky Tag:          (none)
   Sticky Date:         (none)
   Sticky Options:      (none)

cvs status: Examining a-subdir/subsubdir
===================================================================
File: fish.c            Status: Up-to-date

   Working revision:    1.2     Mon Apr 19 06:35:27 1999
   Repository revision: 1.2     /usr/local/cvs/myproj/
                                a-subdir/subsubdir/fish.c,v
   Sticky Tag:          (none)
   Sticky Date:         (none)
   Sticky Options:      (none)

cvs status: Examining b-subdir
===================================================================
File: random.c          Status: Up-to-date

   Working revision:    1.2     Mon Apr 19 06:35:27 1999
   Repository revision: 1.2     /usr/local/cvs/myproj/b-subdir/random.c,v
   Sticky Tag:          (none)
   Sticky Date:         (none)
   Sticky Options:      (none)

paste$

よくわからないところは無視して下さい。実際これは CVS に関してはよいアド バイスなんです。あなたが探しているちょっとした情報に、全然関係ない情報が ぞろぞろついてきてわけがわからない、ということがよくあるのです。というか それが普通です。必要あるところだけ取り出して、残りは気にしないことです。

前の例で、気にしないといけないところは各ファイルのステータス出力の最初の 3行です(空行は数えないで)。最初の行は一番重要です。ファイル名と作業コピー のステータスが書いてあります。現在、ファイルは全てリポジトリと同期してい るので Up-to-date となっています。もし random.c を変更してまだコ ミットしていないとすると、次のようになるでしょう:

===================================================================
File: random.c          Status: Locally Modified

   Working revision:    1.2     Mon Apr 19 06:35:27 1999
   Repository revision: 1.2     /usr/local/cvs/myproj/b-subdir/random.c,v
   Sticky Tag:          (none)
   Sticky Date:         (none)
   Sticky Options:      (none)

Working revision と Repository revision を見ると、ファイルがリポジトリと 同期していないかどうかがわかります。オリジナル作業コピーに戻って(jrandom の作業コピーはまだ hello.c の変更を知りません)、ステータスを見てみましょ う。

floss$ cvs status hello.c
===================================================================
File: hello.c           Status: Needs Patch

   Working revision:    1.2     Mon Apr 19 02:17:07 1999
   Repository revision: 1.3     /usr/local/cvs/myproj/hello.c,v
   Sticky Tag:          (none)
   Sticky Date:         (none)
   Sticky Options:      (none)

floss$

これは、誰かが hello.c の変更をコミットしてリポジトリのリビジョンを 1.3 に上げたのに、この作業コピーはまだ 1.2 のままであることを示してい ます。Status: Needs Patch の意味は、次のアップデートでリポジトリのその 変更を見て、作業コピーに "patch" を当てる、ということです。

ちょっと、qsmith が hello.c を変更したのを知らないつもりになってみましょ う。status も update も走らせません。そのかわりそのファイルを編集して、 同じところを変更してみましょう。こうやると、初めてのコンフリクトにお目 にかかれますよ。


Node:Detecting And Resolving Conflicts, Next:, Previous:Revision Numbers, Up:A Day With CVS

Detecting And Resolving Conflicts

コンフリクトを発見するのは簡単です。 CVS は update を実行する前に、間違 えようのない言葉で「コンフリクトがあるよ」と知らせてくれます。まずコンフ リクトを作ってみましょう。hello.c を編集して、次のような行を追加して下さ い:

printf ("this change will conflict\n");

qsmith がこういう行をコミットしたその場所にです:

printf ("between hello and goodbye\n");

この時点で、作業コピーの hello.c のステータスは次のようになります

floss$ cvs status hello.c
===================================================================
File: hello.c           Status: Needs Merge

   Working revision:    1.2     Mon Apr 19 02:17:07 1999
   Repository revision: 1.3     /usr/local/cvs/myproj/hello.c,v
   Sticky Tag:          (none)
   Sticky Date:         (none)
   Sticky Options:      (none)

floss$

リポジトリも作業コピーも変更されていて、それらの変更をマージしなければな らない、という意味です。(CVS は変更がコンフリクトしていることはまだ気づ いてません、update をまだ実行していないですからね) update を走らせた時に はこうなります:

floss$ cvs update hello.c
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.2
retrieving revision 1.3
Merging differences between 1.2 and 1.3 into hello.c
rcsmerge: warning: conflicts during merge
cvs update: conflicts found in hello.c
C hello.c
floss$

最後の行は giveaway 。ファイル名の横にある C は変更がマージされたけれど もコンフリクトした、ということを示します。 hello.c の内容には両方の変更 が示されています:

#include <stdio.h>

void
main ()
{
  printf ("Hello, world!\n");
<<<<<<< hello.c
  printf ("this change will conflict\n");
=======
  printf ("between hello and goodbye\n");
>>>>>>> 1.3
  printf ("Goodbye, world!\n");
}

コンフリクトはつねにコンフリクトマーカで区切られ、次の形式で示されます:

<<<<<<< (filename)
  作業コピーの未コミットの変更
  blah blah blah =======
  リポジトリからきた新しい変更
  blah blah blah
  などなど (リポジトリの最新リビジョン番号など)
>>>>>>> (latest revision number in the repository)

Entries ファイルには、ファイルが現在中途半端な状態になっていることが書い てあります。

floss$ cat CVS/Entries
/README.txt/1.1.1.1/Sun Apr 18 18:18:22 1999//
D/a-subdir////
D/b-subdir////
/hello.c/1.3/Result of merge+Tue Apr 20 03:59:09 1999//
floss$

コンフリクトを解消するには、ファイルを編集して、あるべき姿にし、コンフリ クトマーカを取り除き、そしてコミットします。必ずしも変更のうちどちらかを 選んでもう片方を捨てたりする必要はありません、どちらの変更もいまいちだと 思えば、コンフリクトしているところ(ファイル全部でもかまわないんですが)を すっかり書き換えてしまってもいいのです。今回は最初の変更に合わせることに して、でもキャピタライズと句読点の打ちかたを少しだけ変えておくことにしま しょう。

floss$ emacs hello.c
  (make the edits...)
floss$ cat hello.c
#include <stdio.h>

void
main ()
{
  printf ("Hello, world!\n");
  printf ("BETWEEN HELLO AND GOODBYE.\n");
  printf ("Goodbye, world!\n");
}
floss$ cvs ci -m "adjusted middle line"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in hello.c;
/usr/local/cvs/myproj/hello.c,v  <-  hello.c
new revision: 1.4; previous revision: 1.3
done
floss$


Node:Finding Out Who Did What (Browsing Log Messages), Next:, Previous:Detecting And Resolving Conflicts, Up:A Day With CVS

Finding Out Who Did What (Browsing Log Messages)

ここまで、今回のこのプロジェクトはいくつかの変更を経験しました。いままで に起こったことをざっと見ようと思ったとき、diff を全部詳しく調べたりする 必要はありません。ログメッセージを見るのが理想的ですね、log コマンドを使 えば見ることができます:

floss$ cvs log
(pages upon pages of output omitted)

ログ出力は繁雑になりがちです。1つのファイルのログメッセージだけを見ましょ う:

floss$ cvs log hello.c
RCS file: /usr/local/cvs/myproj/hello.c,v
Working file: hello.c
head: 1.4
branch:
locks: strict
access list:
symbolic names:
        start: 1.1.1.1
        jrandom: 1.1.1
keyword substitution: kv
total revisions: 5;     selected revisions: 5
description:
--------------
revision 1.4
date: 1999/04/20 04:14:37;  author: jrandom;  state: Exp;  lines: +1 -1
adjusted middle line
--------------
revision 1.3
date: 1999/04/20 02:30:05;  author: qsmith;  state: Exp;  lines: +1 -0
added new middle line
--------------
revision 1.2
date: 1999/04/19 06:35:15;  author: jrandom;  state: Exp;  lines: +1 -0
print goodbye too
--------------
revision 1.1
date: 1999/04/18 18:18:22;  author: jrandom;  state: Exp;
branches:  1.1.1;
Initial revision
--------------
revision 1.1.1.1
date: 1999/04/18 18:18:22;  author: jrandom;  state: Exp;  lines: +0 -0
initial import into CVS
=========================================================================
floss$

いつものとおり、一番上になにかたくさん情報があるようですが、無視しましょ う。ダッシュの行の次に肝心なところが、読んでわかるフォーマットで書いてあ ります。

1つのコミットでたくさんのファイルが送られるとき、それらのファイルは同じ メッセージを共有します。変更を追跡するとき役に立ちます。たとえば、fish.c と random.c を同時にコミットしたときのことを思いだしてみてください。こん な風にコミットしましたよね:

floss$ cvs commit -m "filled out C code"
Checking in a-subdir/subsubdir/fish.c;
/usr/local/cvs/myproj/a-subdir/subsubdir/fish.c,v  <-  fish.c
new revision: 1.2; previous revision: 1.1
done
Checking in b-subdir/random.c;
/usr/local/cvs/myproj/b-subdir/random.c,v  <-  random.c
new revision: 1.2; previous revision: 1.1
done
floss$

ここでやったことは、同じログメッセージ「C のコードをふくらませた」で両方 のファイルをコミットするということです(ここではたまたまどちらのファイル もリビジョン番号が 1.1 から 1.2 になっていますが、それは偶然一致しただけ です。もし random.c が 1.29 だったら、このコミットで 1.30 になって、 fish.c のリビジョン1.2と同じログメッセージを共有することになります)。

cvs log を実行すると、共有ログメッセージが見えます:

floss$ cvs log a-subdir/subsubdir/fish.c b-subdir/random.c

RCS file: /usr/local/cvs/myproj/a-subdir/subsubdir/fish.c,v
Working file: a-subdir/subsubdir/fish.c
head: 1.2
branch:
locks: strict
access list:
symbolic names:
        start: 1.1.1.1
        jrandom: 1.1.1
keyword substitution: kv
total revisions: 3;     selected revisions: 3
description:
--------------
revision 1.2
date: 1999/04/19 06:35:27;  author: jrandom;  state: Exp;  lines: +8 -1
filled out C code
--------------
revision 1.1
date: 1999/04/18 18:18:22;  author: jrandom;  state: Exp;
branches:  1.1.1;
Initial revision
--------------
revision 1.1.1.1
date: 1999/04/18 18:18:22;  author: jrandom;  state: Exp;  lines: +0 -0
initial import into CVS
=========================================================================
RCS file: /usr/local/cvs/myproj/b-subdir/random.c,v
Working file: b-subdir/random.c
head: 1.2
branch:
locks: strict
access list:
symbolic names:
        start: 1.1.1.1
        jrandom: 1.1.1
keyword substitution: kv
total revisions: 3;     selected revisions: 3
description:
--------------
revision 1.2
date: 1999/04/19 06:35:27;  author: jrandom;  state: Exp;  lines: +8 -1
filled out C code
--------------
revision 1.1
date: 1999/04/18 18:18:22;  author: jrandom;  state: Exp;
branches:  1.1.1;
Initial revision
--------------
revision 1.1.1.1
date: 1999/04/18 18:18:22;  author: jrandom;  state: Exp;  lines: +0 -0
initial import into CVS
=========================================================================
floss$

この出力を見れば、この2つのリビジョンが同じコミットだったというのがわか ります(2つのリビジョンのタイムスタンプが同一あるいは直近だ、というのより はるかにわかりやすいですね)。

ログメッセージを読むというのは、あるプロジェクトにどのようなことが起こっ てきたかをさっとつかんだり、ある時刻に特定のファイルに何が起こったか知る には良い方法です。生の cvs log の出力をもっと簡明で読みやすいかたち(GNU の ChangeLog のスタイルみたいな)に整形するフリーのツールもあります。そう いうツールはこのツアーではカバーしませんが、Third-Party Tools で紹 介します。


Node:Examining And Reverting Changes, Next:, Previous:Finding Out Who Did What (Browsing Log Messages), Up:A Day With CVS

Examining And Reverting Changes

qsmith がログを読んでいて、jrandom が hello.c に最近ほどこした変更を見た とします:

revision 1.4
date: 1999/04/20 04:14:37;  author: jrandom;  state: Exp;  lines: +1 -1
adjusted middle line

彼は「jrandom は一体何をしとんねん」と思いました。qsmith がたずねた質問 を正確な言葉で言うと、「hello.c のわたしのリビジョン(1.3)と、そのすぐあ との jrandom のリビジョン(1.4)の違いは何なのでしょう」ですね。これは diff コマンドでわかることですが、今回は過去の2つのリビジョンを比べるので、 -r コマンドオプションを使ってそれらを指定します:

paste$ cvs diff -c -r 1.3 -r 1.4 hello.c
Index: hello.c
===========================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -c -r1.3 -r1.4
*** hello.c     1999/04/20 02:30:05     1.3
--- hello.c     1999/04/20 04:14:37     1.4
***************
*** 4,9 ****
  main ()
  {
    printf ("Hello, world!\n");
!   printf ("between hello and goodbye\n");
    printf ("Goodbye, world!\n");
  }
--- 4,9 --
  main ()
  {
    printf ("Hello, world!\n");
!   printf ("BETWEEN HELLO AND GOODBYE.\n");
    printf ("Goodbye, world!\n");
  }
paste$

このように見ると変更点は明らかです。リビジョン番号を時系列順に指定したの で(普通はこれでいいです)、diff もその順で示されます。リビジョン番号を1つ だけ指定すると、CVS は現在の作業コピーを比較対象にします。

qsmith はこの変更を見てすぐ、自分のやりかたの方がいいから、「アンドゥー」 つまりリビジョンをひとつ戻して解決するんだ、と決めました。

しかし、彼はリビジョン1.4を捨てたいというわけではありません。ですが、た だ技術的な問題としてどうかというと、CVS ではそういうことも可能です、たい がいそんなことをする理由はないですが。リビジョン1.4をそのままにしておい て、1.3 にそっくりな新しいリビジョン1.5を作るほうがましです。こうすると、 アンドゥーそのものもそのファイルの履歴に残ります。

残るはどうやってリビジョン1.3を取ってきてそれを1.5とするか、という疑問だ けです。

この場合に限って言うと、とてもシンプルな変更なので qsmith が 1.3 をうつ すよう手でファイルを編集して、それをコミットすれば済みます。でも、変更が もっと複雑になったら(実際のプロジェクトでは普通そうでしょう)、古いリビジョ ンを手でもう一回作るというのはどう考えても間違えそうです。ですから、 qsmith は CVS を使って古いリビジョンを取ってきて、それを再コミットするべ きです。

これを実現するために、同じくらい良い方法が2つあります。ゆっくりチマチマ やる方法と、速くてカッコイイ方法です。まずはゆっくりチマチマやる方法を先 に見ましょう。


Node:The Slow Method Of Reverting, Next:, Previous:Examining And Reverting Changes, Up:A Day With CVS

The Slow Method Of Reverting

この方法では update に -p フラグと -r フラグを同時に渡します。-p オプショ ンは指定したリビジョン番号の内容を標準出力に送ります。それだけではこのオ プションは全然役に立ちません。ファイル内容がディスプレイ上を流れるだけ、 作業コピーはそのままです。しかしファイルにリダイレクトすれば、そのファイ ルの内容は古いリビジョンになるのです。手で編集してその状態にしたかのよう になります。

しかしまず qsmith はリポジトリの最新に追いついておく必要があります:

paste$ cvs update
cvs update: Updating .
U hello.c
cvs update: Updating a-subdir
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir
paste$ cat hello.c
#include <stdio.h>

void
main ()
{
  printf ("Hello, world!\n");
  printf ("BETWEEN HELLO AND GOODBYE.\n");
  printf ("Goodbye, world!\n");
}
paste$

次に update -p を走らせてリビジョン 1.3 が本当に彼の欲しいものかどうか確 認します:

paste$ cvs update -p -r 1.3 hello.c
===================================================================
Checking out hello.c
RCS:  /usr/local/cvs/myproj/hello.c,v
VERS: 1.3
***************
#include <stdio.h>

void
main ()
{
  printf ("Hello, world!\n");
  printf ("between hello and goodbye\n");
  printf ("Goodbye, world!\n");
}

おっと、最初の何行かが cruft ですね。これらは実際は標準出力ではなくて標 準エラー出力に送られているので害はありません。どちらにしろ出力が読みにく くなるのは確かなので -Q で抑制します:

paste$ cvs -Q update -p -r 1.3 hello.c
#include <stdio.h>

void
main ()
{
  printf ("Hello, world!\n");
  printf ("between hello and goodbye\n");
  printf ("Goodbye, world!\n");
}
paste$

どうでしょう、これは qsmith の欲しかったものですね。次はこれを作業コピー のファイルに置きかえます、Unix のリダイレクトを使いましょう(">" がそれで す):

paste$ cvs -Q update -p -r 1.3 hello.c > hello.c
paste$ cvs update
cvs update: Updating .
M hello.c
cvs update: Updating a-subdir
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir
paste$

update を走らせると変更ファイルとしてリストされました。これは内容が変わっ ているということです。はっきり言うと、これは古いリビジョン1.3の内容と同 じです(CVS はこれが以前のリビジョンと同一だということは知りません、ただ ファイルが変更されたことだけがわかっています)。qsmith が特に確認したいと 思えば、diff をとってチェックできます:

paste$ cvs -Q diff -c
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.4
diff -c -r1.4 hello.c
*** hello.c     1999/04/20 04:14:37     1.4
--- hello.c     1999/04/20 06:02:25
***************
*** 4,9 ****
  main ()
  {
    printf ("Hello, world!\n");
!   printf ("BETWEEN HELLO AND GOODBYE.\n");
    printf ("Goodbye, world!\n");
  }
--- 4,9 --
  main ()
  {
    printf ("Hello, world!\n");
!   printf ("between hello and goodbye\n");
    printf ("Goodbye, world!\n");
  }
paste$

はい、彼のしたかった復帰ができました。実際、これは以前取った diff の逆で す。満足して彼はコミットをかけます:

paste$ cvs ci -m "reverted to 1.3 code"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in hello.c;
/usr/local/cvs/myproj/hello.c,v  <-  hello.c
new revision: 1.5; previous revision: 1.4
done
paste$


Node:The Fast Method Of Reverting, Previous:The Slow Method Of Reverting, Up:A Day With CVS

The Fast Method Of Reverting

元に戻すのに速くてカッコイイ方法というのは、update に -j (join)フラグを 渡すやりかたです。このフラグはリビジョン番号をとるという点で -r に似てい て、同時に2つまでの -j フラグを取ることもできます。CVS は指定された2つの リビジョンの違いを計算し、問題のファイルにパッチとして適用する(だから、 リビジョン番号を指定する順番にはくれぐれも気をつけて)。

qsmith の作業コピーが最新版だとしましょう、その場合こうします:

paste$ cvs update -j 1.4 -j 1.3 hello.c
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.4
retrieving revision 1.3
Merging differences between 1.4 and 1.3 into hello.c
paste$ cvs update
cvs update: Updating .
M hello.c
cvs update: Updating a-subdir
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir
paste$ cvs ci -m "reverted to 1.3 code" hello.c
Checking in hello.c;
/usr/local/cvs/myproj/hello.c,v  <--  hello.c
new revision: 1.5; previous revision: 1.4
done
paste$

ファイルを1つだけ元に戻す場合なら、チマチマしようがすばやくしようが、そ んなに違いがあるわけではないです。しかしあとで出てきますが、複数のファイ ルを一度に元に戻そうとしたときなんかには速い方法のほうがどんなに良いかわ かると思います。とりあえずはやりやすい方法を使ってください。

Reverting Is Not A Substitute For Communication

たいがいの場合、qsmith が例でやったようなことというのはえらく無作法なや りかたです。実際のプロジェクトで他の人と一緒に作業しているときに、だれか が良くない変更をコミットしてるなと思ったら、まずはその人とそれについて話 し合うのがよいでしょう。その変更にはもっともな理由があることもあるし、た だあんまりちゃんと考えていなかっただけのこともあります。どちらにしろ、い きなり元に戻したりするような理由はありません。起こったことはすべて CVS に永久に保存されているのですから、変更した人に相談してからもとに戻しても 遅くはないのです。

あなたが納期目前のプロジェクトのメンテナだったりあるいは無条件に変更を元 に戻す必要も権利もあると思うのなら、そうすればいいですが、元に戻された変 更の主にはすぐにメールでフォローを入れて、あなたが何故そんなことをしたの か、何が原因で変更を再コミットする必要があったのかを説明してください。


Node:Other Useful CVS Commands, Next:, Previous:A Day With CVS, Up:An Overview of CVS

Other Useful CVS Commands

ここまでくれば、基本的なことなら気楽に CVS を使えるようになっていること と思います。ここからはツアー口調をやめて、役に立つコマンドをいくつか要約 して紹介したいと思います。


Node:Adding Files, Next:, Up:Other Useful CVS Commands

Adding Files

ファイル追加には2ステップの処理をします。add コマンドを実行してからコミッ トします。ファイルはコミットを実行するまでリポジトリには入りません:

floss$ cvs add newfile.c
cvs add: scheduling file 'newfile.c' for addition
cvs add: use 'cvs commit' to add this file permanently
floss$ cvs ci -m "added newfile.c" newfile.c
RCS file: /usr/local/cvs/myproj/newfile.c,v
done
Checking in newfile.c;
/usr/local/cvs/myproj/newfile.c,v  <-  newfile.c
initial revision: 1.1
done
floss$


Node:Adding Directories, Next:, Previous:Adding Files, Up:Other Useful CVS Commands

Adding Directories

ファイルを追加する場合とは違い、ディレクトリを追加するのは1ステップです。 コミットする必要はありません:

floss$ mkdir c-subdir
floss$ cvs add c-subdir
Directory /usr/local/cvs/myproj/c-subdir added to the repository
floss$

作業コピーの新しいディレクトリ内を見ると、add コマンドが CVS サブディレ クトリを自動的に生成したのがわかります:

floss$ ls c-subdir
CVS/
floss$ ls c-subdir/CVS
Entries     Repository  Root
floss$

これで、作業ディレクトリ中のほかのディレクトリでやっているように、中にファ イルや新しいディレクトリを追加したりできます。


Node:CVS And Binary Files, Next:, Previous:Adding Directories, Up:Other Useful CVS Commands

CVS And Binary Files

今まで黙っていましたが、CVS にはちょっとしたイヤな秘密があります。CVS は バイナリファイルをうまく扱えないのです(あー、ほかにもちょっとしたイヤな 秘密はありますけど、これは一番イヤな秘密のうちのひとつに数えられるものな んです)。バイナリを全然扱えないというのではないんですが、いいところが全 然ないんです。

いままで扱ってきたのは全てプレーンテキストファイルです。CVS はテキストファ イル用の特別なトリックを使っています。たとえばリポジトリが Unix で作業コ ピーが Windows や Mac にある場合、改行コードをそれぞれの環境に合わせて適 切に変換していたりします。改行コードというのは、Unix ではラインフィード (LF)のみに対し、Windowsではキャリッジリターン/ラインフィード(CRLF)になっ ています。従って Windows の作業コピー中のファイルは CRLF を使う一方で、 同じプロジェクトの Unix マシン上の作業コピーは LF を使っています(リポジ トリではいつも LF です)。

ほかに、CVS は RCS キーワードと呼ばれる特別な文字列を認識するトリックが あって、これはテキストファイルのその文字列を認識したら、リビジョンや他の 便利な情報に置換するというものです。例えば、ファイルがこういう文字列を含 んでいたとすると:

$Revision$

CVS はコミットのたびにリビジョン番号を含むようにこの文字列を展開します。 こんな風に:

$Revision: 1.3 $

CVS はファイルが改良されるのに合わせてこの文字列を最新に保ちます。 (Advanced CVSThird-Party Tools に、こういうキーワード文 字列についていろいろと説明してあります)

文字列展開は、ファイルを編集しているときにリビジョン番号やほかの情報を見 ることができたりするのでとても便利な機能です、テキストファイルについては ね。でもファイルが JPG の画像だったら? コンパイル済みの実行ファイルだっ たら? そういう種類のファイルで、CVS がキーワードを見つけて展開するような ことがあったら、深刻なダメージを与えるかもしれません。バイナリではそうい う文字列が偶然現われることがあるからです。

ですから、バイナリファイルを追加するときには、CVS に対してキーワード展開 と改行コード変換をやめるように教えてやる必要があります。その場合 -kb オ プションを使ってください:

floss$ cvs add -kb filename
floss$ cvs ci -m "added blah" filename
  (etc)

また、ときどき(テキストファイル中に擬似キーワード文字列を含んでいるよう な場合など) キーワード展開をしたくない場合もあるでしょう。そういう場合は -ko オプションを使います:

floss$ cvs add -ko filename
floss$ cvs ci -m "added blah" filename
  (etc)

(実際、この章はそのようなドキュメントのひとつですね、ここでも例のなかに $Revision$ だとか書いてありますし)

バイナリファイルのリビジョン間で cvs diff を走らせても意味が ないことに注意してください。diff はテキストを前提としたアルゴリズムを使っ ているので、バイナリファイルの場合はただ違っているということが報告される だけで違いの内容まではわかりません。CVS の将来のバージョンではバイナリファ イルの diff をとる方法も提供するかもしれません。


Node:Removing Files, Next:, Previous:CVS And Binary Files, Up:Other Useful CVS Commands

Removing Files

ファイルの削除は追加と同様、ひとつ余分な手順を踏みます。まずは作業コピー からそのファイルを削除しなければなりません:

floss$ rm newfile.c
floss$ cvs remove newfile.c
cvs remove: scheduling 'newfile.c' for removal
cvs remove: use 'cvs commit' to remove this file permanently
floss$ cvs ci -m "removed newfile.c" newfile.c
Removing newfile.c;
/usr/local/cvs/myproj/newfile.c,v  <-  newfile.c
new revision: delete; previous revision: 1.1
done
floss$

2つめと3つめのコマンドでは、作業コピーにすでに newfile.c が存在しないに もかかわらずファイル名を明示的に指定していることに注意してください。もち ろん、コミット時にはファイル名を必ずしも指定する必要はありませんが、こう しておけば作業コピー中の他のファイルの変更をまきこんでコミットしてしまう 心配がなくなります。


Node:Removing Directories, Next:, Previous:Removing Files, Up:Other Useful CVS Commands

Removing Directories

前にも言ったとおり、CVS はディレクトリのバージョン管理はしてくれません。 そのかわりお手軽な代替手段として、ほとんどの場合に「正しい動作」をする、 ちょっと変な動作を提供しています。空のディレクトリを特別扱いする、という のは、そういう変な動作のうちのひとつです。プロジェクトからディレクトリを 削除するときには、まずそれの中身を全部削除しないといけません:

floss$ cd dir
floss$ rm file1 file2 file3
floss$ cvs remove file1 file2 file3
  (output omitted)
floss$ cvs ci -m "removed all files" file1 file2 file3
  (output omitted)

次に、ひとつ上のディレクトリで -P フラグ付きの update を 実行します:

floss$ cd ..
floss$ cvs update -P
  (output omitted)

-P オプションは空のディレクトリを刈り込む(prune)よう update に指示します。 こうすると作業コピーから空のディレクトリが削除されます。それが終わって初 めて、そのディレクトリは削除されたと言えます。中のファイルが削除され、ディ レクトリ自身も削除されました(少なくとも作業コピーからは。リポジトリ内で はまだ空のディレクトリが存在したままですが)。

素の update を走らせた場合、CVSは新しいディレクトリをリポジトリから作業 コピーへ自動的に持ってこないのですが、これは上記の動作と対になるおもしろ い動作です。これには2つの理由があるんですが、ここで説明するような価値は ないのでやめておきます。ときどき、リポジトリから新しいディレクトリを取っ てくるよう -d フラグで指示して update を実行してみるとわかると思います。


Node:Renaming Files And Directories, Next:, Previous:Removing Directories, Up:Other Useful CVS Commands

Renaming Files And Directories

あるファイルの名前を変えるということは、新しい名前でファイルを作って、古 いのを消すというのと等価です。 Unix で言うと次のようなコマンドです:

floss$ cp oldname newname
floss$ rm oldname

同じことを CVS で実行するとすると:

floss$ mv oldname newname
floss$ cvs remove oldname
  (output omitted)
floss$ cvs add newname
  (output omitted)
floss$ cvs ci -m "renamed oldname to newname" oldname newname
  (output omitted)
floss$

ファイルに関してはこれでおしまいです。ディレクトリの名前を変えるのもだい たい同じです。新しいディレクトリを作って、cvs add して、古いディレクトリ から新しいディレクトリへファイルを全部移し、古いディレクトリでそれらを cvs remove してから新しいディレクトリで cvs add、cvs commit して実際にコ ミットしたら、cvs update -P で空のディレクトリを作業コピーからなくせばい いのです。

floss$ mkdir newdir
floss$ cvs add newdir
floss$ mv olddir/* newdir
mv: newdir/CVS: cannot overwrite directory
floss$ cd olddir
floss$ cvs rm foo.c bar.txt
floss$ cd ../newdir
floss$ cvs add foo.c bar.txt
floss$ cd ..
floss$ cvs commit -m "moved foo.c and bar.txt from olddir to newdir"
floss$ cvs update -P

3つめのコマンドの warning に注意してください。olddir の CVS/ サブディレ クトリを newdir に移せない、という意味のことを言われます。同じ名前のディ レクトリが newdir にありますからね。olddir に CVS/ サブディレクトリを置 いたままにしておきたいでしょうから、これでいいんですけど。

見てのとおり、ディレクトリを移すのはちょっと面倒です。一番いいのは最初に プロジェクトをインポートする時点で適切な配置にしておくことです。そうすれ ばそうそうディレクトリを移したりする必要もないでしょう。あとで、リポジト リ内のディレクトリを直接変えてディレクトリを移す思い切った方法を紹介しま すが、その方法は緊急のとき以外やらないほうがいいと思います。なにを取り扱 うのでも、できるかぎり作業コピーのなかで CVS の操作で行うのが一番いいの です。


Node:Avoiding Option Fatigue, Next:, Previous:Renaming Files And Directories, Up:Other Useful CVS Commands

Avoiding Option Fatigue

普通の人なら、コマンドを打つたびに同じオプションフラグをタイプするのは面 倒くさいだろうと思います。いつも -Q グローバルオプションを指定するとか、 diff を取るときにはいつも -c を指定するというのがわかっているのに、なん で毎回タイプしなくちゃいけないんでしょう。

幸い、策はあります。CVS はホームディレクトリの .cvsrc を探します。そのファ イルの中でデフォルトオプションを指定すれば、CVS の起動のたびにそれが適用 されます。.cvsrc の例を示します:

diff -c
update -P
cvs -q

行の一番左の単語が CVS コマンド(省略形でないほう)に一致したら、その行の オプションがそのコマンドに毎回適用されます。グローバルオプションを指定す るには cvs を使ってください(上記では最後の行)。この例では cvs diff の実 行には毎回自動的に -c フラグがつきます。


Node:Getting Snapshots (Dates And Tagging), Next:, Previous:Avoiding Option Fatigue, Up:Other Useful CVS Commands

Getting Snapshots (Dates And Tagging)

バグレポートが舞い込んできて壊れた状態になったプログラムの話に戻りましょ う。開発者は突然、最後のリリースの時点のプロジェクト全体にアクセスしなく てはなりません。ファイルはあれもこれも変更してあって、おまけにリビジョン 番号がファイルによってバラバラであろうとも、です。ログメッセージを見て、 ファイルごとにリリースのときのリビジョン番号はどれだったか探して、それぞ れ -r でリビジョン番号指定して update 走らせて、なんて、考えただけで時間 食いそうです。中規模から大規模のプロジェクト(数十から数百のファイルがあ るような…)でそんなことをして、ふりまわされたくないでしょう。

そこで CVS はプロジェクト中の過去のリビジョンをひとまとめにアクセスでき る方法を提供しています。実際には2つの方法が用意されています。ひとつは日 付指定で、コミットされた時刻をもとにリビジョンを選択する方法。もうひとつ はタグ指定で、過去にプロジェクトのスナップショットとしてマークをつけたも のにアクセスする方法。

状況によってどちらを選択するかが決まってきます。日付指定アクセスは update に -D フラグを渡すことによって実行できます。-r に似ていますがリビ ジョン番号のかわりに日付を指定します:

floss$ cvs -q update -D "1999-04-19"
U hello.c
U a-subdir/subsubdir/fish.c
U b-subdir/random.c
floss$

-D オプションを指定すると、update は指定された日付のなかで一番大きいリビ ジョンのファイルを取ってきて、必要があれば作業コピー中のファイルをそのリ ビジョンで置き換えます。

日付だけでなく、時刻も指定できます(したほうがいいことも多いです)。たとえ ば上のコマンドでは全部リビジョン1.1を取ってきています(表示された3つのファ イルだけが変更されていますが、それは他のファイルが全て1.1のままだからで す)。hello.c のステータスを見て確認しましょう:

floss$ cvs -Q status hello.c
===================================================================
File: hello.c                 Status: Up-to-date
   Working revision:          1.1.1.1 Sat Apr 24 22:45:03 1999
   Repository revision:       1.1.1.1 /usr/local/cvs/myproj/hello.c,v
   Sticky Date:               99.04.19.05.00.00
floss$

でもこの章の最初のほうにちょっと戻ってログメッセージを見てみると、 hello.c のリビジョン1.2は確かに1999/4/19にコミットされているはずなのに。 どうしてリビジョン1.2ではなく1.1を取ってきたのでしょう?

これは、"1999-04-19" という日付が「1999-04-19が始まる真夜中」、つま りその日の最初の瞬間、という意味に解釈されてしまうことが問題なのです。こ れはたぶん、望んだことではないですね。1.2 はその日のもうすこしあとでコミッ トされました。日付をもうすこし正確に指定して、リビジョン1.2を取ってきま しょう:

floss$ cvs -q update -D "1999-04-19 23:59:59"
U hello.c
U a-subdir/subsubdir/fish.c
U b-subdir/random.c
floss$ cvs status hello.c
===================================================================
File: hello.c                 Status: Locally Modified
   Working revision:  1.2     Sat Apr 24 22:45:22 1999
   Repository revision:       1.2     /usr/local/cvs/myproj/hello.c,v
   Sticky Tag:                (none)
   Sticky Date:               99.04.20.04.59.59
   Sticky Options:    (none)
floss$

こんなもんでしょうか。Sticky Date の行の日付と時刻をよく見てみると、午前 4:59:59を表示しているように見えますね、コマンドでは 11:59 を指定したはず なのに(sticky というのが何なのかというのは、あとで説明します)。御推察の 通り、このずれは地方時と Universal Coordinated Time (グリニッジ標準時)の 差が原因です。リポジトリは日付を Universal Time で保存しますが、クライア ント側の CVS は地方時を仮定します。今回の -D の場合、リポジトリ内の時刻 を比較することに興味があって、手元のシステムの時刻のことは気にしていない ので、少々運が悪かったですね。コマンド中に GMT を指定すれば回避できます:

floss$ cvs -q update -D "1999-04-19 23:59:59 GMT"
U hello.c
floss$ cvs -q status hello.c
===================================================================
File: hello.c                 Status: Up-to-date
   Working revision:  1.2     Sun Apr 25 22:38:53 1999
   Repository revision:       1.2     /usr/local/cvs/myproj/hello.c,v
   Sticky Tag:                (none)
   Sticky Date:               99.04.19.23.59.59
   Sticky Options:    (none)
floss$

いかがでしょう。こうすることで作業コピーは 4/19 の最終のコミットへと戻り ました(その日の最後の1秒のあいだにコミットしたのなら別ですが、しなかった ですから)。

今 update を走らせたらどうなるんでしょう?

floss$ cvs update
cvs update: Updating .
cvs update: Updating a-subdir
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir
floss$

何も起きません。しかし、少なくとも3つのファイルに関してはもっと新しいバー ジョンがあったはずです。なぜそれらが作業コピーに入ってこないのでしょう。

ここで「sticky」の出番です。-D フラグでアップデート(「ダウンデート」かな?) すると、作業コピーは永続的にその日付以前に制限されることになります。CVS 用語で言うと、その作業コピーは「スティッキーデート」が設定された、という ことになります。作業コピーが一度スティッキーになると、そうでなくなるよう に指示されるまでスティッキーになったままです。ですから、続いて update を 実行しても自動的に最新のリビジョンを取ってきたりはしないのです。スティッ キーかどうかは、cvs status を実行するか、CVS/Entries ファイルを調べれば わかります:

floss$ cvs -q update -D "1999-04-19 23:59:59 GMT"
U hello.c
floss$ cat CVS/Entries
D/a-subdir////
D/b-subdir////
D/c-subdir////
/README.txt/1.1.1.1/Sun Apr 18 18:18:22 1999//D99.04.19.23.59.59
/hello.c/1.2/Sun Apr 25 23:07:29 1999//D99.04.19.23.59.59
floss$

ここで hello.c を変更してコミットしようとすると

floss$ cvs update
M hello.c
floss$ cvs ci -m "trying to change the past"
cvs commit: cannot commit with sticky date for file 'hello.c'
cvs [commit aborted]: correct above errors first!
floss$

CVS はコミットさせてくれません。それは時間を遡って過去を変えようとするよ うなものだからです。CVS はあらゆる記録をとろうとし、その結果、その操作を 許可しないのです。

しかしこれは CVS がその日以来コミットされてきたリビジョンを知らないとい う意味ではありません。スティッキーデートの設定された作業コピーも、未来の リビジョンを含めて比較できます。

floss$ cvs -q diff -c -r 1.5 hello.c
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.5
diff -c -r1.5 hello.c
*** hello.c   1999/04/24 22:09:27     1.5
--- hello.c   1999/04/25 00:08:44
***************
*** 3,9 ****
  void
  main ()
  {
    printf ("Hello, world!\n");
-   printf ("between hello and goodbye\n");
    printf ("Goodbye, world!\n");
  }
--- 3,9 --
  void
  main ()
  {
+   /* this line was added to a downdated working copy */
    printf ("Hello, world!\n");
    printf ("Goodbye, world!\n");
  }

diff を見ると、1999/4/19 現在において hello の行と gooodbye の行の間の行 はまだ追加されていなかったということがわかります。作業コピーに加えた変更 も表示されていますね(コード断片の前にあるコメントを追加しました)。

スティッキーデート(やほかのスティッキー)を取り除くこともできます。update で -A を指定してください(-A はリセットという意味です、理由は聞かないでく ださい)、作業コピーが最新のリビジョンに戻ります:

floss$ cvs -q update -A
U hello.c
floss$ cvs status hello.c
===================================================================
File: hello.c                 Status: Up-to-date
   Working revision:  1.5     Sun Apr 25 22:50:27 1999
   Repository revision:       1.5     /usr/local/cvs/myproj/hello.c,v
   Sticky Tag:                (none)
   Sticky Date:               (none)
   Sticky Options:    (none)
floss$


Node:Acceptable Date Formats, Next:, Previous:Getting Snapshots (Dates And Tagging), Up:Other Useful CVS Commands

Acceptable Date Formats

CVS は日付の指定の形式については幅広く受け入れます。前の例で使った形式は ISO 8601 のフォーマット(International Standards Organization standard #8601 のこと、www.saqqara.demon.co.uk/datefmt.htm も参照のこと)なのです が、これを使えばうまくいかないことはないでしょう。電子メールの日付フォー マット(RFC 822 と RFC 1123、 www.rfc-editor.org/rfc/ を参照のこと)も使え ます。現在の日付から相対的に日付を指定する曖昧な英単語すら使えます。

全てのフォーマットを使える必要はないですが、CVS が何を受け付けるか理解す るためにいくつかの例を示します:

floss$ cvs update -D "19 Apr 1999"
floss$ cvs update -D "19 Apr 1999 20:05"
floss$ cvs update -D "19/04/1999"
floss$ cvs update -D "3 days ago"
floss$ cvs update -D "5 years ago"
floss$ cvs update -D "19 Apr 1999 23:59:59 GMT"
floss$ cvs update -D "19 Apr"

日付を囲むダブルクオートは、日付が空白を含んでいても、Unix シェルがそれ をひとつの引数として扱うようにするためにつけています。空白を含んでいなく ても害はないのでいつも使うようにするのがよいでしょう。


Node:Marking A Moment In Time (Tags), Previous:Acceptable Date Formats, Up:Other Useful CVS Commands

Marking A Moment In Time (Tags)

日付を指定してアクセスする方法は、単なる時間の経過が主な関心事であれば便 利かもしれません。しかし、本当は特定のイベントが起こった時点でのプロジェ クトの状態にアクセスしたい、ということのほうが多いと思います。それはたと えばリリースの時点であったり、ソフトウェア開発が安定したある時点であった り、主要な機能を追加または削除した時点であったりするわけです。

そのイベントが起こった日付を思い出そうとしたり、ログメッセージを読んでそ の日付を推測したりするのは、さぞかし退屈な作業でしょう。おそらく、そのよ うなイベントは重要なのでしょうから、リビジョン履歴のなかにそのようにきち んと記録されています。CVS でそのようなマークをつける方法は タグ付け (tagging) といいます。

タグはコミットとは違い、ファイルの特定の変更を記録するわけではなくて、開 発者のファイルへの姿勢に変更があることを記録します。タグとは、ある開発者 の作業コピーで表わされる、リビジョンの集合につけられたラベルです(通常、 そのような作業コピーは完全に最新なので、タグ名はリポジトリ内の「最新で最 良の」リビジョンにつけられます)。

タグをセットするのは簡単です、こんな感じ:

floss$ cvs -q tag Release-1999_05_01
T README.txt
T hello.c
T a-subdir/whatever.c
T a-subdir/subsubdir/fish.c
T b-subdir/random.c
floss$

このコマンドで、この作業コピーで表されるスナップショットにシンボル名 "Release-1999_05_01" を関連づけます。きちんと定義すると、スナップショッ トとは、プロジェクトのファイルと関連づけられたリビジョン番号の集合です。 これらのリビジョン番号はファイル同士で同じである必要はなく、実際、違うこ とのほうが多いです。たとえば、この章でずっと使っている myproj ディレクト リでタグをつけて、その作業コピーが完全に最新だったと仮定すると、 シンボル名 "Release-1999_05_01" は hello.c はリビジョン1.5、fish.c はリ ビジョン1.2、random.c はリビジョン1.2、その他はリビジョン1.1につきます。

タグを線で表わしてプロジェクト内のファイルのいろいろなリビジョンをつない で可視化するとわかりやすいと思います。Figure 2.1 では、あるプロジェクト 内でタグ付けされた各ファイルのリビジョン番号を線でつないでみました。

     File A      File B      File C      File D      File E
     ------      ------      ------      ------      ------
     1.1         1.1         1.1         1.1         1.1
 ----1.2-.       1.2         1.2         1.2         1.2
     1.3 |       1.3         1.3         1.3         1.3
          \      1.4       .-1.4-.       1.4         1.4
           \     1.5      /  1.5  \      1.5         1.5
            \    1.6     /   1.6   |     1.6         1.6
             \   1.7    /          |     1.7         1.7
              \  1.8   /           |     1.8       .-1.8------->
               \ 1.9  /            |     1.9      /  1.9
                `1.10'             |     1.10    /   1.10
                 1.11              |     1.11    |
                                   |     1.12    |
                                   |     1.13    |
                                    \    1.14    |
                                     \   1.15   /
                                      \  1.16  /
                                       `-1.17-'

[Figure 2.1: How a tag might stand in relation to files's revisions.]

線をひっぱってピンとさせて、それに沿って見ると、そのプロジェクトの履歴中 の特定の時点が見えてきます。すなわち、その時点でタグがセットされたのです (Figure 2.2)。

     File A      File B      File C      File D      File E
     ------      ------      ------      ------      ------
                                         1.1
                                         1.2
                                         1.3
                                         1.4
                                         1.5
                                         1.6
                                         1.7
                 1.1                     1.8
                 1.2                     1.9
                 1.3                     1.10        1.1
                 1.4                     1.11        1.2
                 1.5                     1.12        1.3
                 1.6                     1.13        1.4
                 1.7         1.1         1.14        1.5
                 1.8         1.2         1.15        1.6
     1.1         1.9         1.3         1.16        1.7
 ----1.2---------1.10--------1.4---------1.17--------1.8------->
     1.3         1.11        1.5         1.17        1.9
                             1.6         1.17        1.10

[Figure 2.2: The same tag as a "straight sight" through the revision history.]

続けてファイルを編集し、コミットしても、タグはリビジョン番号が増えるにつ れて動いたりしません。固定したまま、タグづけられた時点での各ファイルのリ ビジョン番号にくっついています(スティッキー)。

タグは記述子として重要性があるにもかかわらず、ログメッセージにタグのこと が含まれなかったり、that the tags themselves can't be full paragraphs of prose というのは少し不幸です。前の例ではタグ自身が、そのプロジェクトが ある日付でのリリース状態であることを明白に説明しています。しかし、もう少 し複雑な状態のスナップショットを作りたいこともあるでしょう、そうするとタ グ名はこんなに見苦しくなってしまいます:

floss$ cvs tag testing-release-3_pre-19990525-public-release

一般的な規則として、タグ名はできるだけ簡潔に、そして記録しようとしている イベントについての情報を必要十分に含んでいるよう心がけるべきです。迷った 時は、説明しすぎるほうへ振っておいたほうがいいでしょう。あとになって、そ の時の状況を正確に記述した冗長なタグ名から何かわかって、嬉しいこともある でしょう。

タグ名にピリオドやスペースが使われていないのに気づいたと思います。CVS で は有効なタグ名を構成するものについてはちょっと厳しいのです。英字で始まり、 英数字、ハイフン("-")、アンダスコア("_")で構成される、というのがそのルー ルです。スペースやピリオド、コロン、カンマ、記号は使えません。

タグ名でスナップショットにアクセスするには、タグ名をリビジョン番号のよう に使えばいいのです。スナップショットへのアクセスには2通りの方法がありま す: あるタグを指定して新しい作業コピーをチェックアウトするか、またはタグ を指定して既存の作業コピーに上書きするか、です。どちらの方法でも、作業コ ピー中のファイルは指定したタグのリビジョンになっています。

たいがいの場合やりたいことというのは、そのスナップショットの時点でプロジェ クトがどんなだったかちょっと見たい、というくらいのことだと思います。その ような場合だと、自分が今作業していて、コミットしていない変更があったり何 か便利な状態が構築してあったりするようなメインの作業コピーでそんなことは あんまりしたくないでしょうから、タグを指定して別の作業コピーをチェックア ウトしたいんだということにしましょう。このようにします(これは今ある作業 コピーとは別の場所、1つ上のディレクトリとか、に居ることを確認してから実 行してくださいね!)

floss$ cvs checkout -r Release-1999_05_01 myproj
cvs checkout: Updating myproj
U myproj/README.txt
U myproj/hello.c
cvs checkout: Updating myproj/a-subdir
U myproj/a-subdir/whatever.c
cvs checkout: Updating myproj/a-subdir/subsubdir
U myproj/a-subdir/subsubdir/fish.c
cvs checkout: Updating myproj/b-subdir
U myproj/b-subdir/random.c
cvs checkout: Updating myproj/c-subdir

update コマンドで -r オプションを見てきました、あれはそのあとにリビジョ ン番号をつけましたよね。多くの場合、タグはリビジョン番号のように使えます。 それは、各ファイルにとってみればタグというのは単に、対応するひとつのリビ ジョン番号をさしているだけだからです(ひとつのプロジェクトで同じ名前のタ グをふたつ持つというのは違反ですし、一般には不可能です)。実際、CVS でリ ビジョン番号を使うようなところではどこでも、かわりにタグ名が使えます(タ グがセットされていさえすれば)。あるファイルについて、現状と最後のリリー ス時点間の diff を取りたければこのようにします:

floss$ cvs diff -c -r Release-1999_05_01 hello.c

一時的にそのリビジョンに戻したければこのようにします:

floss$ cvs update -r Release-1999_05_01 hello.c

タグとリビジョンのこの変換可能性から、有効なタグ名の厳しいルールの理由が 説明できます。タグ名にピリオドを許したらどうなりますか? 実際のリビジョン 番号"1.47"に"1.3"というタグ名をつけることができてしまいますよね。その後 にこういうコマンドを実行したとすると

floss$ cvs update -r 1.3 hello.c

これが "1.3" というタグを指定しているのか、もっと前のリビジョン1.3を指定 しているのか、CVS はどうやって判断すればよいのでしょう。このせいでタグ名 が制限してあって、CVS はタグ名とリビジョン番号の区別を容易に判断できるよ うになっています。リビジョン番号にはピリオドがあって、タグ名にはありませ ん。(ほかの制限も同じ理由によります、CVS がタグ名を認識しやすいようになっ ているのです)

さてここで、そろそろだろうなと思ったことと思いますが、スナップショットに アクセスする2つめの方法を紹介します。既存の作業コピーをタグ付けされたリ ビジョンに切り替えるやつです、これも update でやります:

floss$ cvs update -r Release-1999_05_01
cvs update: Updating .
cvs update: Updating a-subdir
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir
cvs update: Updating c-subdir
floss$

上記のコマンドは、以前 hello.c を Release-1999_05_01 に戻すときに 使ったものとほとんど同じです。ファイル名の指定がないところだけが違います、 今回はプロジェクト全体を元に戻したいですからね。(もしやりたければプロジェ クトのサブツリーだけをタグの時点に戻すこともできます。上のコマンドをトッ プレベルではなくてサブツリー内で実行してください。あんまりそうしたい機会 が多いとも思えませんけど)

アップデートの時点では、どのファイルも変更されていないように見えることに 注意してください。作業コピーはタグ付けされた時点では最新でしたし、それ以 来変更はコミットされていません。

しかしこれは何も変更されていないということを意味しません。作業コピーはこ れがタグ付けされたリビジョンであることを知っています。ここで変更を加えて コミットしようとすると(hello.c を変更したとしましょう):

floss$ cvs -q update
M hello.c
floss$ cvs -q ci -m "trying to commit from a working copy on a tag"
cvs commit: sticky tag 'Release-1999_05_01' for file 'hello.c' is not a branch
cvs [commit aborted]: correct above errors first!
floss$

CVS はコミットを許しません。(エラメッセージの正確な意味についてはいまの ところ放っておいていいです、ブランチについては次で説明します) これはタグ で指定されたこの作業コピーがチェックアウトされたものかアップデートされた ものかには関係ありません。いったんタグで指定したら、CVS はその作業コピー を履歴のある一時点でのスタティックなスナップショットだと見なし、履歴を変 更させなくなります。少なくとも簡単にはさせてくれません。cvs status を実 行するか、CVS/Entries ファイルを見ると、スティッキータグが各ファイルに設 定されているのがわかります。たとえばトップレベルの Entries ファイルはこ うなっています:

floss$ cat CVS/Entries
D/a-subdir////
D/b-subdir////
D/c-subdir////
/README.txt/1.1.1.1/Sun Apr 18 18:18:22 1999//TRelease-1999_05_01
/hello.c/1.5/Tue Apr 20 07:24:10 1999//TRelease-1999_05_01
floss$

ほかのスティッキーと同じように、タグも -A フラグつきの update を実行すれ ば削除できます:

floss$ cvs -q update -A
M hello.c
floss$

hello.c に加えた変更は失われません、CVS is still aware that the file changed with respect to the repository:

floss$ cvs -q diff -c hello.c
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.5
diff -c -r1.5 hello.c
*** hello.c   1999/04/20 06:12:56     1.5
--- hello.c   1999/05/04 20:09:17
***************
*** 6,9 ****
--- 6,10 --
    printf ("Hello, world!\n");
    printf ("between hello and goodbye\n");
    printf ("Goodbye, world!\n");
+   /* a comment on the last line */
  }
floss$

update でリセットしたので、CVS はコミットさせてくれます:

floss$ cvs ci -m "added comment to end of main function"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
cvs commit: Examining c-subdir
Checking in hello.c;
/usr/local/cvs/myproj/hello.c,v  <-  hello.c
new revision: 1.6; previous revision: 1.5
done
floss$

もちろん、Release-1999_05_01 というタグはリビジョン1.5についたま まです。タグ以前と以降のリビジョンのステータスを比べてみてください:

floss$ cvs -q status hello.c
===================================================================
File: hello.c                 Status: Up-to-date
   Working revision:  1.6     Tue May  4 20:09:17 1999
   Repository revision:       1.6     /usr/local/cvs/myproj/hello.c,v
   Sticky Tag:                (none)
   Sticky Date:               (none)
   Sticky Options:            (none)
floss$ cvs -q update -r Release-1999_05_01
U hello.c
floss$ cvs -q status hello.c
===================================================================
File: hello.c                 Status: Up-to-date
   Working revision:  1.5     Tue May  4 20:21:12 1999
   Repository revision:       1.5     /usr/local/cvs/myproj/hello.c,v
   Sticky Tag:                Release-1999_05_01 (revision: 1.5)
   Sticky Date:               (none)
   Sticky Options:            (none)
floss$

CVS は歴史を変えさせてくれない、と言いました。さて、今から歴史を変える方 法を教えます。


Node:Branches, Previous:Other Useful CVS Commands, Up:An Overview of CVS

Branches

ここまで、CVS をインテリジェントで整備された図書館の一種のように見てきま した。しかし、CVS はタイムマシンであると考えることもできます(このたとえ は Jim Blandy によります)。今までは CVS で過去に影響は与えずに調べる方法 だけを見てきました。CVS ではすてきなタイムマシンのように、時を遡って過去 を変えることもできます。そうするとどうなるでしょう? SF ファンならその答 えを知っていますよね: わたしたちの世界と並行するもうひとつの世界が、変え た過去のその時点から分岐するのです。CVS のブランチはプロジェクトの開発を 並行する別々の歴史に分ける働きがあります。あるブランチに加えた変更は別の ブランチには影響しません。


Node:Branching Basics, Next:, Up:Branches

Branching Basics

ブランチは何故便利なのでしょう?

ここであのシナリオに戻りましょう。プログラムの新しいバージョンを作ってい る最中の開発者が、ひとつ前のリリースのバグレポートを受けとったところです。 開発者が問題を解決したとしても、顧客にそのバグフィクス版を届ける方法がわ かりません。いえ、プログラムの古いコピーを取ってきて、CVS の知らないとこ ろでそれを直して出荷する、というのは難しいことではありません。でもこれで は、何をしたか記録が残らないのです。CVS は直しに気づかないまま。もしあと でそのパッチによくないところが発見されても、誰にも問題を再生するスタート 地点さえわかりません。

現在の不安定なバージョンのソースのバグを直して顧客に出荷する、というのは もっと悪いアドバイスですね。そりゃ確かにレポートされたバグについては直っ ているかもしれませんが、その他のところは実装途中でテストもされていない状 態です。動きはするでしょうが、but it's certainly not ready for prime time.

最後のリリースバージョンは、そのバグを除いては安定しているのですから、理 想的な解決策は、時間を戻してその古いリリースのバグを直すことです。つまり、 最後のリリースがこのバグフィクスを取り込むような、もうひとつの世界を作るということです。

ここでブランチの登場です。開発者は、開発のメインライン(トランク(幹))の、 最新リビジョンでなく最後のリリースのところに根を下ろすブランチを作ります。 彼女はこのブランチの作業コピーをチェックアウトして、バグフィクスに必要な 変更を加え、それらをブランチにコミットします。こうすればバグフィクスの記 録ができるというわけです。これで、ブランチに基づく暫定リリースをパッケー ジにして、顧客に出荷できます。

**************************************************************** 彼女の変更はトランクにあるコードに何の影響も与えません

同じバグフィクスがトランクのほうにも必要かどうか見つけだす

Her change won't have affected the code on the trunk, nor would she want it to without first finding out (whether the trunk needs the same bug fix or not). ****************************************************************

もし必要なら、ブランチの変更をトランクにマージすることもできます。CVS は 分岐点からブランチの先端(最新の状態のところ)までに加えられた変更を計算し、 その違いをプロジェクトのトランクの先端に適用します。ブランチの根と先端の 相違分のマージはもちろんうまくいって、バグフィクスになります。

マージはアップデートの特別な場合と考えることもできます。マージでの相違分 というのは、作業コピーとリポジトリを比較するかわりに、ブランチの根と先端 を比較することによって算出されるものです。

アップデートの動作というのは、その変更のパッチを作者から直接受け取って、 それを手でパッチするのと同じです。事実アップデートを実行する際、CVS は作 業コピーとリポジトリの相違分を計算して(相違分というのは diff プログラム がやるわけですが)、その diff を作業コピーに適用します、patch プログラム がやるのと同じようにです。これは、開発者が外の世界からの変更を取りいれる やりかた、パッチを作った人からパッチファイルをもらって手でパッチを当てる というやつですが、それを真似ています。

バグフィクスブランチをトランクにマージするというのは、外のコントリビュー タのパッチを当ててバグを直すのと似ています。コントリビュータは最新のリリー スバージョンへのパッチを作ります。ブランチの変更がそのバージョンに対して なされるのと同じです。現在のソースコードのその領域が最後のリリースからそ う変わっていなければ、マージは問題なく成功するでしょう。しかし、コードが とても変わってしまっていたら、マージはコンフリクトを起こして失敗に終わる でしょう(パッチがリジェクトされるでしょうという意味です)、手で直接ゴソゴ ソやる必要があるわけです。これは通常、コンフリクト領域を読み、手で必要な 変更を施し、そしてコミットすればいいのです。Figure 2.3 はブランチとマー ジで何が起こるかを示した図です。

             (branch on which bug was fixed)
           .---------------->---------------.
          /                                 |
         /                                  |
        /                                   |
       /                                    |
      /                                     V (<------ point of merge)
 ====*===================================================================>
                (main line of development)


[Figure 2.3: A branch and then a merge.  Time flows left to right.]

**************************************************************** We'll now walk through the steps necessary to make this picture happen. **************************************************************** 図で左から右へ流れているのは実際の時間ではなく、リビジョン履歴です。ブラ ンチはリリースの時点で作られたわけではなく、リリースのリビジョンに根を下 ろすように後で作られるものです。

プロジェクト中のファイルは Release-1999_05_01 とタグ付けされてか らもたくさんリビジョンを増やして、ファイルも追加された、と仮定しましょう。 古いリリースに関するバグレポートが舞い込んできたときに最初にしたいのは、 例の古いリリース、Release-1999_05_01 というタグをつけたところに根 を下ろすブランチを作ることです。

ひとつの方法として、まずそのタグに基づいた作業コピーをチェックアウトして、 それから -b (ブランチ, branch) オプションで再度タグづけしてブランチを作 ります。

floss$ cd ..
floss$ ls
myproj/
floss$ cvs -q checkout -d myproj_old_release -r Release-1999_05_01 myproj
U myproj_old_release/README.txt
U myproj_old_release/hello.c
U myproj_old_release/a-subdir/whatever.c
U myproj_old_release/a-subdir/subsubdir/fish.c
U myproj_old_release/b-subdir/random.c
floss$ ls
myproj/      myproj_old_release/
floss$ cd myproj_old_release
floss$ ls
CVS/      README.txt  a-subdir/   b-subdir/   hello.c
floss$ cvs -q tag -b Release-1999_05_01-bugfixes
T README.txt
T hello.c
T a-subdir/whatever.c
T a-subdir/subsubdir/fish.c
T b-subdir/random.c
floss$

最後のコマンドを良く見てください。ブランチを作るのに使うタグは適当でいい ように見えるかもしれませんが、これにはちゃんと理由があります: このブラン チをあとでアクセスするためのラベルとしてこのタグ名は使われます。ブランチ 用のタグはブランチでないタグと変わらないように見えますし、同じネーミング 制限に従っています。ブランチのタグ名には必ず「branch」という単語を居れる ようにしている人もいます(たとえばRelease-1999_05_01-bugfix-branch のように)。こうするとブランチタグとほかの種類のタグを区別できます。よく タグ名をまちがってアクセスしてしまうようなら、そういう風にするのもよいで しょう。

(あと、最初のコマンドでチェックアウトに -d myproj_old_release というオプ ションがつけてあることに注意してください。これはチェックアウトのときに、 作業コピーを myproj_old_release という名前のディレクトリに置くことを指示 するもので、こうしておくと myproj にある現在のバージョンと混同してしまう こともありません。グローバルオプションの -d や、update の -d と混同しな いようにしてください。)

もちろん、単に tag コマンドを実行したからといってこの作業コピーがブラン チに切り替わってしまったりするわけではありません。タグ付けは作業コピーに は影響ありません。作業コピーのリビジョンにあとでアクセスできるように、リ ポジトリ内にちょっとした情報を記録するだけです(履歴内の1コマとして、ある いはブランチとして、場合によります)。

2つのうちどちらかの方法でアクセスできます(もうこのフレーズはそろそろ耳タ コかなあ)。ブランチ上の新しい作業コピーをチェックアウトしましょう:

floss$ pwd
/home/whatever
floss$ cvs co -d myproj_branch -r Release-1999_05_01-bugfixes myproj

あるいは、今ある作業コピーをそっちに切り替えますか:

floss$ pwd
/home/whatever/myproj
floss$ cvs update -r Release-1999_05_01-bugfixes

結果は同じです(新しい作業コピーのトップレベルディレクトリの名前は違いま すが、CVS に関してはあまり重要ではないですね)。現在の作業コピーに未コミッ トの変更があれば、ブランチにアクセスするのに update ではなくて checkout を使いたいだろうと思います。でないと、ブランチに切り替えようとした際、作 業コピーに対して変更をマージしようとしてしまいます。この場合、コンフリク トが起こるかもしれませんし、起こらなくても純粋でないブランチになります。 これでは、作業コピー中のいくつかのファイルは変更されたままの状態なので、 プログラムは指定されたタグをちゃんと反映していないことになります。

とにかく、どちらかの方法でお望みのブランチの作業コピーを取得できたとしま しょう:

floss$ cvs -q status hello.c
===================================================================
File: hello.c                 Status: Up-to-date
   Working revision:  1.5     Tue Apr 20 06:12:56 1999
   Repository revision:       1.5     /usr/local/cvs/myproj/hello.c,v
   Sticky Tag:                Release-1999_05_01-bugfixes
(branch: 1.5.2)
   Sticky Date:               (none)
   Sticky Options:            (none)
floss$ cvs -q status b-subdir/random.c
===================================================================
File: random.c                Status: Up-to-date
   Working revision:  1.2     Mon Apr 19 06:35:27 1999
   Repository revision:       1.2 /usr/local/cvs/myproj/b-subdir/random.c,v
   Sticky Tag:                Release-1999_05_01-bugfixes (branch: 1.2.2)
   Sticky Date:               (none)
   Sticky Options:            (none)
floss$

(これらの Sticky Tag 行の内容を手短に説明します)hello.c と random.c を変更したら、コミットをかけます

floss$ cvs -q update
M hello.c
M b-subdir/random.c
floss$ cvs ci -m "fixed old punctuation bugs"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in hello.c;
/usr/local/cvs/myproj/hello.c,v  <-  hello.c
new revision: 1.5.2.1; previous revision: 1.5
done
Checking in b-subdir/random.c;
/usr/local/cvs/myproj/b-subdir/random.c,v  <-  random.c
new revision: 1.2.2.1; previous revision: 1.2
done
floss$

リビジョン番号を見ると、なんだかおもしろいことが起こっている様子なのに気 づきませんか:

floss$ cvs -q status hello.c b-subdir/random.c
===================================================================
File: hello.c                 Status: Up-to-date
   Working revision:  1.5.2.1 Wed May  5 00:13:58 1999
   Repository revision:       1.5.2.1 /usr/local/cvs/myproj/hello.c,v
   Sticky Tag:                Release-1999_05_01-bugfixes (branch: 1.5.2)
   Sticky Date:               (none)
   Sticky Options:            (none)
===================================================================
File: random.c                Status: Up-to-date
   Working revision:  1.2.2.1 Wed May  5 00:14:25 1999
   Repository revision:       1.2.2.1 /usr/local/cvs/myproj/b-subdir/random.c,v
   Sticky Tag:                Release-1999_05_01-bugfixes (branch: 1.2.2)
   Sticky Date:               (none)
   Sticky Options:            (none)
floss$

数字が2つではなくて、4つになっています!

よく見ると、各ファイルのリビジョン番号はブランチ時の番号( Sticky Tag 行に示されています)の最後に余分な数字をつけ加えて あるようですね:

いま見ているのは CVS の内部作業の片鱗です。ほとんどいつもは、プロジェク ト全体の分岐をマークするためにブランチを使いますが、CVS は実際はファイル 毎にブランチを記録しています。このプロジェクトには、このブランチの分岐時 に5つのファイルがあったので、5つの個別のブランチが作成され、みな同じタグ 名がつけられました(Release-1999_05_01-bugfixes)。

CVS の実装のうち、このようなファイルごとのやりかたは、少々エレガントでな いという意見が多いです。これは RCS の遺物なのです、RCS ではファイルをま とめてプロジェクトにしたりできませんでした。CVS は、RCS のブランチを扱う ところのコードを受け継いでいるのでこうなっているのです。

通常は CVS がファイル等を内部的にどのように追跡しているかなんてあまり考 える必要はありませんが、今回の場合は、ブランチ番号とリビジョン番号の関係 について理解する助けになります。hello.c を見ていきましょう、これから hello.c に関して言うことは、ブランチの他のファイルについてもあてはまりま す(リビジョン/ブランチ番号は適当に読み替えてください)

hello.c のリビジョンは、ブランチが根を下ろした点で1.5でした。ブランチを 作成した時、その端には番号がつけられてそれがブランチ番号になります(CVS は、まだ使われていない非ゼロの偶数整数値をひとつ選んでつけます)。そうす るとこの場合、ブランチ番号は 1.5.2 となります。ブランチ番号そのも のはリビジョン番号ではないですが、ブランチ番号はそのブランチ上の hello.c のリビジョン番号の根(プレフィクス)になります。

しかし、ブランチ後の作業コピーで status コマンドを実行すると、hello.c の リビジョン番号は単に 1.5 となっていて、1.5.2.0 とかではな いようです。これは、ブランチ上の最初のリビジョン番号というのは、枝分かれ した点のトランク上のリビジョン番号とつねに同じだからです。ですから、その ファイルがブランチ上で変更されずにトランク上のと同じあいだは、CVS は status の出力にトランクのリビジョン番号を出力します。

hello.c の新しいリビジョンをコミットすれば、hello.c はトランク上とブラン チ上で違ってしまいます。ブランチ上のファイルは変わってしまいますが、トラ ンク上のファイルは変わりませんから。従って、ブランチ上の hello.c には最 初のブランチリビジョン番号が割り当てられます。コミットしたあとに status の出力を見れば、リビジョン番号が 1.5.2.1 になったことがわかります。

random.c ファイルについても同じことが言えます。ブランチ時点でのリビジョ ン番号は 1.2 ですから最初のブランチは 1.2.2、random.c をブ ランチ上に最初にコミットした時には 1.2.2.1 となります。

1.5.2.11.2.2.1 の間に数字的なつながりはなにもありませ ん。同じ時にブランチしたものだということすらわかりません。両者に Release-1999_05_01-bugfixes というタグがあって、そのタグはそれぞ れ 1.5.21.2.2 というブランチ番号についている、というこ と以外には。従って、プロジェクト全体にわたるものとしてブランチを指定する ためにはタグ名を使うしかありません。リビジョン番号を直接指定してファイル をブランチ上のものにすることは可能ですが、

floss$ cvs update -r 1.5.2.1 hello.c
U hello.c
floss$

これはよくないやりかたです。ブランチリビジョンのファイルを1つ、他のブラ ンチでないリビジョンのファイルと混ぜてしまうことになります。その結果どん な損失があるかわかりません。ブランチにアクセスするときはブランチタグを使 ってすべてのファイルに一度にアクセスすることです。特定のファイルだけ指定 してはいけません。そうすれば、各ファイルの実際のブランチリビジョン番号に ついて知らなくても気にしなくてもかまいません。

ブランチを持つブランチを作ることもでき、不合理なくらいのレベルさえ可能で す。たとえば、あるファイルがリビジョン番号 1.5.4.37.2.3 の場合、 次の図のような履歴になっていると思います:

                  1.1
                   |
                  1.2
                   |
                  1.3
                   |
                  1.4
                   |
                  1.5
                 /   \
                /     \
               /       \
           (1.5.2)   (1.5.4)         <--- (these are branch numbers)
             /           \
         1.5.2.1        1.5.4.1
            |              |
         1.5.2.2        1.5.4.2
            |              |
          (etc)          (...)       <--- (collapsed 34 revisions for brevity)
                           |
                        1.5.4.37
                          /
                         /
                   (1.5.4.37.2)      <--- (this is also a branch number)
                       /
                      /
               1.5.4.37.2.1
                     |
               1.5.4.37.2.2
                     |
               1.5.4.37.2.3

[Figure 2.4: An unusually high degree of branching.  Time flows downward.]

こんなに深いブランチを作るような状況になることはめったにありませんが、し たいと思ったときにはいつでもできるのです。普通のブランチと同じように、ネ ストしたブランチを作成することもできます。ブランチ N の作業コピー をチェックアウトして、その中で cvs tag -b branchname を実行すれば、ブラ ンチ N.M がリポジトリの中にできます(N は各ファイルのブラン チリビジョン番号で(1.5.2.1など)、M はその番号で終わる次の ブランチを表しています(2など))。


Node:Merging Changes From Branch To Trunk, Next:, Previous:Branching Basics, Up:Branches

Merging Changes From Branch To Trunk

さて、ブランチ上にバグフィクスがコミットされました。ここで作業コピーをト ランクのリビジョンの一番大きいものに切り替えて、そっちでもそのバグフィク スをする必要があるかどうか見てみます。update -A を使って作業コピーをブラ ンチから脱出させます(この点、ブランチタグはほかのスティッキーと同じです)。 そのあと、いま離れてきたブランチと diff をとってみましょう:

floss$ cvs -q update -A
U hello.c
U b-subdir/random.c
floss$ cvs -q diff -c -r Release-1999_05_01-bugfixes
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.5.2.1
retrieving revision 1.6
diff -c -r1.5.2.1 -r1.6
*** hello.c   1999/05/05 00:15:07     1.5.2.1
--- hello.c   1999/05/04 20:19:16     1.6
***************
*** 4,9 ****
  main ()
  {
    printf ("Hello, world!\n");
!   printf ("between hello and good-bye\n");
    printf ("Goodbye, world!\n");
  }
--- 4,10 --
  main ()
  {
    printf ("Hello, world!\n");
!   printf ("between hello and goodbye\n");
    printf ("Goodbye, world!\n");
+   /* a comment on the last line */
  }
Index: b-subdir/random.c
===================================================================
RCS file: /usr/local/cvs/myproj/b-subdir/random.c,v
retrieving revision 1.2.2.1
retrieving revision 1.2
diff -c -r1.2.2.1 -r1.2
*** b-subdir/random.c 1999/05/05 00:15:07     1.2.2.1
--- b-subdir/random.c 1999/04/19 06:35:27     1.2
***************
*** 4,8 ****
  void main ()
  {
!   printf ("A random number.\n");
  }
--- 4,8 --
  void main ()
  {
!   printf ("a random number\n");
  }
floss$

diff の結果では、ブランチリビジョンでは good-bye がハイフンつきになって いて、そのファイルのトランクリビジョンのほうには最後近くにブランチのほう にないコメントがついています。一方 random.c ですが、ブランチリビジョンの ほうでは A がキャピタライズされてピリオドがついていますが、トランクのほ うではそうなっていません。

ブランチを実際に現在の作業コピーにマージするには、update を -j フラグを つけて実行します(以前、古いリビジョンに戻すときに使った j と同じ、join という意味です):

floss$ cvs -q update -j Release-1999_05_01-bugfixes
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.5
retrieving revision 1.5.2.1
Merging differences between 1.5 and 1.5.2.1 into hello.c
RCS file: /usr/local/cvs/myproj/b-subdir/random.c,v
retrieving revision 1.2
retrieving revision 1.2.2.1
Merging differences between 1.2 and 1.2.2.1 into random.c
floss$ cvs -q update
M hello.c
M b-subdir/random.c
floss$ cvs -q ci -m "merged from branch Release-1999_05_01-bugfixes"
Checking in hello.c;
/usr/local/cvs/myproj/hello.c,v  <-  hello.c
new revision: 1.7; previous revision: 1.6
done
Checking in b-subdir/random.c;
/usr/local/cvs/myproj/b-subdir/random.c,v  <-  random.c
new revision: 1.3; previous revision: 1.2
done
floss$

こうすると、ブランチの根から先端までの変更を計算し、それを現在の作業コピー にマージします(その後に、まるでそのファイルを手で編集してその状態にした かのように、変更を表示しています)。そして、作業コピーにマージしただけで はリポジトリには変更は反映されないので、この変更をトランクにコミットしま す。

この例ではコンフリクトは起きませんでしたが、通常のマージでは起こりがちな (たぶん起こる)ことです。そうなったら、ほかのコンフリクトを解消するのと同 じように解消作業をして、それからコミットしてください。


Node:Multiple Merges, Next:, Previous:Merging Changes From Branch To Trunk, Up:Branches

Multiple Merges

トランクにマージした後にも、ブランチ上での開発が続くことがあります。たと えば前のリリース版に2つめのバグが見つかった場合、それはブランチ上でバグ フィクスされますね。random.c の冗談がわからない人がいたとしましょう、そ うするとブランチ上でそれを説明する行をつけ加えなければいけません、

floss$ pwd
/home/whatever/myproj_branch
floss$ cat b-subdir/random.c
/* Print out a random number. */
#include <stdio.h>
void main ()
{
  printf ("A random number.\n");
  printf ("Get the joke?\n");
}
floss$

そしてコミットします。このバグフィクスもトランクにマージする必要がある場 合、トランクの作業コピーに再マージするため、前回と同じように update コマ ンドを走らせようとしてしまうと思います:

floss$ cvs -q update -j Release-1999_05_01-bugfixes
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.5
retrieving revision 1.5.2.1
Merging differences between 1.5 and 1.5.2.1 into hello.c
RCS file: /usr/local/cvs/myproj/b-subdir/random.c,v
retrieving revision 1.2
retrieving revision 1.2.2.2
Merging differences between 1.2 and 1.2.2.2 into random.c
rcsmerge: warning: conflicts during merge
floss$

ごらんの通り、望んだ結果は得られません。あれからトランクには変更を加えて いないからコンフリクトは起こらないはず、のような気がするのに、起こってし まいました。

この問題は update コマンドが説明した通りの動作をしたことによって起こりま した: ブランチの根と先端の変更を計算して、現在の作業コピーにマージする、 のです。問題はこれらの変更のうちのいくつかが既に作業コピーにマージされて いた、ということにありました。だからコンフリクトしたのです:

floss$ pwd
/home/whatever/myproj
floss$ cat b-subdir/random.c
/* Print out a random number. */
#include <stdio.h
void main ()
{
<<<<<<< random.c
  printf ("A random number.\n");
=======
  printf ("A random number.\n");
  printf ("Get the joke?\n");
>>>>>>> 1.2.2.2
}
floss$

これらのコンフリクトを手で解消してもかまいません、各ファイルそれぞれやっ てね、と言うのは簡単なことです。まあでも最初っからコンフリクトしないよう にするほうがいいですよね。-j フラグを1つではなく2つ渡すことによって、ブ ランチの根からでなく以前のマージのところから先端までの変更をマージするこ とができます。最初の -j はブランチ上の開始地点、2つめはただブランチ名だ けを書けばいいです(これはブランチの先端という意味になります)。

問題は、どうやってブランチ上で以前のマージの点を指定すればいいか、という ことです。ブランチタグ名に日付をつけて指定するのが1つの方法です。CVS は このために特別な書きかたを用意しています:

floss$ cvs -q update -j "Release-1999_05_01-bugfixes:2 days ago" \
                     -j Release-1999_05_01-bugfixes
RCS file: /usr/local/cvs/myproj/b-subdir/random.c,v
retrieving revision 1.2.2.1
retrieving revision 1.2.2.2
Merging differences between 1.2.2.1 and 1.2.2.2 into random.c
floss$

ブランチタグ名にコロンと日付をつなげると(普通の CVS の日付指定のやりかた ならどれでもいいです)、CVS はその日付以降の変更を取ってきます。最初のバ グフィクスが3日前にコミットされたのを知っていれば、上記のコマンドで2つめ のバグフィクスだけをマージできます。

もう少し良い方法は、前もってやっとかないとだめなんですが、各バグフィクス の後にブランチにタグをつけておく方法です(普通のタグです、新しいブランチ を分岐させたりするようなやつではなくて)。バグをフィクスしてコミットした ら、ブランチの作業コピーでこういうのを実行してください:

floss$ cvs -q tag Release-1999_05_01-bugfixes-fix-number-1
T README.txt
T hello.c
T a-subdir/whatever.c
T a-subdir/subsubdir/fish.c
T b-subdir/random.c
floss$

こうしておけば、2つめの変更をトランクにマージする時にはこのタグを使って、 簡単に前のリビジョンと区別をつけられます。

floss$ cvs -q update -j Release-1999_05_01-bugfixes-fix-number-1 \
                     -j Release-1999_05_01-bugfixes
RCS file: /usr/local/cvs/myproj/b-subdir/random.c,v
retrieving revision 1.2.2.1
retrieving revision 1.2.2.2
Merging differences between 1.2.2.1 and 1.2.2.2 into random.c
floss$

変更を加えたのが何日前だったか、とか思い出さないといけない方法より、この 方法のほうが断然いいのですが、この方法を使おうとするとトランクにマージす るたびにブランチにタグをつけるのを忘れないようにせねばなりません。この話 の教訓は、前もって、度々タグをつけよ、ということですね。タグは少なすぎる より多すぎるほうがマシです(説明的な名前をつけている限りにおいてはです)。 たとえば上の例で言うと、ブランチ上の新しいタグ名はブランチタグと似ている 必要はないわけです。ですから簡単に fix1 という名前をつけてもよかっ たのですが、Release-1999_05_01-bugfixes-fix-number-1 という名前に しました。他のブランチのタグと混同しないためには後者のほうがいいのです。 (タグ名はブランチ内でユニークではなく、ファイル内でユニークであることを 思い出してください。別のブランチだからといって、同じファイルに fix1 という名前のタグをふたつつけたりはできません。)


Node:Creating A Tag Or Branch Without A Working Copy, Previous:Multiple Merges, Up:Branches

Creating A Tag Or Branch Without A Working Copy

前にも述べたとおり、タグ付けというのは作業コピーでなくリポジトリに影響を 与えます。では、タグをつけるときに作業コピーはいらないんじゃないか、とい う疑問が出てくると思います。作業コピーは、どのプロジェクトが対象で、各ファ イルがどのリビジョンかというのを指定するために必要なだけです。作業コピー に関係なくプロジェクトとリビジョンを指定できたとしたら、作業コピーは必要 ないのです。

こんな方法があります: rtag コマンドです("repository tag")。これは tag コ マンドととても似たものなので、例を2つだけ示して使いかたの説明にかえます。 最初のバグレポートが来たときに戻りましょう。最後のリリースに根を下ろすブ ランチを作らなければなりません。リリースのタグを指定して作業コピーをチェッ クアウトし、tag -b を実行しました:

floss$ cvs tag -b Release-1999_05_01-bugfixes

こうすると Release-1999_05_01 に根を下ろすブランチが作成されます。 しかし、リリースのタグを知っているなら、それを使ってブランチの根の場所を 指定して rtag コマンドを実行すればいいのです、作業コピーを作る必要はあり ません:

floss$ cvs rtag -b -r Release-1999_05_01 Release-1999_05_01-bugfixes myproj

これだけでおわりです。このコマンドは作業コピーの中でも外でも、どちらで実 行してもしてもかまいません。ただし、CVSROOT 環境変数にリポジトリの場所を 設定しておくか、-d グローバルオプションで指定する必要があります。このコ マンドでブランチでないタグをつけることもできますが、各ファイルのリビジョ ン番号をひとつひとつ指定しなければならないので、あまり便利とは言えません。 (それをタグで参照することも可能ですが、ということはもうタグがついている わけで、同じリビジョンに2つもタグをつけても仕方ないですよね)

さて、CVS でいろいろできるくらいに知識がつき、プロジェクトで他の人と作業 できるくらいにはなったと思います。あまり重要でない機能いくつかと、ここま でに見てきた機能につける便利なオプションいくつかをまだ説明していません。 このあと適切な章のなかで、どのように、どういう時につかえば良いかを実地例 で説明します。よくわからない時には迷わず Cederqvist マニュアルを読んでみ ること、本気で CVS を使う人には不可欠なものですから。


Node:Repository Administration, Next:, Previous:An Overview of CVS, Up:Top

Repository Administration

An Overview of CVS ではプロジェクトの一参加者として CVS を使う方 法を学びました。が、プロジェクト管理者になる場合にはそれに加え、CVS の インストールとリポジトリ管理の方法を知る必要があります。この章では、リ ポジトリがどういう構造になっていて CVS がそれをどう使っているかを、カー テンを取って詳しく見ます。また、アップデートやコミットの際に CVS が通 過する主なステップと、その動作の変更のしかたを学びます。CVS がどのよう に動いているかを理解すれば、問題が起こった時に原因を調べて、保守性のよ いやりかたで解決することもできます。

とても involved に聞こえるかもしれませんが、CVS は長生きすることは間違 いなく、何年も使うことになるでしょう。今から勉強することは長い間役に立 つことになるのです。CVS は使えば使うほど不可欠になります。何かに頼ろう とする時には(絶対そうなるって)、それを知る価値があるのです。

ということを心に留めておいて、始めましょう。まずは CVS をあなたのシス テムに導入するところから。


Node:Getting And Installing CVS, Next:, Up:Repository Administration

Getting And Installing CVS

既にシステムに入っていて、わざわざ CVS を取りに行ったり必要はないこと が多いです。主な Linux や FreeBSD のディストリビューションを使っている 場合には、/usr/bin とかにインストールされていると思います。インストー ル済みでなくても、Red Hat Linux ユーザなら最新またはほとんど最新の CVS の RPM (Red Hat Package) がディストリビューションにあります。Debian ユー ザなら以下のコマンドでインストールできます:

floss$ apt-get update
floss$ apt-get install cvs

CVS がマシン上にない場合はソースから作ります。Unix ユーザでない場合は その OS 用のバイナリが見つかると思います。幸い CVS は autoconfiscated で、GNU autoconf を使ってソースのコンパイルが非常に簡単になっています。


Node:Getting And Building CVS Under Unix, Next:, Up:Getting And Installing CVS

Getting And Building CVS Under Unix

これを書いている時点で、CVS の正規ダウンロードサイトが2つあります。ひ とつはフリーソフトウェアファウンデーション(FSF)のFTPサイト ftp:/ /ftp.gnu.org/gnu/cvs/ で、公式な GNU ツールとして CVS を提供していま す。Cyclic Software は CVS のメンテナ、じゃなくて「メンテナのメンテナ」 で、ユーザと開発者向けにリポジトリサーバとダウンロードサイトを提供して います。http://download.cyclic.com/pub/ でリリースを配布してい ます。

どちらでもかまいません。下の例では Cyclic Software のサイトを利用しま した。FTP クライアント(ウェブブラウザかもしれません)でそこへアクセスす るとディレクトリ一覧が見えます、こんな感じ:

Index of /pub
    cvs-1.10.5/            18-Feb-99 21:36      -
    cvs-1.10.6/            17-May-99 10:34      -
    cvs-1.10/              09-Dec-98 17:26      -
    macintosh/             23-Feb-99 00:53      -
    os2/                   09-Dec-98 17:26      -
    packages/              09-Dec-98 17:26      -
    rcs/                   09-Dec-98 17:26      -
    tkcvs/                 09-Dec-98 17:26      -
    training/              09-Dec-98 17:26      -
    unix/                  09-Dec-98 17:26      -
    vms/                   09-Dec-98 17:26      -

「cvs-」で始まるディレクトリに注意して下さい(ほかのは無視して構いませ ん)。cvs- ではじまるディレクトリは3つあって、ここで選択せねばなりませ ん: 安定した(stable)リリースがいいか、新しい(けどテストの少ない)暫定バー ジョンを追いかけるか。安定したリリースは「cvs-1.10」のように小数点が1 つだけで、暫定リリースは "1.10.5" のように最後にマイナーバージョンをつ けてあってその数字が増えていきます。

GNU のサイトは通常メジャーリリースだけを提供して暫定のは提供しませんか ら、GNU のほうから CVS を取ってくる場合はこれら全部が見えるわけではあ りません。一般に、暫定リリースは大抵安全で、メジャーリリースで見つかっ たバグのバグフィクスが入っています。一番いいのは暫定リリースを追いかけ て、問題があったら必要なだけ前のリリースに戻すというやりかたです。さき ほどの例で一番新しい暫定リリースは cvs-1.10.6 です。ディレクトリに入ると

Index of /pub/cvs-1.10.6
    cvs-1.10.6.tar.gz      17-May-99 08:44   2.2M

というのが見えます。これが CVS のソースコードです。これをダウンロード し、コンパイルします。GNU ツールのコンパイルに慣れていれば何をするかわ かるでしょうから、ここから Anatomy Of A CVS Distribution の節ま で飛ばしてもかまいません。慣れていなければ何をしていいかわからないでし ょうから、読みつづけて下さい…

ここからのコンパイル方法と例では、標準の Unix ディストリビューションを 前提にしています。フリーの Unix (FreeBSD や Linux) や、主な商業 Unix (SunOS/Solaris, AIX, HP-UX, Ultrix など)ならば問題ないと思います。もし 書いてある通りのやり方でうまくいかなくても諦めないで下さい。各 OS のコ ンパイルの詳しいところまではこの本の範囲ではありませんが、この章の最後 に参考になるリソースへのポインタを示します。

とにかくコンパイルを進めましょう、まず GNU gunzip と tar を使って tar ファイルをほどきます(これらがインストールされていない場合、gunzip は ftp://ftp.gnu.org/gnu/gzip/ で、GNU の tar は ftp://ftp.gnu.org/gnu/tar/ で手に入ります)。

floss$ gunzip cvs-1.10.6.tar.gz
floss$ tar xvf cvs-1.10.6.tar

スクリーン上をファイル名がたくさん流れるのが見えると思います。

これで新しいディレクトリ cvs-1.10.6 ができ、その中に CVS のコードがあ ります。ディレクトリの中に移り、そこにある configure スクリプトで CVS をそのシステム用に合わせましょう:

floss$ cd cvs-1.10.6
floss$  ./configure
creating cache ./config.cache
checking for gcc... gcc
checking whether we are using GNU C... yes
checking whether gcc accepts -g... yes
checking how to run the C preprocessor... gcc -E
  (etc)

configure コマンドが終わると、ソースツリーはそのマシンでコンパイルする ために必要なことをすべて知っている状態になります。次はこうします:

floss$ make

出力がたくさん流れるのが見えると思います。その次はこうです:

floss$ make install

(最後のステップではスーパユーザになる必要があります) また出力が流れます、 これが終わると、CVS はマシンにインストールされています。

デフォルトでは CVS の実行ファイルは /usr/local/bin/cvs となりま す。それなりの make プログラムがシステムにインストールされていればの話 です(持っていなければ GNU プロジェクトの make を ftp://ftp.gnu.org/gnu/make/ から持っていって下さい)

CVS を /usr/local/bin 以外の場所にインストールしたければ、最初のコンフ ィギュレーションのやりかたを変えます。例えば

floss$ ./configure --prefix=/usr

とすると、CVS は /usr/bin/cvs としてインストールされます(PREFIX/bin/cvs になるわけです)。デフォルトの prefix は /usr/local になっていて、たい ていのインストールではこれでよいと思います。

今までのユーザのかたへ、以前のバージョンでは CVS は1つの実行ファイルに なっておらず、また、RCS がインストールされていることに依存していました が、バージョン 1.10 ではそうなっていません。ですから、cvs 以外のライブ ラリや実行ファイルのことを気にかけなくてもよくなりました。

リモートリポジトリにアクセスするためだけに CVS を使う場合は、ここまで でおしまいです。リポジトリを提供しようとしている場合には、ステップがあ といくつか残っています。この章のなかで、あとで説明します。


Node:Getting And Installing CVS Under Windows, Next:, Previous:Getting And Building CVS Under Unix, Up:Getting And Installing CVS

Getting And Installing CVS Under Windows

ソースコードから実行ファイルを作ることについてこだわっているのでなけれ ば、 CVS を Windows 上でソースコードからコンパイルする必要はありません。 Unix とは違ってコンパイルツールがない場合が多いので、ソースから作ろう と思えばそのようなツールを持ってくることから始めなければならなくなりま す。そういうことはこの本の範囲ではありませんので、前もってコンパイルさ れた CVS バイナリをインストールする方法を教えます。

CVS の Windows バイナリディストリビューションは通常、メジャーリリース の分についてのみ作られることに注意して下さい、暫定リリースの分はありま せん。また、GNU FTP のほうには置いてありません。Cyclic Software のダウ ンロードサイトのメジャーバージョンディレクトリ http://download.cyclic.com/pub/cvs-1.10/ にアクセスしてください。 そうすると特別なサブディレクトリが見えます:

Index of /pub/cvs-1.10
    cvs-1.10.tar.gz        14-Aug-98 09:35   2.4M
    windows/

この中には ZIP ファイルがあります:

Index of /pub/cvs-1.10/windows
    cvs-1.10-win.zip       14-Aug-98 10:10   589k

この ZIP ファイルは CVS のバイナリディストリビューションを含んでいます。 その ZIP ファイルをダウンロードしてほどいてください:

floss$ unzip cvs-1.10-win.zip

Archive:  cvs-1.10-win.zip
  inflating: cvs.html
  inflating: cvs.exe
  inflating: README
  inflating: FAQ
  inflating: NEWS
  inflating: patch.exe
  inflating: win32gnu.dll

README に詳しいインストール方法が書いてあります。インストールはだいた い次のような手順になります: EXE と DLL ファイルを PATH の通ったディレ クトリに置きます。リモートのリポジトリに pserver アクセスするつもりな ら C:\AUTOEXEC.BAT に次のように書いてください:

set HOME=C:

これは .cvspass をどこに置くか指示するものです。そしてリブートします。

Windows 上で CVS を動かしても、現時点ではリモートのマシンにリポジトリ を提供できません。クライアントになれる(リモートのリポジトリに接続でき る)のと、ローカルモードで作業できる(同じマシンのリポジトリを使う)だけ です。この本では大部分で Windows で動く CVS はクライアントだと仮定して います。しかし、この章の残りの Unix 向けのところを読めば、Windows でロー カルリポジトリを設定することも難しくはありません。

リモートリポジトリにアクセスしているだけなら、CVS を走らせる必要さえあ りません。クライアントの機能を実装した WinCvs というツールがあります。 CVS そのものとは別に、それだけで配布されていますが、CVS と同様 GNU GPL ライセンス下で使用できます。詳しい情報は http://www.wincvs.org にてご覧ください。


Node:Getting And Installing CVS On A Macintosh, Next:, Previous:Getting And Installing CVS Under Windows, Up:Getting And Installing CVS

Getting And Installing CVS On A Macintosh

CVS は Macintosh でも利用できますが、メインのディストリビューションに は入っていません。今のところ、それぞれ異なる3つの Macintosh 用 CVS ク ライアントが存在します:

正直に言うと、どれが一番いいかわからないです。全部使ってみて(上の順番 通りに試す必要はありません)、いいと思うものを選んで下さい。MacCVS は WinCVS と同じホームページですし明らかに兄弟プロジェクトですね(これを書 いている今、WinCVS のページに「MacCvs の開発はまもなく再開されます」と いう意味のお知らせがあります)。


Node:Limitations Of The Windows And Macintosh Versions, Previous:Getting And Installing CVS On A Macintosh, Up:Getting And Installing CVS

Limitations Of The Windows And Macintosh Versions

CVS の Windows 及び Macintosh ディストリビューションは機能に制限があり ます。これらはクライアントとしては動きます。つまりリポジトリサーバに接 続して作業コピーを取得し、コミット、アップデートなどを実行できます。し かしリポジトリを提供することはできません。正しく設定すれば Windows 用 のはローカルディスクリポジトリを使えますが、他のマシンへプロジェクトの リポジトリを提供することはできないのです。一般に、ネットワーク経由でア クセスできる CVS リポジトリを作りたい場合には Unix マシン上で CVS サー バを動かす必要があります。


Node:Anatomy Of A CVS Distribution, Next:, Previous:Getting And Installing CVS, Up:Repository Administration

Anatomy Of A CVS Distribution

ここまで説明してきた方法は、とにかく始められるよう、早く動かすことが目 的でした。しかし、CVS のソースディストリビューションにはコードだけでな くいろいろ入っています。ここではソースツリーの早わかりロードマップをお 見せします。便利なものがいろいろあるので無視できないと思いますよ。


Node:Informational Files, Next:, Up:Anatomy Of A CVS Distribution

Informational Files

ディストリビューションツリーのトップレベルにファイルがいくつかあります が、これには有用な情報が書いてあります(あるいはより詳しい情報へのポイ ンタが書いてあります)。以下、重要な順に説明します:


Node:Subdirectories, Next:, Previous:Informational Files, Up:Anatomy Of A CVS Distribution

Subdirectories

CVS ディストリビューションにはたくさんのサブディレクトリがあります。通 常のインストールではその中を見たりしませんが、それぞれが何なのか知って おいても良いでしょう。

contrib/
diff/
doc/
emx/
lib/
man/
os2/
src/
tools/
vms/
windows-NT/
zlib/

大半は無視してかまいません。emx/, os2/, vms/, windows-NT/ には OS 依存 のコードがおさめてあって、CVS のコードレベルでデバッグするときくらいし か必要ないでしょう(あんまりありそうもない状況です、聞かないわけではな いですが)。diff/, zlib/ サブディレクトリはそれぞれ CVS の内部実装の diff プログラムとGNU gzip 圧縮ライブラリ部分です。(ネットワーク越しにリモー トリポジトリにアクセスする際にビット数を減らすため、後者を使っています)

contrib/, tools/ サブディレクトリに CVS とともに使用するフリーのサード パーティーソフトウェアをおさめてあります。contrib/ には特定の用途に専 門化された小さなシェルスクリプトの一群があります(contrib/README に何を するものか書いてあります)。tools/ サブディレクトリにはコントリビュート されたソフトウェアがあって、README にはこうあります:

以前このサブディレクトリには CVS とともに使用するツールがおさめてあり
ました。特に pcl-cvs バージョン1.xがおさめてありました。pcl-cvs は CVS
の Emacs インタフェースです。

pcl-cvs をお探しの場合、こちらにある pcl-cvs バージョン2.xをお勧めします:

    ftp://ftp.weird.com/pub/local/

ここで述べられている PCL-CVS パッケージはとても使いやすいです、のちほ ど Third-Party Tools でもう少し説明します。

src/, lib/ サブディレクトリには CVS の内部を含むソースコード一式がおさ めてあります。主なデータ構造とコマンドの実装は src/ にあって、lib/ に は CVS が一般的に使用する小さなコードモジュールがあります。

man/ サブディレクトリに CVS の man page があります(Unix のオンラインマ ニュアルシステム用)。ここにある man page は make install を実行したと きにシステムの man page におさめられているので

floss$ man cvs

とタイプすると簡潔な紹介と CVS のサブコマンドのリファレンスが読めます。 これはクイックリファレンスとしては便利ですが、Cederqvist マニュアル(次 の節参照のこと)ほどには最新でもないし完璧でもありません。しかし間違っ ているわけではなくて不完全なだけですから、何かの足しになればと思います。


Node:The Cederqvist Manual, Next:, Previous:Subdirectories, Up:Anatomy Of A CVS Distribution

The Cederqvist Manual

残りは doc/ サブディレクトリですが、このディレクトリの中の一番重要な住 人はあの有名な Cederqvist です。最近では "the Cederqvist" と呼ば れるようです。Per Cederqvist(Signum Support, Linkoping Sweden,www.signum.se) が 1992 年頃最初のバージョンを書き、それ以来たくさんの人々によって更新 されてきました。例えば、コントリビュータが新しい機能を CVS に追加したら、 Cederqvist にもドキュメントを残す、というかたちで。

Cederqvist マニュアルは GNU project でも使用されている Texinfo 形式で 書かれています。この形式にしておくと、オンライン出力もプリントアウト出 力も比較的容易に生成できるのです(それぞれ、Info と PostScript)。 doc/cvs.texinfo がマスタファイルですが、 CVS ディストリビューションには Info と PostScript 形式も入っていますから、自前で Texinfo ツールを走ら せる必要はありません。

Cederqvist は導入とチュートリアルとしても使用できますが、リファレンス ドキュメントとして使うのが一番便利です。そのため、多くの人はこれをプリ ントアウトせずにオンラインで閲覧しています(紙に印刷しておきたい人のた めに、PostScript ファイルは doc/cvs.ps です)。もし今回初めて CVS をインストールしたのなら、マニュアルにアクセスできるようにするために あとひと手間必要です。

Info ファイル (doc/cvs.info, doc/cvs.info-1, doc/cvs.info-2, など) は make install した時にインストールされました。しかしそれは Info ツリー にコピーされただけなので、Info の目次("Top" ノード)に1行追加する必要が あるのです。(これは最初にインストールした時だけ必要なことです、2回目以 降なら以前のインストールの時に追加したエントリがありますから)

もし今までに Info ドキュメントをシステムに追加したことがあるならこの手 順には慣れていると思います。まず Info ページがどこにインストールされて いるか探して下さい。デフォルトのインストールを使っていれば(/usr/local/)、 Info ファイルは /usr/local/info/cvs.info* です。次のようにインストール したのであれば

floss$ ./configure --prefix=/usr

Info ファイルは /usr/info/cvs.* になります。ファイルの場所が分かったら、 Info の目次に行を追加して下さい。そのディレクトリの dir という名前のフ ァイルです(後者の例では /usr/info/dir です)。root 権限がない場合はシス テム管理者にそれをしてくれるよう頼んで下さい。以下は CVS ドキュメント が追加される前の dir からの抜粋です:

* Bison: (bison).         The Bison parser generator.
* Cpp: (cpp).             The GNU C preprocessor.
* Flex: (flex).           A fast scanner generator

追加後の同じところは次の通り:

* Bison: (bison).         The Bison parser generator.
* Cpp: (cpp).             The GNU C preprocessor.
* Cvs: (cvs).             Concurrent Versions System
* Flex: (flex).           A fast scanner generator

追加する行の形式はとても重要です。* Cvs: にはアスタリスク、 スペース、コロンがあって、そのあとの (cvs). は括弧とピリオドが あります。どれが欠けても Info dif のフォーマットとして間違いで、Cederqvist が読めません。

マニュアルがインストールされ、目次から参照されるようになったら、Info 互換ブラウザで読めるようになります。典型的な Unix システムにインストー ルされているのは、以下のように起動すれば CVS のページへ直接行けるコマ ンドライン Info リーダか、

floss$ info cvs

Emacs 中で

M-x info

あるいは

C-h i

とタイプすることによって起動されるものです。

CVS インストール時には、Cederqvist をきちんと読めるよう設定するために 時間を取って下さい。何度も調べる必要ができてくるのですから、その分の時 間の節約ができます。


Node:Other Sources Of Information, Previous:The Cederqvist Manual, Up:Anatomy Of A CVS Distribution

Other Sources Of Information

ディストリビューション中の Cederqvist, FAQ 他のファイルに加え、インター ネット上にも CVS に関するリソースがあります。CVS サーバを管理しようと している場合、info-cvs メーリングリストに参加したいなと思うかもしれま せん。購読するには、info-cvs-request@gnu.org にメールを送って 下さい(メーリングリスト本体は info-cvs@gnu.org です)。流量は 中から多量、1日に約20通程度で、大半が質問のメールです。たいがいは読ま ずに削除しても構わないですが(質問に回答する時は別です、素敵なことです ね)、時々、バグを発見したという報告や、ずっと欲しかった新しい機能を実 装するパッチのアナウンスがあるかもしれません。

正式なバグレポートメーリングリストに参加することもできます。ここにはバ グレポートがすべて送られます。バグをフィクスしたい人(素晴らしいことで すね)か、偏執狂気味で CVS について他の人が見つけた問題を全部知っておか ないと気が済まない人以外には必要ないと思います。参加したければ bug-cvs-request@gnu.org までメールを送って下さい。

Usenet ニュースグループもあります、comp.software.config-mgmt と いうものです。これはバージョン管理と設定管理一般のニュースグループで、 CVS についても少なからず議論されているようです。

最後に CVS 関連のウェブサイトを。少なくとも3つあります。ここ数年間、Cyclic Software の http://www.cyclic.com は CVS の非公式なホームサイト であり、多分近い未来についてもそうでありつづけると思います。Cyclic Software はサーバスペースと、CVS のソースが入ったリポジトリのネットワークアクセ スを提供しています。また、Cyclic のウェブページには広範囲にわたるリン クがあります。CVS の実験的パッチあり、CVS とともに使用するサードパーテ ィーツールあり、ドキュメントやメーリングリストアーカイブあり。他にもい ろいろあります。分散したリソースの中で、必要なものが見つからない時は、 http://www.cyclic.com から見始めるのがよいでしょう。

ステキなサイトをあと2つ紹介しましょう。Pascal Molli の http://www.loria.fr/~molli/cvs-index.html と、Sean Dreilinger の http://durak.org/cvswebsites/ です。Molli のサイトの一番の目玉 はもちろん FAQ ですが、CVS 関連ツールへのリンクや、メーリングリストの アーカイブなどもあります。Dreilinger のサイトはウェブドキュメントを管 理する場合の CVS の使い方の情報に詳しく、また、CVS に特化したサーチエ ンジンがあります。


Node:Starting A Repository, Next:, Previous:Anatomy Of A CVS Distribution, Up:Repository Administration

Starting A Repository

CVS の実行ファイルがシステムにインストールできたら、まずは An Overview of CVS に従って、リモートリポジトリにアクセスするク ライアントとして使ってみましょう。しかし、自分のマシンからリビジョンを 提供したいのであれば、そこにリポジトリをつくらねばなりません。それを実 現するコマンドは以下のとおり、

floss$ cvs -d /usr/local/newrepos init

/usr/local/newrepos はリポジトリになって欲しいディレクトリへの パスです(当然そこへ書込める権限がなければなりません、コマンドを root 権限で実行しても構いません)。init サブコマンドの後ではなく、前に新しい リポジトリの場所を指定するのは直感に反するような気がするかもしれません が、-d オプションの使い方に関して言えば、他の CVS コマンドと一貫してい ます。

このコマンドを走らせると、黙って終わります。新しいディレクトリを調べて みましょう:

floss$ ls -ld /usr/local/newrepos
drwxrwxr-x   3 root     root         1024 Jun 19 17:59 /usr/local/newrepos/
floss$ cd /usr/local/newrepos
floss$ ls
CVSROOT
floss$ cd CVSROOT
floss$ ls
checkoutlist     config,v        history     notify     taginfo,v
checkoutlist,v   cvswrappers     loginfo     notify,v   verifymsg
commitinfo       cvswrappers,v   loginfo,v   rcsinfo    verifymsg,v
commitinfo,v     editinfo        modules     rcsinfo,v
config           editinfo,v      modules,v   taginfo

floss$

新しいディレクトリにはサブディレクトリがひとつだけあり(CVSROOT/)、CVS の動作をコントロールするいろいろな管理ファイルがおさまっています。今後、 これらのファイルをひとつひとつ見ていく予定です。今のところはリポジトリ がちゃんと動くことが目標です。ここで「ちゃんと動く」とは、ユーザがプロ ジェクトをインポート、チェックアウト、アップデート、コミットできるとい う意味です。 An Overview of CVS で述べた CVSROOT 環境変数と、リポジトリの中に あるこの CVSROOT サブディレクトリを混同しないように。全然関係ないです。 不運にも偶然一致してしまい、同じ名前がついているだけです。前者は、CVS を使うときにいちいち -d <repository-location> とタイプする のがイヤなユーザ向けのもので、後者はリポジトリの管理用サブディレクトリ です。

リポジトリが作成できたら、そのパーミッションに注意して下さい。CVS は特 別な統一されたパーミッションやファイルの所有権の計画(?)は要求しません。 リポジトリへ書き込める権限が必要なだけです。しかし、一部セキュリティー 上の理由ですがどちらかというと主に管理者のまっとうな分別として、次の手 順を踏むことを強く推奨します。

  1. システムに cvs というグループを追加し、リポジトリにアクセスする 必要のあるユーザはそのグループに加えます。例として筆者のマシンの /etc/group ファイルの関連する行をお見せしましょう:
    cvs:*:105:kfogel,sussman,jimb,noel,lefty,fitz,craig,anonymous,jrandom
    
  2. リポジトリのオーナグループとグループパーミッションを変えて、そのグルー プを反映するようにします:
    floss$ cd /usr/local/newrepos
    floss$ chgrp -R cvs .
    floss$ chmod ug+rwx . CVSROOT
    

これで、グループにいるユーザは誰でも cvs import を実行すれ ば(このへんのことは An Overview of CVS に説明があります)プロジェ クトを始めることができるようになりました。checkout, update, commit も 同様に動きます。リポジトリマシンに rsh または sh アクセスができるなら、 :ext: を使ってリモートからアクセスすることもできます。( 上の例の chgrp と chmod コマンドだと、誰だかもわからない anonymous ユーザに書込み権限を与えてしまうことにお気づきでしょうか。こうしている 理由は、リポジトリを読むだけの anonymous ユーザであろうとも、CVS プロ セスがリポジトリ内に一時ロックファイルを作れるようにしておかなければな らないので、システムレベルでの書込み権限が必要だからなのです。CVS は read-only 制限を実現するのに Unix ファイルシステムの権限は使っておらず、 別の方法を使っているのです。これについては Anonymous Access で述 べます。)

コントリビュータがリポジトリマシン上のアカウントを持つ必要がないような プロジェクトを一般に公開するリポジトリの場合は、パスワード認証サーバを 設定するべきです(see The Password-Authenticating Server)。これは匿 名の読み出し専用アクセスに必要ですし、マシンのフルアカウントを与えるこ となしに特定の人々にコミットアクセスを許可する一番簡単な方法でもありま す。


Node:The Password-Authenticating Server, Next:, Previous:Starting A Repository, Up:Repository Administration

The Password-Authenticating Server

パスワードサーバのセットアップに必要な手順を実行する前に、このような接 続がどのように動いているのか大雑把に見ておきましょう。リモートの CVS クライアントが :pserver: メソッドを使ってリポジトリに接続すると き、クライアントは実際にはサーバマシンの特定のポート番号、明確に言うと 2401 番(49の2乗と言ってもいいです、そういうのがお好きなら)に接続しに行き ます。2401番ポートは CVS pserver 用に指定されたデフォルトポートですが、 クライアントとサーバの共通了解さえあれば別のポートに設定することも可能 です。

CVS サーバはそのポートで接続をずっと待っているわけではなく、実際に接続 要求があるまで起動されません。かわりに Unix の inted (InterNET Daemon) プログラムがそのポートを listen しています。接続要求を受け取ったときに どうするかを教えておく必要がありますが、そうすると inetd が CVS サーバ を起動して、入って来たクライアントと接続してくれます。

これを実現するには inetd の設定ファイル、/etc/services/etc/inetd.conf を変更します。services ファイルは生のポート番号とサー ビス名を対応づけ、inetd.conf には inetd が各サービス名に対して何をすれ ばよいかを書いてあります。

まず /etc/services に次の1行を追加して下さい(この行がファイルにないこ とを確認してからですよ)

cvspserver	2401/tcp

次に /etc/inetd.conf にこれを追加してください:

cvspserver stream tcp nowait root /usr/local/bin/cvs cvs \
   --allow-root=/usr/local/newrepos pserver

(上記は実際のファイルではバックスラッシュなしの1つの長い行にしてくださ い) ただし、tcpwrapper が導入されたシステムにおいては次のようにしてく ださい:

cvspserver stream tcp nowait root /usr/sbin/tcpd /usr/local/bin/cvs \
   --allow-root=/usr/local/newrepos pserver

inetd を再起動して、設定ファイルの変更を反映して下さい。(inetd の再起 動の仕方を知らない場合はマシンをリブートしてください、それで動くように なります)

接続を許可するためにはこれだけでいいのですが、ユーザの通常のログインパ スワードとは別に、CVS 用のパスワードを設定したいでしょう。そうすればシ ステム全体のセキュリティについて妥協することなくリポジトリにアクセスさ せることができるようになります。

リポジトリ内の CVSROOT/passwd が CVS のパスワードファイルです。このフ ァイルは cvs init を実行したときにデフォルトで生成されたりはしません。 CVS を pserver で提供するとは限らないからです。パスワードファイルが生 成されたとしても、CVS ではユーザ名とパスワードを生成できません。自分で 作る必要があります。CVSROOT/passwd ファイルのサンプルを示します:

kfogel:rKa5jzULzmhOo
anonymous:XR4EZcEs0szik
melissa:tGX1fS8sun6rY:pubcvs

フォーマットはご覧の通りごく簡単です。各行次のようにしてください:

<USERNAME>:<ENCRYPTED_PASSWORD>:<OPTIONAL_SYSTEM_USERNAME>

余分にコロンとオプショナルシステムユーザ名をつけると、CVS は USERNAME として認証した接続を SYSTEM_USERNAME というシステムアカウントの権限で 実行します。言い換えると そのセッションでは SYSTEM_USERNAME としてログ インしたときと同様のことがリポジトリ内で実行できる、ということです。

システムユーザ名が指定されていない場合、USERNAME はシステム上に実際に 存在するログインアカウントと一致しなければならず、セッションはそのユー ザの権限で実行されます。どちらの場合でも、ENCRYPTED_PASSWORD はシステ ムの実際のログインパスワードと一致しなくても構いません。それは CVS の pserver 接続にのみ用いられる、他と関係ないパスワードです。

パスワードは /etc/passwd にパスワードを保存するときに使われているのと 同じアルゴリズムで暗号化されます。ここで「どうやって暗号化したパスワー ドを持ってくればいいの?」という疑問が出てきます。Unix のシステムパスワー ドなら、passwd コマンドが /etc/passwd の暗号化を面倒見てくれます。しか し、それに対応するような cvs passwd コマンドはありません(これは何度か 提案されていますが、まだ誰も書いていないんですよ。あなたがやってくれま すか?)。

不便でしょうもない方法がひとつあります。他にどうしようもなければ、通常 のシステムパスワードを passwd コマンドで変えて、暗号化されたものを /etc/passwd から CVSROOT/passwd へカットアンドペーストし、元のパスワード に戻す、というのでなんとかなります(暗号化されたパスワードが /etc/shadows にあって、root 権限がないと読めない場合もあります)。

この方法はしかしかなり厄介です。暗号化されていないパスワードを引数で渡 せば暗号化されたものを出力してくれるような、コマンドラインユーティリテ ィを作るほうが簡単でしょう。Perl で書いたそういうツールをお見せしまし ょう:

#!/usr/bin/perl

srand (time());
my $randletter = "(int (rand (26)) + (int (rand (1) + .5) % 2 ? 65 : 97))";
my $salt = sprintf ("%c%c", eval $randletter, eval $randletter);
my $plaintext = shift;
my $crypttext = crypt ($plaintext, $salt);

print "${crypttext}\n";

わたしはこのスクリプトを /usr/local/bin/cryptout.pl に置いてい ます。

floss$ ls -l /usr/local/bin/cryptout.pl

-rwxr-xr-x   1   root   root   265  Jun 14 20:41 /usr/local/bin/cryptout.pl
floss$ cryptout.pl "some text"
sB3A79YDX5L4s

floss$

この例の出力を CVSROOT/passwd のエントリ作成に使ったとします:

jrandom:sB3A79YDX5L4s:craig

で、誰かが次のコマンドを使ってリポジトリに接続するとすると:

remote$ cvs -d :pserver:jrandom@floss.red-bean.com:/usr/local/newrepos login

ここでパスワードに some text とタイプすれば、以降は craig のアクセス権限で CVS コマンドを実行することができます。

CVSROOT/passwd にないユーザ名とパスワードでログインしようとした場合、 CVS は /etc/passwd のユーザ名とパスワードをチェックします。もし存在す れば(そして当然ですがパスワードが一致すれば)、CVS はアクセスを許可しま す。この振舞いは管理者の便宜のため、つまり通常のシステムユーザの分まで いちいち CVSROOT/passwd のエントリを設定しなくてもよいようにするための ものです。しかしこれはセキュリティホールにもなります。なぜなら、そのよ うなユーザが CVS に接続しようとすると、通常のログインパスワードが暗号 化されずにネットワーク上に流れてしまい、パスワード盗み屋に見られてしま うかもしれないからです。このあとでこの「fallback」な振舞いを無効にする 方法を述べます。有効にするにしろ無効にするにしろ、ログインアカウントも 持っている CVS ユーザに対しては、それぞれを違うパスワードにするように、 おそらく強制すべきです。

passwd ファイルでの認証はリポジトリ全体に対して有効なのですが、少し工 夫すればプロジェクトごとのアクセス許可を設定するために使用することもで きます。一方法を示します:

次のように仮定します。リモートの開発者にプロジェクト foo へのア クセスを許可し、また別のリモートの開発者にプロジェクト bar への アクセスを許可するが、一方のプロジェクトの開発者には、もう一方のプロジ ェクトにコミットさせたくない。この場合、プロジェクト別のユーザアカウン トとグループをシステム上に作成し、CVSROOT/passwd ファイルで対応させる ことによって実現できます。

/etc/passwd から関連部分の抜粋:

cvs-foo:*:600:600:Public CVS Account for Project Foo:/usr/local/cvs:/bin/false
cvs-bar:*:601:601:Public CVS Account for Project Bar:/usr/local/cvs:/bin/false

/etc/group からの抜粋:

cvs-foo:*:600:cvs-foo
cvs-bar:*:601:cvs-bar

最後に CVSROOT/passwd からの抜粋です:

kcunderh:rKa5jzULzmhOo:cvs-foo
jmankoff:tGX1fS8sun6rY:cvs-foo
brebard:cAXVPNZN6uFH2:cvs-foo
xwang:qp5lsf7nzRzfs:cvs-foo
dstone:JDNNF6HeX/yLw:cvs-bar
twp:glUHEM8KhcbO6:cvs-bar
ffranklin:cG6/6yXbS9BHI:cvs-bar
yyang:YoEqcCeCUq1vQ:cvs-bar

CVS ユーザ名のうちいくつかはシステムユーザアカウント cvs-foo と 対応しており、いくつかは cvs-bar に対応しています。CVS はシステ ムアカウントのユーザIDの権限で動作しますので、リポジトリのうち関連する 部分には適切なユーザとグループの書込み権限を与えておいて下さい。ユーザ アカウントをきちんとロックしておけば(ログインパスワードを無効にし、シ ェルには /bin/false を設定)、このシステムは十分セキュアです(こ の章で後ほど述べる CVSROOT のパーミッションについて読んでおいて下さい ね!)。また、CVS ではレコードやログメッセージの変更をシステムユーザ名で はなく CVS ユーザ名で行うので、ある変更が誰の責任なのかというのもわか ります。


Node:Anonymous Access, Next:, Previous:The Password-Authenticating Server, Up:Repository Administration

Anonymous Access

ここまでパスワード認証サーバを使って、リポジトリに対し通常のフルアクセ スを許可するやりかたを見てきました(Unix ファイルパーミッションを巧みに 利用してアクセス制限をすることは可能ですが)。一方、匿名の読み出し専用 アクセスを実現するにはワンステップで済みます: CVSROOT/ の下に新しいフ ァイルを1つか、もしかしたら2つ、追加するだけでいいのです。ファイル名は readerswriters、前者はリポジトリを読めるユーザ名の、 後者は読み書きできるユーザ名のリストです。

CVSROOT/readers にユーザ名を書くと、そのユーザはリポジトリ内の全てのプ ロジェクトに対する読み出しアクセスのみ可能になります。CVSROOT/writers にユーザ名を書くと、そのユーザは書込み権限を得、writers ファイルに書か れていない他の pserver ユーザは読み出しアクセスのみ可能になります(つま り、writers ファイルが存在するということは、そこに書かれていないユーザ を読み出しアクセスのみに制限するという意味になります)。両方のファイル に同じユーザ名が書かれている場合、CVS はその矛盾を保守的な方法で解決し ます: そのユーザは読み出しアクセスのみ可能になります。

ファイルの形式は非常に単純で、各行1ユーザです(最後のユーザの後に改行を 入れるのを忘れないで)。readers ファイルのサンプルを示します:

anonymous
splotnik
guest
jbrowse

それらのファイルは CVS ユーザ名に適用されます。システムユーザ名ではあ りません。CVSROOT/passwd のユーザエイリアス機能を使っている場合(2番目 のコロンの後にシステムユーザ名を書いている場合)、readers または writers ファイルに使用するのは一番左側のユーザ名です。

Just to be painfully accurate about it, 許可するのが読み出しアクセスか 読み書きアクセスかを決定する時のサーバの動作を正式に説明するとこうなり ます:

readers ファイルが存在し、その中にそのユーザが書いてあれば、読み出しア クセス。writers ファイルが存在し、その中にそのユーザがなければ、読み出 しアクセス(これは readers ファイルが存在していてそのユーザがその中にな い場合にも真)。そのユーザが両方に書いてあれば、読み出しアクセス。その 他の場合、読み書きアクセス。

従って、典型的な匿名 CVS アクセスのリポジトリは CVSROOT/passwd には次 のような行があります

anonymous:XR4EZcEs0szik

/etc/passwd には次のような行

anonymous:!:1729:105:Anonymous CVS User:/usr/local/newrepos:/bin/false

CVSROOT/readers には次のような行

anonymous

当然 /etc/services と /etc/inetd.conf には以前言ったような設定を。これ で全部です。

古めの Unix システムでは8文字以上のユーザ名をサポートしていないことに 注意して下さい。これに対応するひとつの方法は、CVSROOT/passwd ファイル とシステムファイルでそのユーザを anonymous ではなく anon にしておくことです。anonymous の短縮形は anon だろう、と思う人は多いで すから。しかし、 CVSROOT/passwd ファイルにこのような行を書くほうがよい かもしれません:

anonymous:XR4EZcEs0szik:cvsanon

(もちろんシステムファイルでは cvsanon を使って下さい)。こうすれ ば、多少標準的な anonymous を使ったリポジトリアドレスを公開でき ます。リポジトリに

cvs -d :pserver:anonymous@cvs.foobar.com:/usr/local/newrepos (etc...)

でアクセスしてくる人は、サーバ上では cvsanon (かなんか)の権限で実行さ れますが、その人たちはサーバ側でどう設定されているかなんて知る必要はな く、ただ公開されたアドレスだけ見ていればいいのです。


Node:Repository Structure, Next:, Previous:Anonymous Access, Up:Repository Administration

Repository Structure

新しく作られたリポジトリにはプロジェクトはありません。An Overview of CVS の最初のインポートのところからやりなおして、リポジトリに何が起 こるかを見ていきましょう。(単純のため、全てのコマンドで CVSROOT 環境変 数が /usr/local/newrepos が設定されていると仮定します、インポートやチ ェックアウトの際 -d でリポジトリを指定しなくてもいいように)

floss$ ls /usr/local/newrepos
CVSROOT/
floss$ pwd
/home/jrandom/src/
floss$ ls
myproj/
floss$ cd myproj
floss$ cvs import -m "initial import into CVS" myproj jrandom start
N myproj/README.txt
N myproj/hello.c
cvs import: Importing /usr/local/newrepos/myproj/a-subdir
N myproj/a-subdir/whatever.c
cvs import: Importing /usr/local/newrepos/myproj/a-subdir/subsubdir
N myproj/a-subdir/subsubdir/fish.c
cvs import: Importing /usr/local/newrepos/myproj/b-subdir
N myproj/b-subdir/random.c

No conflicts created by this import

floss$ ls /usr/local/newrepos
CVSROOT/  myproj/
floss$ cd /usr/local/newrepos/myproj
floss$ ls
README.txt,v  a-subdir/     b-subdir/	  hello.c,v
floss$ cd a-subdir
floss$ ls
subsubdir/    whatever.c,v
floss$ cd ..

floss$

インポート前、リポジトリ内には管理領域しかありません。インポート後、新 しいディレクトリ(myproj)が出現しています。新しいディレクトリの 中のファイルとサブディレクトリはインポートしたプロジェクトに見えなくは ないですが、なんだかファイル名に ,v というサフィックスがついて います。これらは RCS 形式のバージョン管理ファイルで(,v は "version" という意味です)、リポジトリの大黒柱になるものです。各 RCS ファイルはプ ロジェクト内の対応するファイルの、ブランチやタグも含めたリビジョン履歴 を保持しています。


Node:RCS Format, Next:, Previous:Repository Structure, Up:Repository Administration

RCS Format

CVS を使うにあたり、RCS 形式について知っておく必要は一切ありません(ソー スディストリビューションに素晴らしい記事がありますけれど。doc/RCSFILES をご覧ください)。しかし、この形式の基本的なところを理解していると CVS のトラブルシューティングに非常に役に立ちますので、ファイルの1つ hello.c,v をちょっと覗いてみることにしましょう。ファイル内容を示します:

head     1.1;
branch   1.1.1;
access   ;
symbols  start:1.1.1.1 jrandom:1.1.1;
locks    ; strict;
comment  @ * @;

1.1
date     99.06.20.17.47.26;  author jrandom;  state Exp;
branches 1.1.1.1;
next;

1.1.1.1
date     99.06.20.17.47.26;  author jrandom;  state Exp;
branches ;
next;

desc
@@

1.1
log
@Initial revision
@
text
@#include <stdio.h>

void
main ()
{
  printf ("Hello, world!\n");
}
@

1.1.1.1
log
@initial import into CVS
@
text
@@

うひゃー! もうほとんど無視してもかまわないです; 例えば 1.1 と 1.1.1.1 の関連や、暗黙の 1.1.1 ブランチとかは気にしないで下さい、ユーザ、管理 者、どちらの観点からもあまり重要なことではありません。理解すべきは全体 のフォーマットです。最初はヘッダフィールドのコレクションです:

head     1.1;
branch   1.1.1;
access   ;
symbols  start:1.1.1.1 jrandom:1.1.1;
locks    ; strict;
comment  @ * @;

そのあとは各リビジョンのメタ情報のグループです(リビジョンの中身はまだ 先です)、こんな感じ:

1.1
date     99.06.20.17.47.26;  author jrandom;  state Exp;
branches 1.1.1.1;
next     ;

最後にログメッセージと実際のリビジョンのテキストが来ます:

1.1
log
@Initial revision
@
text
@#include <stdio.h>

void
main ()
{
  printf ("Hello, world!\n");
}
@

1.1.1.1
log
@initial import into CVS
@
text
@@

よくみると、最初のリビジョンの内容が 1.1 という見出しの下にあって、そ のログメッセージがなぜか "Initial revision" になっています。インポート 時に使ったのは "initial import into CVS" というログメッセージなのに。 そっちのログメッセージはもっと下のほう、Revision 1.1.1.1 の下に あります。今この矛盾を気にする必要はありません。これはインポートが特別 な場合だから起こることなのです。It happens because imports are a special circumstance: Inorder to make repeated imports into the same project have a usefuleffect, import actually places the initial revision on both the maintrunk and on a special branch (これの理由は Advanced CVS でベンダブランチについて述べるときにもう少し明らかになります). 今のと ころは 1.11.1.1.1 を同じものとして扱っても構いません。

hello.c の最初の変更をコミットすると、このファイルのことがもう少しわか ってきます:

floss$ cvs -Q co myproj
floss$ cd myproj
floss$ emacs hello.c
    (ファイルを変更してみる)

floss$ cvs ci -m "print goodbye too"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in hello.c;
/usr/local/newrepos/myproj/hello.c,v  <--  hello.c
new revision: 1.2; previous revision: 1.1
done

ここでリポジトリ内の hello.c,v を見ると、コミットの結果がわかります:

head  1.2;
access;
symbols
      start:1.1.1.1 jrandom:1.1.1;
locks; strict;
comment   @ * @;

1.2
date   99.06.21.01.49.40;   author jrandom;   state Exp;
branches;
next   1.1;

1.1
date   99.06.20.17.47.26;   author jrandom;   state Exp;
branches
       1.1.1.1;
next   ;

1.1.1.1
date   99.06.20.17.47.26;   author jrandom;   state Exp;
branches;
next   ;

desc
@@

1.2
log
@print goodbye too
@
text
@#include <stdio.h>

void
main ()
{
  printf ("Hello, world!\n");
  printf ("Goodbye, world!\n");
}
@

1.1
log
@Initial revision
@
text
@d7 1
@

1.1.1.1
log
@initial import into CVS
@
text
@@

リビジョン1.2全体の内容がファイルに保存されており、リビジョン1.1の内容 は暗号ちっくな形式に置き換わっています:

d7 1

d7 1 は「7行目から始めて、1行削除する」という意味の diff コー ドです。言い換えると、リビジョン1.1を導出するにはリビジョン1.2から7行 目を削除する、ということなのです! 自分で実際にやってみてください。これ でリビジョン1.1ができるのがわかると思います。単純に、ファイルに追加し た行をなくすだけです。

これは RCS 形式の基本原則を示しています: リビジョン間の相違のみを保存 し、そうすることによって各リビジョンそれぞれの全体を保存するのに比べて 容量を節約します。一番新しいリビジョンから以前のリビジョンへ戻るには、 保存してある diff をより最近のリビジョンに対して patch すればよろしい。 つまりこれは、過去に戻ろうとすればするほど、より多くの patch 操作が必 要になる、ということです(たとえばリビジョン1.7のファイルがあって、その ファイルのリビジョン1.4へのアクセスを要求された場合、patch で 1.7 から 1.6 を生成し、1.6 から 1.5 を、そして 1.5 から 1.4 を生成します)。幸い、 古いリビジョンはあまりアクセスされませんので、実用上 RCS システムはう まく動きます。最近のファイルになるほど取得するコストが低いわけです。

ファイルの冒頭のヘッダ情報が何を意味するか、全てを理解する必要はありませ ん。しかし、ある種の操作は、結果がとても明確にヘッダに示されますので、ヘッ ダに親しんでおくと便利には違いありません。

トランクに新しいリビジョンをコミットした時、head ラベルが更新され ます(先に示した例で、2回目に hello.c をコミットした時、そのラベルがどの ように 1.2 になったか注意して見てみてください)。あるファイルをバイナリと して追加した時、あるいはタグをつけた時にも、それらの操作はヘッダに記録さ れます。例として foo.jpg をバイナリファイルとして追加し、その後二度ほど タグづけしてみましょう:

floss$ cvs add -kb foo.jpg
cvs add: scheduling file 'foo.jpg' for addition
cvs add: use 'cvs commit' to add this file permanently
floss$ cvs -q commit -m "added a random image; ask jrandom@red-bean.com why"
RCS file: /usr/local/newrepos/myproj/foo.jpg,v
done
Checking in foo.jpg;
/usr/local/newrepos/myproj/foo.jpg,v  <--  foo.jpg
initial revision: 1.1
done
floss$ cvs tag some_random_tag foo.jpg
T foo.jpg
floss$ cvs tag ANOTHER-TAG foo.jpg
T foo.jpg
floss$

さて、リポジトリ内の foo.jpg,v のヘッダ部分を見てみましょう:

head   1.1;
access;
symbols
      ANOTHER-TAG:1.1
      some_random_tag:1.1;
locks; strict;
comment   @# @;
expand	@b@;

最後の expand の行の b を見て下さい。これはこのファイルを -kb つきで add したためにこうなっています。通常のテキストファイルでは、チェックアウトと アップデートの時にキーワードや改行コードの変換が行われるのですが、このファ イルではそれが行われない、という意味です。タグは symbols セクションに 1 タグ1行で記してあります。最初のリビジョンに2回タグをつけたので、タグは両 方とも最初のリビジョンについています。(タグ名に英数字、ハイフン、アンダ スコアしか使えない理由もこれで説明できます。タグがコロンやピリオドを含ん でいたとしたら、RCS ファイルのこの欄のタグとリビジョンの区切りが曖昧になっ てしまうからですね。)

RCS Format Always Quotes @ Signs

RCS ファイル中の @ シンボルはフィールド(訳注: フィールドとは各リ ビジョンの領域のことのようです)の区切りに使用されますので、ファイルのテ キスト中やログメッセージに出てくる場合にはクオートする必要があります(そ うしないと CVS はそれをフィールドの最後だと誤解してしまいます)。クオート するには を2つ続けます。つまり、CVS は @@ が出てくると、フィー ルドの終わりという意味ではなく、@ 記号であると解釈します。foo.jpg をコ ミットしたときのログメッセージは

"added a random image; ask jrandom@red-bean.com why"

でした、これは foo.jpg,v 中ではこのようになります:

1.1
log
@added a random image; ask jrandom@@red-bean.com why
@

ログメッセージにアクセスするときにはj random@@red-bean.com の中の @ 記号は自動的にクオートがはずされます:

floss$ cvs log foo.jpg
RCS file: /usr/local/newrepos/myproj/foo.jpg,v
Working file: foo.jpg
head: 1.1
branch:
locks: strict
access list:
symbolic names:
      ANOTHER-TAG: 1.1
      some_random_tag: 1.1
keyword substitution: b
total revisions: 1;	selected revisions: 1
description:
----------------------------
revision 1.1
date: 1999/06/21 02:56:18;  author: jrandom;  state: Exp;
added a random image; ask jrandom@red-bean.com why
============================================================================

floss$

RCS ファイルを手で編集する時くらいしか気にすることはないです(ほとんどな いとは思いますが、全然ないわけではありません)。その場合はリビジョン内容 とログメッセージで 記号を2つ重ねて書くことを思い出してください。もし忘 れたら、RCS ファイルはむちゃくちゃになり、思いもかけないヘンな動作をする でしょう。

Speaking of hand-editing RCS files, don't be fooled by the permissions in the repository:

floss$ ls -l
total 6
-r--r--r--   1 jrandom   users         410 Jun 20 12:47 README.txt,v
drwxrwxr-x   3 jrandom   users        1024 Jun 20 21:56 a-subdir/
drwxrwxr-x   2 jrandom   users        1024 Jun 20 21:56 b-subdir/
-r--r--r--   1 jrandom   users         937 Jun 20 21:56 foo.jpg,v
-r--r--r--   1 jrandom   users         564 Jun 20 21:11 hello.c,v

floss$

(Unix の ls の出力に詳しくない人へ、左のほうの -r--r--r-- は、そ のファイルは読めるけど変更できないよ、という意味です) これらのファイルは 誰に対してもリードオンリーのように見えますが、ディレクトリパーミッション のほうを考慮に入れなくてはなりません:

floss$ ls -ld .
drwxrwxr-x   4 jrandom   users        1024 Jun 20 22:16 ./
floss$

myproj/ 自身とそのサブディレクトリは、オーナ(jrandom)とグループ(users)の 書き込み権限があります。これはつまり、(jrandom 及び users グループの メンバーなら誰でも、の権限で実行される) CVS はそれらのディレクトリでファ イルを作ったり削除したりできるということです、既に存在するファイルを直接 編集することができないとしても。CVS は RCS ファイルのコピーを取って編集 するので、あなたも一時コピーを好きなように変更し、既存の RCS ファイルを その新しいファイルで置き換えたっていいのです。(なんでファイルがリードオ ンリーなのかというのは聞かないでください、RCS がスタンドアロンで動く時の 動作のしかたに関係のある歴史的経緯があるのです)

ついでに言うと、リポジトリのトップレベルディレクトリのグループが cvs であることを考えれば、それらのファイルのグループが users になっているのは望ましいことではないと思います。リポジトリ 内でこのコマンドを実行すれば問題を解決できます:

floss$ cd /usr/local/newrepos
floss$ chgrp -R cvs myproj

リポジトリ内に新しく作成されるファイルのグループについては、Unix の通常 のファイル作成時のルールが適用されてしまうので、たまにリポジトリ内のファ イルやディレクトリを chgrp または chmod してやる必要があると思います。 リポジトリのパーミッションをどう構成するかについて、難しい固定した規則 はありません、単にどのプロジェクトで誰が作業しているかによります。


Node:What Happens When You Remove A File, Next:, Previous:RCS Format, Up:Repository Administration

What Happens When You Remove A File

あるプロジェクトからファイルを削除する場合、そのファイルは単になくなる だけではありません。 プロジェクトの古いスナップショットを要求されても、 CVS はそのようなファイルにもちゃんとアクセスできなければならないのです。 なくなるかわりに、そのファイルは文字通り Attic(訳注: Attic=屋根 裏)に移されます:

floss$ pwd
/home/jrandom/src/myproj
floss$ ls /usr/local/newrepos/myproj/
README.txt,v  a-subdir/     b-subdir/     foo.jpg,v   hello.c,v
floss$ rm foo.jpg
floss$ cvs rm foo.jpg
cvs remove: scheduling 'foo.jpg' for removal
cvs remove: use 'cvs commit' to remove this file permanently
floss$ cvs ci -m "Removed foo.jpg" foo.jpg
Removing foo.jpg;
/usr/local/newrepos/myproj/foo.jpg,v  <--  foo.jpg
new revision: delete; previous revision: 1.1
done
floss$ cd /usr/local/newrepos/myproj/
floss$ ls
Attic/      README.txt,v  a-subdir/     b-subdir/   hello.c,v
floss$ cd Attic
floss$ ls
foo.jpg,v
floss$

プロジェクトのリポジトリ内ディレクトリに Attic/ というサブディレ クトリがある場合、それは、少なくとも1ファイルはそのディレクトリから削除 された、ということを意味します(つまりプロジェクトで Attic という名前の ディレクトリは使ってはいけないということです)。CVS は RCS ファイルを Attic/ に移すだけではなく、そのファイルの新しいリビジョンを、 dead という特別なリビジョン状態でコミットします。Attic/foo.jpg,v から関係個所を示します:

1.2
date   99.06.21.03.38.07;   author jrandom;   state dead;
branches;
next	1.1;

あとでファイルを生き返らせた場合CVS は、過去のある時点でこのファイルが 死に、今は再び生きている、ということを記録します。

これはつまり、削除したファイルを元に戻したい場合に Attic/ ディレクトリ から出してプロジェクトに戻しただけではダメだということを意味します。そ うではなくて、作業コピー内で次のようにしてください:

floss$ pwd
/home/jrandom/src/myproj
floss$ cvs -Q update -p -r 1.1 foo.jpg > foo.jpg
floss$ ls
CVS/       README.txt   a-subdir/   b-subdir/   foo.jpg     hello.c
floss$ cvs add -kb foo.jpg
cvs add: re-adding file foo.jpg (in place of dead revision 1.2)
cvs add: use 'cvs commit' to add this file permanently
floss$ cvs ci -m "revived jpg image" foo.jpg
Checking in foo.jpg;
/usr/local/newrepos/myproj/foo.jpg,v  <-- foo.jpg
new revision: 1.3; previous revision: 1.2
done
floss$ cd /usr/local/newrepos/myproj/
floss$ ls
Attic/	      a-subdir/     foo.jpg,v
README.txt,v  b-subdir/     hello.c,v
floss$ ls Attic/
floss$

RCS 形式について知るべきことはもっとたくさんありますが、CVS のリポジト リの管理者としてはこれで十分だと思います。RCS ファイルを直接いじること は滅多にないことです。普通はせいぜいリポジトリ内のファイルのパーミッショ ンをいじったりする程度です、少なくともわたしが経験してきたところでは。 そうは言っても、CVS の動作が変になりはじめたら(稀ではありますが可能な範 疇を越えているわけではありませんから)、実際に RCS の中身を見てみて、何 が起こっているのか調べたくなるだろうと思います。


Node:The CVSROOT/ Administrative Directory, Next:, Previous:What Happens When You Remove A File, Up:Repository Administration

The CVSROOT/ Administrative Directory

newrepos/CVSROOT/ の中のファイルはどのプロジェクトにも属していませんが、 そのリポジトリ内での CVS の動作を制御するために使用されます。それらのファ イルを編集するときには、普通のプロジェクトと同様、CVSROOT の作業コピー をチェックアウトするのがよいでしょう:

floss$ cvs co CVSROOT
cvs checkout: Updating CVSROOT
U CVSROOT/checkoutlist
U CVSROOT/commitinfo
U CVSROOT/config
U CVSROOT/cvswrappers
U CVSROOT/editinfo
U CVSROOT/loginfo
U CVSROOT/modules
U CVSROOT/notify
U CVSROOT/rcsinfo
U CVSROOT/taginfo
U CVSROOT/verifymsg
floss$

さて、重要な順(だいたい)にファイルを見ていくことにしましょう。各ファイ ルの最初には説明のコメントが入っていますので見てみてください(コメントの 書きかたはどのファイルも同じです。行頭に # があればコメントで、 CVS はファイルを解釈する時にそのような行を無視します)。チェックアウトし た作業コピー中の管理ファイルを変更しても、コミットするまでは CVS の動作 に何の影響も及ぼさない、ということを忘れないでください。

セキュリティを気にするのであれば、CVSROOT の Unix レベルのパーミッショ ンを適切に設定して、リポジトリの他のところのパーミッションと違うように しておきたいかもしれません。そうしておけば CVSROOT にコミットできるユー ザをきめ細かく制御できます。あとで出てきますが、CVSROOT のファイルを変 更可能にしておくということは、どんな CVS ユーザにも(リモートのユーザに も)リポジトリマシン上で任意のコマンドを実行する権利を与えてしまうことを 意味するのです。


Node:The config File, Next:, Up:The CVSROOT/ Administrative Directory

The config File

config ファイルでは、グローバルな動作を決定するパラメータを設定し ます。形式は非常に厳しくて

PARAMETER=VALUE
(etc)

余分なスペースは許されません。例えば、ありそうな config ファイルはこん な感じです:

SystemAuth=yes
TopLevelAdmin=no
PreservePermissions=no

(ここにないエントリは no と設定するのと等価です)

SystemAuth パラメータは、指定されたユーザ名が CVSROOT/passwd ファ イルに見つからなかった場合に、システムの passwd ファイルを見に行くかど うかを決定します。システムのセキュリティを守るため、ディストリビューショ ンではno に設定して出荷してあります。

TopLevelAdmin は作業コピーをチェックアウトするときに兄弟 CVS/ ディ レクトリを作るかどうかを決定します。兄弟 CVS/ ディレクトリは作業ディレ クトリ内ではなく、作業ディレクトリの隣に作成されます。同じリポジトリか ら多数のいろいろなプロジェクトをチェックアウトしたいと思う場合はこれを オンにすると便利です。そうでなければこれはオフのままにしておいたほうが いいでしょう、思いがけないところに余分な CVS/ ディレクトリができて混乱 しますからね。

PreservePermissions はファイルのパーミッションを保存し、同様のメ タデータをリビジョン履歴に記録します。これはある種の隠れ機能で、たぶん 詳しく説明する価値もありません。もし興味があるなら Cederqvist の Special Files ノードを見てください(ノードというのは、 Texinfo で、Info ドキュメントの中の特定の場所を示す言葉です。Info を読 んでいるときにあるノードに行きたければ、g をタイプしたあとにその ノードの名前を打てば、そのドキュメント中のどこからでも飛べます)

LockDir はほとんど使われない機能です。特殊な状況では、パーミッショ ンの問題で、CVS のロックファイルをプロジェクトのサブディレクトリ内に直 接作るのではなくもっと別のところに作るようにしたいかもしれません。ロッ クファイルというのは、同じリポジトリディレクトリ上で同時に複数の操作が 行われたとき、CVS がやりそこねるのを防いでいるものです。普通気にする必 要はありませんが、ときどきロックファイルを作れないことが原因でアップデー トやチェックアウトができなくて困るユーザがでてくるかもしれません(読むだ けの操作においても、CVS はロックファイルを作ります。他の CVS が書き込ん でいる最中に読んでしまうような状況を避けるためです)。通常このような問題 を解決するにはリポジトリのパーミッションを変更するのが普通ですが、それ ができないようなら LockDir パラメータが便利でしょう。

現在のところパラメータはこれだけですが、CVS の将来のバージョンでは新し いものが追加されるでしょう、Cederqvist やディストリビューション中の config ファイル自体をいつもチェックしておいてくださいね。


Node:The modules File, Next:, Previous:The config File, Up:The CVSROOT/ Administrative Directory

The modules File

modules ファイルではリポジトリ内のプロジェクトの別名や alternate grouping を定義します。module の行は基本的に次の形式です:

MODULE_NAME   DIRECTORY_IN_REPOSITORY

例えば、

mp    myproj
asub  myproj/a-subdir

(右側で指定するパスはリポジトリのトップからの相対パスです。) 開発者がプ ロジェクトやプロジェクトの一部分をチェックアウトする時の別名を指定して います:

floss$ cvs co mp
cvs checkout: Updating mp
U mp/README.txt
U mp/foo.jpg
U mp/hello.c
cvs checkout: Updating mp/a-subdir
U mp/a-subdir/whatever.c
cvs checkout: Updating mp/a-subdir/subsubdir
U mp/a-subdir/subsubdir/fish.c
cvs checkout: Updating mp/b-subdir
U mp/b-subdir/random.c

あるいは

floss$ cvs -d /usr/local/newrepos/ co asub
cvs checkout: Updating asub
U asub/whatever.c
cvs checkout: Updating asub/subsubdir
U asub/subsubdir/fish.c

両方の場合で、モジュールの名前がどのように作業コピーのディレクトリ名になっ ているかを見てください。asub の場合、中間に myproj/ ディレクトリができな いですが、代りにトップレベルに asub ができました。リポジトリの myproj/a-subdir からできたにもかかわらずです。それらの作業コピー内では、 アップデート、コミット、その他の CVS コマンドがすべて正常に動きます。普 通と違うのは名前だけです。

ディレクトリ名のあとにファイル名をつけることによって、リポジトリディレ クトリ内の指定されたファイルで構成されたモジュールを定義することができ ます。例えば

readme  myproj  README.txt

no-readme  myproj  hello.c  foo.jpg

とすると、それぞれ、次のようなチェックアウトができるようになります:

floss$ cvs -q co readme
U readme/README.txt
floss$ cvs -q co no-readme
U no-readme/hello.c
U no-readme/foo.jpg
floss$

-a (alias という意味) を使えば複数のリポジトリディレクトリを含むモジュー ルを定義することができますが、チェックアウトするともとの名前のディレクト リができるので注意してください。たとえば、この行を書くと

twoproj  -a  myproj  yourproj

下のようになります(myproj/ と yourproj/ がリポジトリに存在するとします):

floss$ cvs co twoproj
U myproj/README.txt
U myproj/foo.jpg
U myproj/hello.c
U myproj/a-subdir/whatever.c
U myproj/a-subdir/subsubdir/fish.c
U myproj/b-subdir/random.c
U yourproj/README
U yourproj/foo.c
U yourproj/some-subdir/file1.c
U yourproj/some-subdir/file2.c
U yourproj/some-subdir/another-subdir/blah.c

twoproj はプロジェクトを両方持ってくるのに便利な名前ではあります が、作業コピーの名前には使われません。(There is no requirement that alias modules refer to multiple directories, by the way; we could have omitted twoproj, in which case myproj would still have been checked out under the name myproj.)

前にアンパサンドをつけることによって、別のモジュールを参照することもでき ます:

mp    myproj
asub  myproj/a-subdir
twoproj -a myproj yourproj
tp  &twoproj

tp をチェックアウトすると、twoproj のチェックアウトと完全 に同一の結果が得られます。

モジュールを処理する仕掛けはまだいくつかありますが、その大半は今述べたも のより使う機会が少ないです。それらについて知りたければ、Cederqvist の modules ノードを参照してください。


Node:The commitinfo And loginfo And rcsinfo Files, Next:, Previous:The modules File, Up:The CVSROOT/ Administrative Directory

The commitinfo And loginfo And rcsinfo Files

他の管理ファイルは、大半がコミット処理のさまざまな部分に programmatic な フックをしかけるためのものです(たとえばコミットを許可する前にログ メッセージやファイルの状態を検証するようにもできますし、リポジトリの特定 ディレクトリへのコミットが発生したら開発者グループに知らせるようにもでき ます)。

それらのファイルでは共通の文法を用います。各行はこのような形式です:

REGULAR_EXPRESSION    PROGRAM_TO_RUN

この正規表現は、コミットが起こるディレクトリに対してテストされます(リポ ジトリのトップディレクトリからの相対パス名)。マッチすれば指定したプログ ラムが実行されます。コミット処理中の各ファイル名がプログラムに渡されます; プログラムはそれらのファイル名を使って何でも好きなことができます、ファイ ルをオープンして内容を調べたりすることも含めてです。プログラムの終了コー ドがノンゼロの場合、コミットは実行されません。

(Regular expressions とは文字列のクラスを簡潔に記述するシステムで す。正規表現をよく知らないかたは、概略を手短に示しますので、これでとりあ えずなんとかなると思います: foo は文字列 foo を含むファイ ル名にマッチします。foo.*bar は、foo を含み、任意の数の文 字が続き、そのあとに文字列 bar が続くファイル名にマッチします。通 常の文字列はそれ自体にマッチしますが、.* は特殊だから です。. は任意の文字にマッチし、* は前の文字が任意の数(0含 む)だけ続いたものにマッチします。^$ はそれぞれ、文字列 の最初と最後にマッチします: ですから、^foo.*bar.*baz$fooで始まり、中ほどのどこかに bar を含んでおり、そして baz で終わる文字列とマッチします。ここではこのくらいにしておきま しょう、この概略説明は正規表現の文法全てのうち、かなり省略されたサブセッ トです)

commitinfo ファイルでは全てのコミット時に実行したい汎用的フックを 書きます。commitinfo の行の例をいくつか示します:

^a-subdir*     /usr/local/bin/check-asubdir.sh
ou             /usr/local/bin/validate-project.pl

myproj/a-subdir へコミットすると最初の行にマッチするので、 check-asubdir.sh スクリプトが実行されます。文字列 ou を含む名前の プロジェクト(実際のリポジトリディレクトリ名であってモジュール名である必 要はありませ)へコミットすると、 validate-project.pl スクリプトが実行され ます。ただし、既に a-subdir の行にマッチしたコミットは除きます。

正規表現の個所に DEFAULT あるいは ALL という語を使うことが できます。DEFAULT 行(もし2つ以上ある場合は最初の DEFAULT 行)はどの正規表 現にもマッチしなかった場合に実行され、各 ALL 行はマッチする他の行に加え て実行されます。

プログラムに渡されるファイル名は RCS ファイルを指すものではありません、 コミットされつつある作業コピーと同じ内容の通常のファイルを指します。CVS はそのファイルをリポジトリ内に一時的に置き、プログラムはリポジトリの中で そのファイルを使うことができます、あまり普通ではありませんね。

loginfo ファイルは commitinfo と同じですが、ファイルの内容ではなく ログメッセージに作用します。loginfo ファイルの左側は正規表現で、DEFAULT や ALL 行も書けます。右側のプログラムは標準入力からログメッセージを受け 取ります。その入力を使って好きなことができます。

右側にあるプログラムは任意の数のコマンドライン引数を取ることもできま す。引数のうちのひとつは特殊な % コードを書いてもよく、それは CVS によって実行時に次のように展開されます:

%s    ------>      コミットされつつあるファイルの名前
%V    ------>      コミット前のリビジョン番号
%v    ------>      コミット後のリビジョン番号

展開された文字列の最初はリポジトリサブディレクトリ(リポジトリのトッ プディレクトリからの相対パス)で、そのあとに各ファイルの情報が続きま す。たとえば、コミットされたファイルが foo, bar, baz で、それらがす べて myproj/a-subdir の中にあった場合、%s は次のように 展開されます。

myproj/a-subdir  foo  bar  baz

そして %V は古いリビジョン番号に展開されます

myproj/a-subdir  1.7  1.134  1.12

and %v their new revision numbers:

myproj/a-subdir  1.8  1.135  1.13

% 記号に続けて中括弧でくくることによって、% 表現を結合 することができます。コミット時に、各ファイルごとの対応する情報をカン マで区切ったサブリストに展開されます。例えば、 %{sv} は次の ように展開されます:

myproj/a-subdir  foo,1.8  bar,1.135  baz,1.13

%{sVv} だとこのように展開されます:

myproj/a-subdir  foo,1.7,1.8  bar,1.134,1.135  baz,1.12,1.13

(カンマとピリオドを見間違えないよう、注意して見て下さい)

loginfo ファイルのサンプルを示します:

^myproj$   /usr/local/newrepos/CVSROOT/log.pl -m myproj-devel@foobar.com %s
ou         /usr/local/bin/ou-notify.pl  %{sv}
DEFAULT    /usr/local/bin/default-notify.pl  %{sVv}

最初の行では、リポジトリの myproj サブディレクトリへのコミットが発生 すると log.pl を起動するよう設定しています。log.pl の 引数として、電子メールアドレス(log.pl はそのアドレスへログメッ セージを送信すします)、リポジトリ、コミットされたファイルを渡します。

2行目では、リポジトリの文字列 ou を含むサブディレクトリへのコ ミットが発生すると ou-notify.pl (架空のものです)を起動するよ う設定しています。リポジトリのあとに、ファイル名と新しいリビジョン番 号をリストで渡します。

3行目では、上記2行にマッチしないコミットが発生した時に default-notify.pl を起動するよう設定しています。渡せる情報は 渡しています(リポジトリパス、ファイル名、古いリビジョン番号、新しい リビジョン番号)。


Node:The verifymsg And rcsinfo Files, Next:, Previous:The commitinfo And loginfo And rcsinfo Files, Up:The CVSROOT/ Administrative Directory

The verifymsg And rcsinfo Files

ログメッセージがある一定の標準に従っているかどうか自動的に検証して、 従っていなければコミットを中止するようなプログラムが欲しいこともある と思います。これは、verifymsg を使えば実現できます。 補助に rcsinfo も使うかもしれません。

verifymsg ファイルは例によって正規表現とプログラムを結び付ける ものです。プログラムは標準入力からログメッセージを受け取って、そのロ グメッセージがある基準に沿っているかどうかチェックして、ゼロまたはノン ゼロの終了コードで終了します。ノンゼロで終了した場合はそのコミットは 失敗します。

一方、rcsinfo の左側はいつもの正規表現ですが、右側はプログラムではな くてテンプレートファイルを指定します。テンプレートファイルというのは このようなものです

Condition:
Fix:
Comments:

こんな感じの、有効なログメッセージの形式を満たすために記入する必要の あるフィールドの集合です。開発者みんなが -m オプションを使ってコミッ トする場合にはあまり有用ではありませんが、多くはそうではないでしょう。 -m で指定するかわりに

floss$ cvs commit

を実行して、CVS が自動的にテキストエディタ(EDITOR 環境変数で指定して あるやつです)を起動するのを待って、そこにログメッセージを記入し、セー ブして終了します。そのあと、CVS はコミット処理を続けます。

このシナリオに沿う場合、rcsinfo テンプレートはユーザがタイプする前に エディタ中に挿入されますので、記入すべきフィールドが表示されます。 ユーザがコミットした時点で verifymsg 中で指定された適切なプロ グラムが起動されます。ログメッセージがフォーマットに沿っているかチェッ クし、結果が終了コードに反映されます(ゼロの場合成功)。

検証プログラムに便利なように、verifymsg のプログラムの最後の 引数として、テンプレートへのパスが rcsinfo ファイルから追加されます。 なので、お望みならテンプレートをもとに検証処理ができます。

リモートマシンへ作業コピーをチェックアウトした場合には、対応する rcsinfo テンプレートファイルもクライアントに送信されます(作業コピー の CVS/ サブディレクトリに保存されます)。しかしこれは、チェックアウ トより後にサーバ上の rcsinfo ファイルが変更された場合、クライアント 側からはその変更が再度チェックアウトしない限りわからないということに なるので注意して下さい(update しただけではダメなんです)。

また、verifymsg ファイルでは ALL キーワードがサポートされていないこ とに注意して下さい(DEFAULT はサポートされているのですが)。デフォルト の検証スクリプトを、サブディレクトリ向けのスクリプトでオーバライドし やすくなっているのです。


Node:The taginfo File, Next:, Previous:The verifymsg And rcsinfo Files, Up:The CVSROOT/ Administrative Directory

The taginfo File

loginfo がログメッセージに対して行うようなことを、taginfo はタグに対 して行います。taginfo の左側は例によって正規表現で、右側はプログラム です。cvs tag の起動時、各プログラムには自動的に引数が渡されます。順 序は次の通り:

arg 1:          タグ名
arg 2:          オペレーション種別 ("add" => tag, "mov" => tag -F, "del" => tag -d)
arg 3:          リポジトリ
arg 4, 5, etc:  ファイルリビジョン [ファイルリビジョン ...]

プログラムがノンゼロを返した場合、tag コマンドは中断されます。

tag コマンドの -F オプションについてまだ説明していませんでしたね、で も上に書いてあることでわかると思います: タグをあるリビジョンから別の リビジョンへ移動します。例えば、Known_Working というタグがあ るファイルのリビジョン1.7につけられていて、それをリビジョン1.11につ けなおしたい場合には、こうします

cvs tag -r 1.11 -F Known_Working foo.c

こうすると1.7(またはそのファイルでそのタグがつけられていたところ)か らはタグが削除され、1.11につけられます。


Node:The cvswrappers File, Next:, Previous:The taginfo File, Up:The CVSROOT/ Administrative Directory

The cvswrappers File

cvswrappers ファイル(くどい名前ですよね)は、どのファイルをバイナリと して扱うかをファイル名によって指定するものです。CVS は、たとえば .jpg ファイルをすべて JPG 画像データと見なしたりはしないので、JPG ファ イルを追加するときに自動的に -kb オプションをつけたりはしてくれません。 ですが、プロジェクトによっては JPG ファイルはすべてバイナリだと指定 できればとても便利です。次のような行を cvswrappers ファイルに書けば そのようなことができます:

*.jpg -k 'b'

b が離れており、クオートされています。RCS の展開モードキーワー ドは b だけではないからです。o を指定すると $ は展開するが改行の変換は行わないという意味になります。しかし、 b は最も普通のパラメータではあります。

wrappers ファイルから指定できるモードはあといくつかありますが、滅多 にない状況のためのものなので、ここに書くほどの価値はありません(意訳: 著者はそれらを使ったことがありません)。好奇心旺盛なアナタは Cederqvist の Wrappers ノードを参照してくださいね。


Node:The editinfo File, Next:, Previous:The cvswrappers File, Up:The CVSROOT/ Administrative Directory

The editinfo File

このファイルはもう obsolete です、ディストリビューションにただ含まれ ているだけです。無視して下さい。


Node:The notify File, Next:, Previous:The editinfo File, Up:The CVSROOT/ Administrative Directory

The notify File

このファイルは CVS の watch 機能とともに用いられるものです。 watch 機能については Advanced CVS を参照のこと。watch (便利ですが必須の機能ではありません)というのが何かわからなければ何を 書いても意味ありませんので、このファイルと watch について詳しくは Advanced CVS をお読み下さい。


Node:The checkoutlist File, Previous:The notify File, Up:The CVSROOT/ Administrative Directory

The checkoutlist File

CVSROOT/ の中を見ると、ファイルの作業コピーが RCS リビジョンファイル と並んでいるのが見えると思います:

floss$ ls /usr/local/newrepos/CVSROOT
checkoutlist     config,v       history     notify     taginfo
checkoutlist,v   cvswrappers    loginfo     notify,v   taginfo,v
commitinfo       cvswrappers,v  loginfo,v   passwd     verifymsg
commitinfo,v     editinfo       modules     rcsinfo    verifymsg,v
config           editinfo,v     modules,v   rcsinfo,v

floss$

CVS がその動作を決める際にはその作業バージョンだけを見て、RCS ファイ ルは見ません。従って、CVSROOT/ の作業コピーをコミットすると(別のマシン にチェックアウトされていようとも)、CVS は自動的にリポジトリ内のファ イルを更新します。コミットの最後に次のようなメッセージが表示されるの で、何が起きたかわかると思います:

floss$ cvs ci -m "added mp and asub modules" modules
Checking in modules;
/usr/local/newrepos/CVSROOT/modules,v  <--  modules
new revision: 1.2; previous revision: 1.1
done
cvs commit: Rebuilding administrative file database

CVS は標準の管理ファイルに何があったか知っていて必要に応じて CVSROOT/ を再構築します。CVSROOT/ 中にカスタムファイルを置きたければ (プログラムや rcsinfo テンプレートファイル等)、他のファイルと同様に 扱うよう、CVS に明示的に伝えなければなりません。

これを伝えるために、checkoutlist ファイルがあります。今まで見てきた ファイルとは違うフォーマットになります。

FILENAME     ERROR_MESSAGE_IF_FILE_CANNOT_BE_CHECKED_OUT

例えば、

log.pl           unable to check out / update log.pl in CVSROOT

bugfix.tmpl      unable to check out / update bugfix.tmpl in CVSROOT

CVSROOT 中のいくつかのファイルは、伝統的にリビジョン管理下には置かな いことになっています。1つは history で、これはリポジトリ中の全 ての操作の a running record を保持しており、cvs history コマンドが使用します(このコマンドは指定されたファイルまたはプロジェ クトディレクトリについて、チェックアウト、アップデート、タグ付けの状 況の一覧を表示します)。 偶然 history ファイルを削除してしまっ たら、CVS はログを取るのをやめます。

注意: history ファイルがパーミッション問題の原因になることがあります が、解決するにはファイルをワールドライタブルにするか、ただ削除するか してください。

リビジョン管理されていないファイルには他に passwd ファイルが あります。それがネットワーク越しにチェックアウトされたとしたら、パス ワードの信頼性を損うからです(たとえ暗号化されていようとも)。passwd を checkoutlist に追加するかどうかは、守るべきキュリティの状況を鑑み て決定してください。デフォルトでは checkoutlist に入っていません。

CVSROOT/ ディレクトリについて最後に2つほど: すごいミスをして、壊れた 管理ファイルをコミットしたあげくどんなコミットも全く実行できなくなっ たとしましょう。ありうる話です。もしそうなったら、管理ファイルを直し てコミットしなおそうとしても、当然できないわけです。これを解決するに は、管理ファイルのリポジトリ内の作業コピーを手で編集して直します。直 せるまで、リポジトリ全体がアクセス不可の状態のままになります。

また、セキュリティを確保するため、CVSROOT/ ディレクトリは信頼できる ユーザのみが書き込めることを確認してください(信頼できるという のは、その人にパスワードの信頼性を損わないだけの能力があってその努力 ができることを信頼できる、という意味です)。*info ファイルは任 意のプログラムを実行できる権限を与えてしまうので、CVSROOT/ ディレク トリ中のファイルをコミットまたは編集できる人はすなわち、基本的にはシ ステムのどんなコマンドでも実行できるということです。このことは心に留 めておくべきです。


Node:Commit Emails, Next:, Previous:The CVSROOT/ Administrative Directory, Up:Repository Administration

Commit Emails

loginfo ファイルはコミットメール、すなわちコミットが発生した時にプロ ジェクトで作業している全員に送信される自動電子メールを設定できます。 (commitinfo ではなく loginfo で設定するというのは直観に反するように 見えるかもしれませんが、ポイントはメールにログメッセージを含めたいと いうところです) 配送プログラム(CVS のソースディストリビューション に入っている contrib/log.pl)はシステム上のどこにインストール してもかまいません。わたしはリポジトリの CVSROOT/ サブディレクトリ内 に置くのを週間にしていますが、それはただ好みの問題です。

システム上でうまく動くようにするため、ほんの少しだけ log.pl を編集する必要があるかもしれません、おそらく Perl インタプリタを指す 最初の行と、あとは多分この行

$mailcmd = "| Mail -s 'CVS update: $modulepath'";

をお好みのメーラ(Mailという名前かもしれないし違うかもしれない) を起動するように直すくらいでしょう。好きなように設定し終わったら、 loginfo ファイルに下記と同じ行を書き加えてください:

listerizer CVSROOT/log.pl %s -f CVSROOT/commitlog -m listerizer@red-bean.com
RoadMail   CVSROOT/log.pl %s -f CVSROOT/commitlog -m roadmail@red-bean.com
bk/*score  CVSROOT/log.pl %s -f CVSROOT/commitlog -m \
                                        bkscore-devel@red-bean.com

%s はコミットされたファイルの名前に展開されます。 log.pl の -f オプションはファイル名を取り、そのファイルにログ メッセージが追加されます(ですから CVSROOT/commitlog は永久に成長する ログメッセージのファイルになります)。-m フラグはメールアドレスを取り、 log.pl はそのアドレスへコミットのメッセージを送信します。そこ に書くアドレスは通常メーリングリストですが、ひとつの log.pl コマンド ラインに -m オプションを必要なだけ並べることもできます。


Node:Finding Out More, Previous:Commit Emails, Up:Repository Administration

Finding Out More

この章で CVS のインストールと管理を完全に紹介しようと思ったのですが、 あまりにも使うことがなくて言及する価値がない事柄や、既に Cederqvist マニュアルで十分説明されているようなことを積み残してしまいました。後 者に属することとしては、他のリモートアクセス方法、RSH/SSH, kserver (ケルベロス4), GSSAPI(ケルベロス5等を含む) の設定が挙げられます。問 題のユーザがリポジトリマシンへ RSH か SSH でログインできることを確認 する以外に何も特別なことをする必要がない、ということだけは言っておく べきでしょう。それができて、クライアントにもサーバにも CVS がインス トールしてあって、サーバマシンで直接リポジトリを使える正しい権限があ れば、:ext: メソッドを経由してリモートからリポジトリにアクセスできま す。

CVS の機能のうち特殊ないくつかは、のちほどの章で説明してありますので、 その機能が明らかに便利だとわかる文脈のなかで紹介します。一般的な CVS のトラブルシューティングのコツについては Tips And Troubleshooting に書いてあります。 Cederqvist マニュアル全部を読む 必要はありませんが、親しんでおくべきです。貴重なリファレンスツールで すから。もし何か理由があってあなたのマシンで Info が動かなくて、マニュ アルをプリントアウトするのもダメなら、オンラインで http://durak.org/cvswebsites/doc/ または http://www.loria.fr/~molli/cvs/doc/cvs_toc.html で閲覧できま す。


Node:Advanced CVS, Next:, Previous:Repository Administration, Up:Top

Advanced CVS

さて、これまでに CVS の使いかたの基礎の考えかたとリポジトリ管理につ いて見てきました。今度は CVS を開発プロセス全体に導入するやりかたを 見ていきたいと思います。基本的な CVS の作業サイクル(checkout, update, commit, update, commit, …)は An Overview of CVS で示 しました。この章ではそのサイクルを改善し、開発者間のコミュニケーショ ンの補助・プロジェクトの活動と履歴の概要の提供・開発の別ブランチの分 離と統一・よくある作業の自動化に対し、CVS をどう役立てていくかについ て議論します。テクニックの説明のなかで新しい CVS コマンドを紹介して いることもありますが、多くは既に知っているコマンドのより良い使い方を 説明しているだけです。


Node:Watches (CVS As Telephone), Next:, Up:Advanced CVS

Watches (CVS As Telephone)

プロジェクトを CVS で使用する場合の主な利点は、CVS がコミュニケーショ ン機器として、また記録係としての機能を果たすことです。この節ではプロ ジェクトの参加者に、プロジェクト内で何が起こっているかを通知するのに CVS をどう使えばよいかということを集中的に取り上げます。 **************** As is true with other aspects of CVS, these features reward cooperation. **************** 参加者は通知してもらい たいに違いありません; コミュニケーション機能を使わないなら、CVS はそ れについてなにもしないこともできます。


Node:How Watches Work, Next:, Up:Watches (CVS As Telephone)

How Watches Work

CVS はデフォルトでは各作業コピーを独立したサンドボックスとして扱いま す。あなたが変更をコミットするまでは、あなたが作業コピーで何をしてい るのか、誰にもわかりません。そしてあなたも、他の人が自分の作業コピー で何をしているのかなんてわかりません、通常のコミュニケーション方法を とる以外ないのです。たとえば廊下で「ねえ、いま parse.c いじろうと思 うんだけどさ、コンフリクトすんのヤだから編集するときは教えてね!」な どと叫んだりするしか。

このような非公式なやりかたは、誰が何を担当しているか全般について皆が 知っているようなプロジェクトならうまくいきます。が、このやりかたでは 大人数の開発者がコードベースの全てをいじっていて、かつコンフリクトを 避けたいような場合にはうまくいかないでしょう。このような場合、メンバー が地理的に分散しているので、お互いの分担責任領域に頻繁に踏み込む必要 があっても廊下で叫び合うわけにはいきません。

CVS には監視(watch)と呼ばれる機能があって、ある時刻に誰がどのファ イルをいじっているかをお互いに通知し合うことができます。ある開発者が、 あるファイルを監視設定すると、他のだれかがそのファイルをいじり 始めるとその開発者に CVS から通知が来ます。その通知は普通メールで送 信されますが、他の方法に設定することもできます。

監視を使うには、リポジトリの管理エリア内のファイルを1つか2つ変更 しなければなりません。また、開発者はチェックアウト/アップデート/コミッ トのサイクルに余分な手順を加えなければなりません。リポジトリ側の変更 はごく簡単なものです: CVSROOT/notify ファイルを編集して、CVS がどのように通知すればいいかを設定します。CVSROOT/users ファ イルに電子メールアドレスの行を追加します。

作業コピー側では、開発者は CVS に対し、どのファイルを監視したい かを CVS に指示し、他の誰かがそれらのファイルを編集し始めたら CVSが 通知を送れるようにします。また、ファイルを編集し始めた時やし終わった 時には CVS にそれを知らせなければなりません。CVS はそれを受けて、そ のファイルを監視している他の人に通知します。これらの手順 を実現するコマンドを次に示します:

watchコマンドは通常の CVS コマンドのパターンとは違い、もう一 段階サブコマンドを必要とします。cvs watch add..., cvs watch remove...,などなど。

以下の例で、リポジトリ側で監視を有効にするやりかたと、開発者側で の監視の使いかたを見ていきます。2人のユーザ、jrandom と qsmith を例にとります。同じプロジェクトの作業コピーをそれぞれが持っていて、 それらは別のマシン上にあるかもしれません。全ての例において、 $CVSROOT 環境変数が設定してあると仮定しますので、cvs コマンドに -d <REPOS> をつける必要はありません。


Node:Enabling Watches In The Repository, Next:, Previous:How Watches Work, Up:Watches (CVS As Telephone)

Enabling Watches In The Repository

まずは CVSROOT/notify ファイルを編集して、電子メールでの通知を有 効にします。開発者の1人がやってもいいですし、もし開発者にリポジトリ の管理ファイルを変更する権限がなければ、管理者がやってもいいです。ど ちらにしろ、まずは管理領域をチェックアウトして、それから notify ファ イルを編集します:

floss$ cvs -q co CVSROOT
U CVSROOT/checkoutlist
U CVSROOT/commitinfo
U CVSROOT/config
U CVSROOT/cvswrappers
U CVSROOT/editinfo
U CVSROOT/loginfo
U CVSROOT/modules
U CVSROOT/notify
U CVSROOT/rcsinfo
U CVSROOT/taginfo
U CVSROOT/verifymsg
floss$ cd CVSROOT
floss$ emacs notify
...

notify ファイルは、初回編集時にはこのようになっています:

# The "notify" file controls where notifications from watches set by
# "cvs watch add" or "cvs edit" are sent. The first entry on a line is
# a regular expression which is tested against the directory that the
# change is being made to, relative to the $CVSROOT. If it matches,
# then the remainder of the line is a filter program that should contain
# one occurrence of %s for the user to notify, and information on its
# standard input.
#
# "ALL" or "DEFAULT" can be used in place of the regular expression.
#
# For example:
# ALL mail %s -s "CVS notification"

最後の行の冒頭の#を取ってコメントをはずすだけです。notify ファ イルは他の管理ファイルと同様、ディレクトリ名に対する正規表現を書ける 柔軟なインタフェースを提供していますが、実際はその柔軟さを活用するこ とはほとんどありません。リポジトリの特定部分にマッチする正規表現を複 数行で書くことがあるとすれば、各プロジェクト用に別々の通知機構を使い たいときだけでしょう。しかし、通常の電子メールは完璧に良い通知機構な ので、大抵はそれを使います。

電子メール通知を指定するには、標準的な Unix マシンではこの行

ALL mail %s -s "CVS notification"

を書けば動きます。このコマンドは通知をサブジェクトCVS notificationの電子メールで送ります(例によって、特殊表現 ALL は全て のディレクトリにマッチします)。この行のコメントをはずしたら、notify ファイルをコミットしてリポジトリに変更を知らせましょう:

floss$ cvs ci -m "turned on watch notification"
cvs commit: Examining .
Checking in notify;
/usr/local/newrepos/CVSROOT/notify,v  <--  notify
new revision: 1.2; previous revision: 1.1
done
cvs commit: Rebuilding administrative file database
floss$

notify ファイルをこのように編集しさえすればリポジトリの監視ができま す。しかしながら、リモートからプロジェクトに参加している開発者がいる 場合、CVSROOT/users ファイルも編集する必要があります。users ファイルの目的は、外部のメールアドレスを持っているユーザに対し、電子 メール通知をどこへ送信すればよいかを CVS に指示することです。users ファイルの各行の形式は:

CVS_USERNAME:EMAIL_ADDRESS

です。例えば、

qsmith:quentinsmith@farawayplace.com

行の冒頭の CVS ユーザ名というのは、そのユーザ名が CVSROOT/passwd に存在し、かつ pserver アクセスを使っている場合はそのユーザに、そうでな ければ CVS を実行している人のサーバ側のシステムユーザ名に対応します。コ ロンのあとにはそのユーザに通知を送る場合の外部のメールアドレスを書きます。 これを書いている時点では、users ファイルは CVS ディストリビューション中 に存在しません。管理ファイルなので、普通のやりかたでファイルを作成して cvs add して commit してもらって、それに加え CVSROOT/checkoutlist に書き足してください。こうするとリポジトリ内のチェックアウトコピーが常に 保守されるようになります。

これを行うセッションの実地例をお見せしましょう:

floss$ emacs checkoutlist
  ... (add the line for the users file) ...
floss$ emacs users
  ... (add the line for qsmith) ...
floss$ cvs add users
floss$ cvs ci -m "added users to checkoutlist, qsmith to users"
cvs commit: Examining .
Checking in checkoutlist;
/usr/local/newrepos/CVSROOT/checkoutlist,v  <--  checkoutlist
new revision: 1.2; previous revision: 1.1
done
Checking in users;
/usr/local/newrepos/CVSROOT/users,v  <--  users
new revision: 1.2; previous revision: 1.1
done
cvs commit: Rebuilding administrative file database
floss$

CVSROOT/users にメールアドレスを拡張形式で書くこともできますが、 空白文字には注意して、全てクオートしてください。例えば、次のは動きますが:

qsmith:"Quentin Q. Smith <quentinsmith@farawayplace.com>"

または

qsmith:'Quentin Q. Smith <quentinsmith@farawayplace.com>'

しかし、これは動きません:

qsmith:"Quentin Q. Smith" <quentinsmith@farawayplace.com>

動くかどうかわからないと思う時は、 notify ファイルに指定するコマンドライ ンを手で動かしてテストしてください。下記の %s

mail %s -s "CVS notification"

users ファイルのコロンのあとに書いたのと同じものに置き換えてください。コ マンドプロンプトでやってみて動いたら、users ファイルでも動きます。

これらが終わった時点で checkoutlist ファイルはこのようになっています:

# The "checkoutlist" file is used to support additional version controlled
# administrative files in $CVSROOT/CVSROOT, such as template files.
#
# The first entry on a line is a filename which will be checked out from
# the corresponding RCS file in the $CVSROOT/CVSROOT directory.
# The remainder of the line is an error message to use if the file cannot
# be checked out.
#
# File format:
#
#       [<whitespace>]<filename><whitespace><error message><end-of-line>
#
# comment lines begin with '#'

users   Unable to check out 'users' file in CVSROOT.

users ファイルはこのようになっていると思います:

qsmith:quentinsmith@farawayplace.com

さて、これでリポジトリの監視用設定はできました。次に開発者が作業コピーで しなければならないことを見ていきましょう。


Node:Using Watches In Development, Next:, Previous:Enabling Watches In The Repository, Up:Watches (CVS As Telephone)

Using Watches In Development

開発者はまず、作業コピーをチェックアウトして、次にプロジェクト中の1ファ イルの監視者リストに自分を加えます:

floss$ whoami
jrandom
floss$ cvs -q co myproj
U myproj/README.txt
U myproj/foo.gif
U myproj/hello.c
U myproj/a-subdir/whatever.c
U myproj/a-subdir/subsubdir/fish.c
U myproj/b-subdir/random.c
floss$ cd myproj
floss$ cvs watch add hello.c
floss$

最後のコマンド cvs watch add hello.c で、誰かが hello.c で作業しはじめた ら jrandom に通知するよう CVS に対し指示します(つまり、 jrandom を hello.c の監視リストに加えます)。あるファイルが編集され次第 CVS が通知を 送るするためには、編集しようとするユーザがそのファイルに対してまず cvs edit を実行する必要があります。CVS にとって、編集が始まったことを知る方 法は他にありません。チェックアウトが済んだら通常 CVS は次のアップデート かコミットまで起動されることはありませんから、そのときにはもうそのファイ ルは編集されているかもしれません:

paste$ whoami
qsmith
paste$ cvs -q co myproj
U myproj/README.txt
U myproj/foo.gif
U myproj/hello.c
U myproj/a-subdir/whatever.c
U myproj/a-subdir/subsubdir/fish.c
U myproj/b-subdir/random.c
paste$ cd myproj
paste$ cvs edit hello.c
paste$ emacs hello.c
...

qsmith が cvs edit hello.c を実行したとき、CVS は hello.c の監視リストを 見て jrandom がその中にいることを知り、jrandom に対して qsmith がそのファ イルを編集しはじめたことをメールを送って知らせます。メールは qsmith から 来たようなかたちになります:

From: qsmith
Subject: CVS notification
To: jrandom
Date: Sat, 17 Jul 1999 22:14:43 -0500

myproj hello.c
--
Triggered edit watch on /usr/local/newrepos/myproj
By qsmith

加えて、qsmith (またはどの人でも) が hello.c の新しいリビジョンをコミッ
トするたびに、jrandom はメールを受けとります:

myproj hello.c
--
Triggered commit watch on /usr/local/newrepos/myproj
By qsmith

これらのメールを受けとったら、jrandom は hello.c をアップデートして qsmith が何をしたか見てみたいでしょうし、qsmith にそのファイルをいじった 理由をきくためにメールするでしょう。qsmith は cvs edit を実行するのを忘 れないように強制はされなかったことに注意してください。おそらく、自分が何 をしようとするかを jrandom に知ってもらいたかったから実行したのです(どち らにしろ、cvs edit するのを忘れていたとしても、コミットすれば通知が起こ ります)。cvs edit を使うのは、あるファイルをいじりはじめる前にそのファイ ルを監視している人たちにそれを知らせるのが目的です。監視している人たちが、 コンフリクトが起こるかもしれないと思った場合には連絡できますから、時間を 無駄にせずに済むでしょう。

あるファイルに対し cvs edit を実行した人は全てそのファイルの監視リストに 加えて欲しいと思っている(少なくとも一時的に、誰か他の人がそのファイルを 編集しはじめた場合とかには)、と CVS は仮定します。qsmith が cvs edit を 実行した場合、彼は hello.c の監視者になります。彼と jrandom は第三者が cvs edit を実行した場合(あるいはコミットした場合)、通知を受けとります。

しかし、CVS はそのファイルを編集中の人は編集している間だけ監視者リストに 居たいだろうと仮定します。そのようなユーザは編集が済んだら監視者リストか らはずされます。そのファイルをずっと監視したい場合は cvs watch add を実 行する必要があります。また、デフォルトでコミット時には編集が終わったと仮 定します(とりあえず次回までは)。

あるファイルに対し cvs edit を走らせただけで監視者リストに入っ た人は、一時監視者(temporary watcher)と呼ばれ、変更をコミットし次 第監視者リストからはずされます。そのファイルを再度編集したい場合は、 cvs edit を再実行する必要があります。

変更作業が終わるまで何回コミットする必要があるかということは CVS にはわ かりませんので、最初のコミットで編集が終わると仮定する以外に良い方法はあ りません。この仮定は1回限り(one-off)の変更の場合、つまりファイルの 1個所だけ直してコミットするなら、うまくいきます。ちょっと長いあいだ編集 して何度かコミットするような場合は、自分を恒久的に監視リストに加えたほう がよいでしょう:

paste$ cvs watch add hello.c
paste$ cvs edit hello.c
paste$ emacs hello.c
...
paste$ cvs commit -m "print hello in Sanskrit"

watch add したので、qsmith はコミット後も hello.c を監視したままになりま す。(ところで、qsmith は自分が編集したことについては通知されません、他の 人にだけ通知が行きます。CVS はかしこいので、誰かがやったことをその人自身 宛に通知したりしないのです)


Node:Ending An Editing Session, Next:, Previous:Using Watches In Development, Up:Watches (CVS As Telephone)

Ending An Editing Session

コミットしたくないけど編集が終わったことを明示したい場合は、cvs unedit を実行しましょう:

paste$ cvs unedit hello.c

でも、気をつけてください! こうすると、監視している人全員に編集を終えたこ とを知らせる以上のことをします。そのファイルに加えたけれどコミットしてい ない変更を、取り消すかどうか聞いてきます:

paste$ cvs unedit hello.c
hello.c has been modified; revert changes? y
paste$

もし y と答えると、CVS は変更を全て取り消したあと、あなたがもはや そのファイルを編集中ではないということを監視者に知らせます。n と 答えると、CVS は変更をそのままに置いておき、あなたは編集者として登録され たままになります(通知はされません、つまりcvs uneditを全く実行 しなかったのと同じということになります)。キーの1打の違いで変更が全部取り 消されてしまうのはちょっとこわいような気もしますが、原則は理解しやすいと 思います: その編集を終える旨世界に宣言するということは、コミットしていな い変更を置いておく意味はない、ということです。少なくとも CVS はそのよう に理解します。おせっかいかもしれませんが、十分気をつけてくださいね!


Node:Controlling What Actions Are Watched, Next:, Previous:Ending An Editing Session, Up:Watches (CVS As Telephone)

Controlling What Actions Are Watched

デフォルトでは、監視している人には3種類のアクションが通知されます: edit, commit, unedit です。ですが、たとえば commit だけを通知して欲しい場合に は -a フラグ(a は action という意味)で通知のしかたを調整することができ ます:

floss$ cvs watch add -a commit hello.c

あるいは、edit と commit だけを監視したいけれど unedit は気にしないとい う場合には -a フラグを2つ渡してください:

floss$ cvs watch add -a edit -a commit hello.c

-a フラグで監視を追加する場合、いまある監視を削除したりはしません。もし 既に3種類のアクションを hello.c について監視している場合には、

floss$ cvs watch add -a commit hello.c

とやっても何の効果もありません、3種類全部を監視したままになります。監視 を削除したければ、

floss$ cvs watch remove hello.c

を実行してください。add と同じように、デフォルトでは3つのアクション全て の監視を削除します。 -a 引数を渡すと、指定した監視のみを削除します:

floss$ cvs watch remove -a commit hello.c

これは commit についての通知を受けとるのをやめるが、edit と unedit につ いての通知は引き続き受けとる、という意味になります(初めに edit と unedit を監視しているとすればそうなるということです)。

-a フラグに渡すアクションで、2つ特殊なものがあります: all と none です。 前者は監視できるアクション全てを意味し(これを書いている時点では edit, commit, unedit です)、後者はどれでもないという意味になります。-a がない 場合、CVS のデフォルト動作では全てのアクションを監視することになりますし、 どれも監視しないというのは監視者リストから自分を削除するというのに等しい ので、これら2つの特殊アクションを使うような状況というのは考えにくいです。 しかしながら、cvs edit は -a オプションを解釈しますので、この場合にはall または none を指定できるのは便利かもしれません。例えば、だれかがあるファ イルをとても短い間だけいじっていて、ほかの人がそのファイルで何をしている かの通知を受け取りたくないとしましょう。その場合、このコマンドを実行すると

paste$ whoami
qsmith
paste$ cvs edit -a none README.txt

README.txt の監視者は qsmith がそのファイルをいじろうとしているという通 知を受けとりますが、qsmith は自分の編集中 README.txt の一時監視者には加 えられません(通常は加えられます)。どのアクションも監視したくないと言った からです。

cvs watch コマンドでは、自分自身の監視をいじれるだけだということを覚えて おいてください。あるファイルについて自分自身が監視するのを止めることはで きますが、誰か他の人の監視状況を変えることはできません。


Node:Finding Out Who Is Watching What, Next:, Previous:Controlling What Actions Are Watched, Up:Watches (CVS As Telephone)

Finding Out Who Is Watching What

cvs edit を実行する前に誰が監視しているか知りたいと思うこともあるでしょ う。また、自分を監視リストに加えずに、誰が何を編集しているのか知りたいこ ともあるでしょう。あるいは、自分の状況を忘れてしまうこともあるでそう。監 視を設定/設定解除してファイルをコミットしたあとは、何を監視していて何を 編集中なのかを見失いがちです。

CVS は誰が監視していて、誰がファイルを編集中なのかを見せてくれるコマンド を2つ用意しています。cvs watchers と cvs editors です:

floss$ whoami
jrandom
floss$ cvs watch add hello.c
floss$ cvs watchers hello.c
hello.c jrandom  edit unedit  commit
floss$ cvs watch remove -a unedit hello.c
floss$ cvs watchers hello.c
hello.c jrandom  edit commit
floss$ cvs watch add README.txt
floss$ cvs watchers
README.txt      jrandom edit    unedit  commit
hello.c jrandom edit    commit
floss$

最後の cvs watchers コマンドはファイルを指定していないことに注意して ください。従って、すべてのファイルの監視者を表示します(つまり、監視 者のいるファイルをすべて、ということです)。

他の CVS コマンドと同様、すべての watch コマンドと edit コマンドはこ のように動作します。ファイル名を指定した場合、そのファイルについて動 作します。ディレクトリ名を指定した場合、そのディレクトリとそのサブディ レクトリ内のすべてについて動作します。何も指定しない場合、カレントディ レクトリとそれ以下できる限り深いレベルまですべてについて動作します。 たとえば(同じセッションを続けます):

floss$ cvs watch add a-subdir/whatever.c
floss$ cvs watchers
README.txt      jrandom edit    unedit  commit
hello.c jrandom edit    commit
a-subdir/whatever.c     jrandom edit    unedit  commit
floss$ cvs watch add
floss$ cvs watchers
README.txt      jrandom edit    unedit  commit
foo.gif jrandom edit    unedit  commit
hello.c jrandom edit    commit  unedit
a-subdir/whatever.c     jrandom edit    unedit  commit
a-subdir/subsubdir/fish.c       jrandom edit    unedit  commit
b-subdir/random.c       jrandom edit    unedit  commit
floss$

コマンドの最後2つはそれぞれ、 jrandom をプロジェクト中のすべてのファ イルの監視者にし、プロジェクト中の全ファイルの監視者リストを表示して います。cvs watchers の出力は可変長の情報とタブストップが 混合しているので、カラムがいつも完璧に整列しているわけではありません。 しかし、各行は一貫した形式になっています:

[FILENAME] [whitespace] WATCHER [whitespace] ACTIONS-BEING-WATCHED...

さて、qsmith がファイルをひとつ編集し始めたとして、何が起こるか見て みましょう:

paste$ cvs edit hello.c
paste$ cvs watchers
README.txt      jrandom edit    unedit  commit
foo.gif jrandom edit    unedit  commit
hello.c jrandom edit    commit  unedit
       qsmith  tedit   tunedit tcommit
a-subdir/whatever.c     jrandom edit    unedit  commit
a-subdir/subsubdir/fish.c       jrandom edit    unedit  commit
b-subdir/random.c       jrandom edit    unedit  commit

hello.c にはもう一人監視者が増えました: qsmith です(ファイル名は繰り 返されませんが、行の冒頭は空白文字になっていることに注意してください。 このことは watchers の出力を読むプログラムを書くときに重要になるでしょ う) hello.c を編集しているので、qsmith はそのファイルを 一時監 視 していることになります。ただし彼が hello.c の新しいリビジョンを コミットするとそうではなくなってしまいます。各アクションの前にあるプ レフィクス t は、これらが一時監視であることを示します。 qsmith がまた、自分自身を hello.c の正規監視者とした場合

paste$ cvs watch add hello.c
README.txt      jrandom edit    unedit  commit
foo.gif jrandom edit    unedit  commit
hello.c jrandom edit    commit  unedit
       qsmith  tedit   tunedit tcommit edit    unedit  commit
a-subdir/whatever.c     jrandom edit    unedit  commit
a-subdir/subsubdir/fish.c       jrandom edit    unedit  commit
b-subdir/random.c       jrandom edit    unedit  commit

彼は、一時監視者かつ常時監視者であると表示されます。常時監視状態は一 時監視をオーバライドして次のようになると思ったかもしれませんが:

        qsmith  edit    unedit  commit

しかし、どんな順序で起こるかわからないので CVS は一時監視を置き換え ることはできません。qsmith が編集を終わる前に常時監視をやめたとした ら? あるいは監視したままで編集を終えたとしたら? 前者の場合、tedit/ tunedit/tcommit アクションを残したままedit/unedit/commit アクション は削除されます。後者の場合、逆になります。

とにかく、通常は監視者リスト側のことはあまり気にすることはありません。 たいていはプロジェクトのトップレベルで

floss$ cvs watchers

floss$ cvs editors

を実行し、誰が何をしているか見ればよいのです。誰がどのアクションに注 意しているか、詳細を知る必要はありません、重要なのは人とファイルです。


Node:Reminding People To Use Watches, Next:, Previous:Finding Out Who Is Watching What, Up:Watches (CVS As Telephone)

Reminding People To Use Watches

監視機構は開発者全員の協力に全面的に依存しているということに、既にお 気づきだと思います。誰かが cvs edit をし忘れてファイルを編集し始めた 場合、変更がコミットされるまで、誰もそのことを知らないということになっ てしまいます。cvs edit というのは通常の開発のサイクルに入っておらず、 余分に手数のかかるものですから、忘れやすいと言えるでしょう。

CVS は cvs edit を使うよう強制はできませんが、そうするのを忘れないよ うな機構を持っています。それは、watch on コマンドです:

floss$ cvs -q co myproj
U myproj/README.txt
U myproj/foo.gif
U myproj/hello.c
U myproj/a-subdir/whatever.c
U myproj/a-subdir/subsubdir/fish.c
U myproj/b-subdir/random.c
floss$ cd myproj
floss$ cvs watch on hello.c
floss$

jrandom が cvs watch on hello.c を実行すると、これ以降の myproj のチェッ クアウトにおいて、作業コピー中の hello.c は読み出し専用で作成される ようになります。qsmith がそれをいじろうとすると、hello.c は読み出し 専用であることに気づき、まず cvs edit を実行することを思い出します:

paste$ cvs -q co myproj
U myproj/README.txt
U myproj/foo.gif
U myproj/hello.c
U myproj/a-subdir/whatever.c
U myproj/a-subdir/subsubdir/fish.c
U myproj/b-subdir/random.c
paste$ cd myproj
paste$ ls -l
total 6
drwxr-xr-x   2 qsmith    users        1024 Jul 19 01:06 CVS/
-rw-r--r--   1 qsmith    users          38 Jul 12 11:28 README.txt
drwxr-xr-x   4 qsmith    users        1024 Jul 19 01:06 a-subdir/
drwxr-xr-x   3 qsmith    users        1024 Jul 19 01:06 b-subdir/
-rw-r--r--   1 qsmith    users         673 Jun 20 22:47 foo.gif
-r--r--r--   1 qsmith    users         188 Jul 18 01:20 hello.c
paste$

こうすると、ファイルは読み書きできるようになります。編集してコミット すると、また読み出し専用になります:

paste$ cvs edit hello.c
paste$ ls -l hello.c
-rw-r--r--   1 qsmith    users         188 Jul 18 01:20 hello.c
paste$ emacs hello.c
  ...
paste$ cvs commit -m "say hello in Aramaic" hello.c
Checking in hello.c;
/usr/local/newrepos/myproj/hello.c,v  <--  hello.c
new revision: 1.12; previous revision: 1.11
done
paste$ ls -l hello.c
-r--r--r--   1 qsmith    users         210 Jul 19 01:12 hello.c
paste$

彼の edit と commit は hello.c の監視者全員に通知されます。jrandom は監視者でなくてもかまわないことに注意してください。cvs watch on hello.c を実行しても、jrandom はそのファイルの監視者リストの一員になっ たりはしないのです。ただ読み出し専用でチェックアウトされるように指定 しただけになります。ファイルを監視したい人は自分を監視者リストに加え るのを忘れないようにする必要があります。CVS はその面倒は見ません。

ファイルひとつについて watch on するのは例外的な状況です。一般にはプ ロジェクト全体を watch on するのが普通です:

floss$ cvs -q co myproj
U myproj/README.txt
U myproj/foo.gif
U myproj/hello.c
U myproj/a-subdir/whatever.c
U myproj/a-subdir/subsubdir/fish.c
U myproj/b-subdir/random.c
floss$ cd myproj
floss$ cvs watch on
floss$

このアクションはプロジェクト全体についてのポリシーをアナウンスするの に等しいです: 「ファイルをいじるときは cvs edit を使って監視してる人 に知らせてね。興味があったり、担当のファイルは自由に監視してください」 プロジェクト中の全ファイルは読み出し専用でチェックアウトされますので、 何かいじろうとすると cvs edit をしなければならないのを思い出すことで しょう。

おもしろいことに、監視されているファイルをチェックアウトすると読み出 し専用になるのに、アップデートではそうはなりません。jrandom が cvs watch on する前に既に qsmith が作業コピーをチェックアウトしていたと すると、qsmith のファイルはアップデートしても読み書きできるままにな ります。しかし、jrandom が watch on した後でコミットしたファイルは読 み出し専用になります。jrandom が watch off しても、

floss$ cvs watch off

qsmith の読み出し専用ファイルは自動的に読み書きできるようになったり はしません。一方、コミット後に読み出し専用には戻らなくなります(watch on のままだと読み出し専用に戻ります)。qsmith がひねくれていて、 cvs edit を全く避けて chmod コマンドで作業コピー中 のファイルを書込み可にしたとします

paste$ chmod u+w hello.c

またはこのように全部一挙に

paste$ chmod -R u+w .

したとします。

こういう風なことをされると CVS には手も足も出ません。もともと作業コ ピーというのはプライベートなサンドボックスなのです。監視機構は the watch features can open them up to public scrutiny a little bit, but only as far as the developer permits. Only when a developer does something that affects the repository (such as commits) is her privacy unconditionally lost.

watch add, watch remove, watch on, watch off の関係はちょっとわかり にくいかもしれません。全体の規則をまとめるとこうなります:

addremove はそのファイルの監視者リストにユーザを追 加したり、削除したりするもので、チェックアウト時やチェックアウト後に ファイルを読み出し専用にしたりはしません。onoff は ファイルのパーミッションについてのものです。ファイルの監視者リストに 居るユーザをどうこうしたりはしません。開発者に監視ポリシーを思い出し てもらうために作業コピー中のファイルを読み出し専用にするだけです。

ちょっと一貫していないように見えるかもしれません。監視機構を使うとい うのは、ある意味で CVS の本質に反しています。複数の開発者が自分の作 業コピー内で自由に編集し、そのことはコミットするまでお互いから見えな い、という理想世界からそれることになります。監視機構は開発者が自分の 作業コピーで何をしているかお互いに知らせるために CVS が提供している 便利な方法なのです。しかし監視ポリシーを強制する方法はありませんし、 編集セッションがどのように構成されるかについて絶対的なコンセプトがあ るわけでもありません。まあそれでも監視機構が役に立つような状況もある のです。


Node:What Watches Look Like In The Repository, Previous:Reminding People To Use Watches, Up:Watches (CVS As Telephone)

What Watches Look Like In The Repository

ブラックボックスと要らぬ神秘を打ち消すため、リポジトリ中で監視機構が どう実装されているかちょっと覗いてみることにしましょう。でも pretty ではないので、ほんとにちょっと覗くだけですよ。

監視するよう設定すると

floss$ pwd
/home/jrandom/myproj
floss$ cvs watch add hello.c
floss$ cvs watchers
hello.c jrandom edit    unedit  commit
floss$

CVS は適切なリポジトリのサブディレクトリ中のある特殊なファイル CVS/fileattr にそれを記録します:

floss$ cd /usr/local/newrepos
floss$ ls
CVSROOT/   myproj/
floss$ cd myproj
floss$ ls
CVS/          a-subdir/     foo.gif,v
README.txt,v  b-subdir/     hello.c,v
floss$ cd CVS
floss$ ls
fileattr
floss$ cat fileattr
Fhello.c        _watchers=jrandom>edit+unedit+commit
floss$

リポジトリに中の CVS サブディレクトリに fileattr が保存されるからと いって、リポジトリが作業コピーになったわけではありません。単に、 CVS という名前が既に作業コピー中で図書管理用に予約されているの で、リポジトリ中にその名前のサブディレクトリを作るようなプロジェクト は存在しないというのを確信できるから、という理由によります。

fileattr の形式をきちんと説明したりはしません。コマンドごとに そのファイルが変わるのを見るだけで理解できると思います。

floss$ cvs watch add hello.c
floss$ cat /usr/local/newrepos/myproj/CVS/fileattr
Fhello.c        _watchers=jrandom>edit+unedit+commit
floss$ cvs watch add README.txt
floss$ cat /usr/local/newrepos/myproj/CVS/fileattr
Fhello.c        _watchers=jrandom>edit+unedit+commit
FREADME.txt     _watchers=jrandom>edit+unedit+commit
floss$ cvs watch on hello.c
floss$ cat /usr/local/newrepos/myproj/CVS/fileattr
Fhello.c        _watchers=jrandom>edit+unedit+commit;_watched=
FREADME.txt     _watchers=jrandom>edit+unedit+commit
floss$ cvs watch remove hello.c
floss$ cat /usr/local/newrepos/myproj/CVS/fileattr
Fhello.c        _watched=
FREADME.txt     _watchers=jrandom>edit+unedit+commit
floss$ cvs watch off hello.c
floss$ cat /usr/local/newrepos/myproj/CVS/fileattr
FREADME.txt     _watchers=jrandom>edit+unedit+commit
floss$

fileattr 中のレコードを編集してみたりしましょう。qsmith が自分自身を 編集者として登録したときに起こることを見てみます:

paste$ cvs edit hello.c

floss$ cat /usr/local/newrepos/myproj/CVS/fileattr
Fhello.c        _watched=;_editors=qsmith>Tue Jul 20 04:53:23 1999 GMT+floss\
+/home/qsmith/myproj;_watchers=qsmith>tedit+tunedit+tcommit
FREADME.txt     _watchers=jrandom>edit+unedit+commit

そのディレクトリのファイル全部に監視者も編集者もいなくなると、CVS は fileattr ファイルも CVS サブディレクトリも削除してしまうことに注 意して下さい:

paste$ cvs unedit

floss$ cvs watch off
floss$ cvs watch remove
floss$ cat /usr/local/newrepos/myproj/CVS/fileattr
cat: /usr/local/newrepos/myproj/CVS/fileattr: No such file or directory
floss$

こうやってちょっと見てみるとおわかりの通り、fileattr のフォーマット の解釈の詳細は CVS に任せておいたほうがよいです。

そのフォーマットについて理解するのは、舞台裏でなにが起こっているのか 知って満足するというのを置いておくとすると、CVS の監視機構の拡張を書 くとか、そのへんをデバッグするとかが主な理由だと思います。普通はリポ ジトリの中に CVS/ サブディレクトリができたからといってびっくりしない 程度で十分です。CVS/ サブディレクトリは CVS にとって、監視者リストの ようなメタ情報を安全に保存しておける唯一の場所なのです。


Node:Log Messages And Commit Emails, Next:, Previous:Watches (CVS As Telephone), Up:Advanced CVS

Log Messages And Commit Emails

コミットメールとはコミット時に送信される通知で、ログメッセージやその コミットに含まれるファイル情報などを示します。通常はプロジェクトの参 加者全員に送信されます。そのプロジェクトに興味のある他の人々に送られ ることもあります。コミットメールの設定方法の詳細は Repository Administration で説明しましたのでここでは繰り返しません。

take into account 考慮に入れる

I have noticed, however, that commit emails can sometimes result in unexpected side effects to projects, effects that you may want to take into account if you set up commit emails for your project.

まず、メッセージは大概無視されることを覚悟して下さい。読まれるかどう かは、少なくとも部分的には、そのプロジェクトにおいてどのくらいの頻度 でコミットが行われるかによります。開発者は一日の終わりに1回だけ大き な変更をコミットするのでしょうか、それとも一日を通して小さな変更をた くさんコミットするのでしょうか? プロジェクトが後者に近いようなら、一 日中さみだれのようにやってくる小さな通知に対して開発者は壁を作ってし まうでしょうし、各メッセージに対して払う注意も小さくなるでしょう。

これは別に通知が役に立たないという意味ではないです、ただみんながメッ セージを全部読んでいるはずだなんて思っちゃいけないというだけのことで す。皆に誰が何をしているかを見張ってもらう方法(監視リストに追加する 以外で)としては依然便利なものです。誰でも購読できるメーリングリスト を送信先にすると、興味のあるユーザ(と未来の開発者)に、日常的にコード に何が起こっているかを知る機会を与える素晴らしい機構になります。

すべてのログメッセージを見ていて、プロジェクト全体の活動について概要 を知っている開発者がいるんだ、と考えたいだろうと思います(もちろん良 きプロジェクトリーダというものは何らかの手段でこういうことをしている のだろうと思います)。明確な分担区分がある場合、つまり、ある開発者が プロジェクト中のあるサブディレクトリの係りである、ということなんです が、その場合は CVSROOT/loginfo をちょっといじって、分担領域で変更が 行われたら、係の部隊が特別に印をつけた変更通知を受け取るようにもでき ます。これは、開発者が少なくとも自分のサブディレクトリに関係あるメー ルは読むよう保証するのに役に立つでしょう。

コミットメールが無視されない場合、おもしろい副作用が起こります。リア ルタイムのコミュニケーションの手段として使われ始めるのです。そうなっ た場合のログメッセージの例をお見せしましょう:

Finished feedback form; fixed the fonts and background colors on the
home page.  Whew!  Anyone want to go to Mon Lung for lunch?

別に何も悪いことはないですし、あとでログメッセージを読むと楽しいでしょ うね。でも、以下の例のようなログメッセージには注意しなければなりません。 ログメッセージは電子メールで配られるだけのものではなく、プロジェクト の履歴として永久に保存されるものなのです。顧客の仕様についてのグチを 言ったりするのは、プログラマの間で気晴らしによく行われることです。他 のプログラマがメールですぐ見るとわかっている場合、こんなログメッセー ジをコミットしてしまうのは想像に難くないでしょう:

Truncate four-digit years to two-digits in input.  What the customer
wants, the customer gets, no matter how silly & wrong.  Sigh.
(訳: 入力時の4桁の年を2桁に縮める。お客様は御所望のものを手に入れる、
どんなにバカみたいだろうが間違ってようが気にしない。はー。)

これはおもしろいメールにはなりますが、しかし顧客が後日このログをレビュー したらどうなりますか? (これと同じような問題のために、1つ以上のサイト で、ログメッセージに不快な単語が入らないようガードするスクリプトが起 動するように CVSROOT/loginfo が設定されているのに賭けますよ!)

コミットメールの一般的な効果は、短く不明瞭なログメッセージを書きにく くなるということです。良いことだと思います。しかし、聴衆はコミットメー ルを受け取る人だけではなく、ログを読むかもしれない人全員である、とい うことに留意する必要があります。


Node:Changing A Log Message After Commit, Next:, Previous:Log Messages And Commit Emails, Up:Advanced CVS

Changing A Log Message After Commit

後悔するようなログメッセージをコミットしてしまった場合には、CVS では コミットしたあとでそのログを書き直すことができます。これは admin コ マンドの -m オプションででき(このコマンドについてはこの章のもう少し 後で説明します)、一度に1つのログメッセージ(リビジョン毎、ファイル毎) を変更できます。どのように動くか見てみましょう:

floss$ cvs admin -m 1.7:"Truncate four-digit years to two in input." date.c
RCS file: /usr/local/newrepos/someproj/date.c,v
done
floss$

リビジョン1.7とともにコミットされた元の無礼なログメッセージは、完璧 に無垢なメッセージ(退屈にはなりましたが)に置き換えられました。(リビ ジョン番号と新しいログメッセージの間のコロンを忘れないようにして下さ い。)

複数のファイルに良くないメッセージがコミットされてしまった場合、1つ ずつ cvs admin を実行しなければなりません。各ファイルでリビジョン番 号が違うからです。ですから、これは CVS では数少ない、引数にファイル 名を1つだけ渡すコマンドなのです:

floss$ cvs admin -m 1.2:"very boring log message" hello.c README.txt foo.gif
cvs admin: while processing more than one file:
cvs [admin aborted]: attempt to specify a numeric revision
floss$

まぎらわしいことに、ファイル名を渡さなかった場合にも同じエラーになり ます(なぜならそのような場合にはカレントディレクトリ及びそれ以下にあ るすべてのファイルを指定したことになるからです):

floss$ cvs admin -m 1.2:"very boring log message"
cvs admin: while processing more than one file:
cvs [admin aborted]: attempt to specify a numeric revision
floss$

(このように不幸にも CVS のエラーメッセージが出てしまった場合には、 CVS の視点に立ってそのメッセージの意味を読み取らねばなりません。)

admin -m を実行すると、そのプロジェクトの履歴を変えてしま うことになるので注意して使って下さい。ログメッセージが変更されたとい う記録は残りません。ただ、そのリビジョンは最初からその新しいメッセー ジでコミットされたかのように見えるのです。古いログメッセージの痕跡は どこにも残されません(元のコミットメールをセーブしているのでなければ)。

名前からするとこのコマンドは指定された CVS の管理者にしか使えないよ うに見えますが、実際は、そのプロジェクトの書込み権限さえあれば誰でも cvs admin を実行することができます。それでもやはり注意し て使うのが一番良いでしょう。プロジェクトの履歴を変えられるというのは 他の有害なものに匹敵するようなことができてしまいます。 admin については CVS Reference に詳しいです。使用を制限 する方法も書いてあります。


Node:Getting Rid Of A Working Copy, Next:, Previous:Changing A Log Message After Commit, Up:Advanced CVS

Getting Rid Of A Working Copy

CVS の典型的な使い方では、作業コピーのディレクトリを削除するには、他 のディレクトリツリーと同じように削除します:

paste$ rm -rf myproj

しかしこの方法で作業コピーを削除すると、その作業コピーを使い終わった ことが他の開発者にわかりません。CVS は明示的に作業コピーを放棄するコ マンドを用意しています。リリース(release)はチェックアウトの反対だと 思って下さい。その作業コピーでの作業が終わったことをリポジトリに知ら せます。チェックアウトと同様、リリースはツリーの親ディレクトリで起動 します:

paste$ pwd
/home/qsmith/myproj
paste$ cd ..
paste$ ls
myproj
paste$ cvs release myproj
You have [0] altered files in this repository.
Are you sure you want to release directory 'myproj': y
paste$

リポジトリ中に未コミットの変更があった場合リリースは失敗し、変更のあ るファイルの一覧を出力する以外何もしません。そのディレクトリツリーが クリーン(すべて最新)だとするとリリースコマンドはその作業コピーがリリー スされたという記録をリポジトリ中に残します。

リリースコマンドに作業コピーのツリーを自動的に削除させることもできま す。-d フラグを渡して下さい:

paste$ ls
myproj
paste$ cvs release -d myproj
You have [0] altered files in this repository.
Are you sure you want to release (and delete) directory 'myproj: y
paste$ ls
paste$

CVS バージョン 1.10.6 現在、リリースコマンドは作業コピーを調べてリポ ジトリの場所を推定することができません(このコマンドが作業コピーの中 ではなくその上で実行されるものだからです)。-d <REPOS> グ ローバルオプションを渡すか、 CVSROOT 環境変数が正しく設定されている ことを確認するかして下さい。(このバグは将来のバージョンの CVS では解 決されているでしょう)

Cederqvist では、作業コピーを削除する代わりにリリースを使った場合、 リリースされたファイルに監視設定をしている人々には unedit を したかのような通知が行く、と書いてあります。しかしながら筆者が試しに やってみたところ、そうはならないようでした。


Node:History -- A Summary Of Repository Activity, Next:, Previous:Getting Rid Of A Working Copy, Up:Advanced CVS

History - A Summary Of Repository Activity

Repository Administration で、cvs history コマンドについて少し 述べました。このコマンドはそのリポジトリで行われた全ての checkout, commit, update, rtag, release の概略を表示します(少なくとも CVSROOT/history ファイルが作成されてログの記録が始まって以降のものは)。 この概略の形式と内容は、いろいろなオプションによって変えることができ ます。

まずはリポジトリ中でログが記録できるようになっていることを確認します。 リポジトリ管理者は最初に history ファイルが存在することを確かめなく てはなりません:

floss$ cd /usr/local/newrepos/CVSROOT
floss$ ls -l history
ls: history: No such file or directory
floss$

ない場合には次のように作成して下さい:

floss$ touch history
floss$ ls -l history
-rw-r--r--   1 jrandom   cvs           0 Jul 22 14:57 history
floss$

この history ファイルはリポジトリを使用する人全員が書き込めるように なっていなければなりません。そうしないとそのファイルを変更する CVS コマンドを実行するたびにエラーが起きるようになってしまいます。ワール ドライタブルにするのが一番簡単でしょう:

floss$ chmod a+rw history
floss$ ls -l history
-rw-rw-rw-   1 jrandom   cvs           0 Jul 22 14:57 history
floss$

リポジトリが cvs init により作成されたのであれば history ファイルは既に存在しているはずですが、パーミッションの問題は解決する 必要があるかもしれません。

以降の例では、しばらくの間 hisotry ログ記録がされていたと仮定します。 history ファイルにデータが蓄積されるだけの時間がたっています。(?)

cvs history の出力は少々ぶっきらぼうです(これは多分、人間が読むため というよりはプログラムが解釈するためにこうなっているのでしょうけれど、 ちょっと勉強すれば読めます)。とりあえず走らせてみて出力を見てみましょ う:

paste$ pwd
/home/qsmith/myproj
paste$ cvs history -e -a
O 07/25 15:14 +0000 qsmith  myproj =mp=     ~/*
M 07/25 15:16 +0000 qsmith  1.14 hello.c    myproj == ~/mp
U 07/25 15:21 +0000 qsmith  1.14 README.txt myproj == ~/mp
G 07/25 15:21 +0000 qsmith  1.15 hello.c    myproj == ~/mp
A 07/25 15:22 +0000 qsmith  1.1  goodbye.c  myproj == ~/mp
M 07/25 15:23 +0000 qsmith  1.16 hello.c    myproj == ~/mp
M 07/25 15:26 +0000 qsmith  1.17 hello.c    myproj == ~/mp
U 07/25 15:29 +0000 qsmith  1.2  goodbye.c  myproj == ~/mp
G 07/25 15:29 +0000 qsmith  1.18 hello.c    myproj == ~/mp
M 07/25 15:30 +0000 qsmith  1.19 hello.c    myproj == ~/mp
O 07/23 03:45 +0000 jrandom myproj =myproj= ~/src/*
F 07/23 03:48 +0000 jrandom        =myproj= ~/src/*
F 07/23 04:06 +0000 jrandom        =myproj= ~/src/*
M 07/25 15:12 +0000 jrandom 1.13 README.txt myproj == ~/src/myproj
U 07/25 15:17 +0000 jrandom 1.14 hello.c    myproj == ~/src/myproj
M 07/25 15:18 +0000 jrandom 1.14 README.txt myproj == ~/src/myproj
M 07/25 15:18 +0000 jrandom 1.15 hello.c    myproj == ~/src/myproj
U 07/25 15:23 +0000 jrandom 1.1  goodbye.c  myproj == ~/src/myproj
U 07/25 15:23 +0000 jrandom 1.16 hello.c    myproj == ~/src/myproj
U 07/25 15:26 +0000 jrandom 1.1  goodbye.c  myproj == ~/src/myproj
G 07/25 15:26 +0000 jrandom 1.17 hello.c    myproj == ~/src/myproj
M 07/25 15:27 +0000 jrandom 1.18 hello.c    myproj == ~/src/myproj
C 07/25 15:30 +0000 jrandom 1.19 hello.c    myproj == ~/src/myproj
M 07/25 15:31 +0000 jrandom 1.20 hello.c    myproj == ~/src/myproj
M 07/25 16:29 +0000 jrandom 1.3  whatever.c myproj/a-subdir == ~/src/myproj
paste$

There, isn't that clear?

出力を調べる前に、起動時のオプション(-e と -a)に注意して下さい。 history を実行するときには大抵、どのデータをどのように表示するか指定 するオプションをつけると思います。この点においてこのコマンドは他の CVS のコマンドとは違います。他のコマンドは普通、オプション無しでも役 に立ちます。この例の2つのフラグはそれぞれ、everything (起こったイベン トの全種類を表示) と all (全てのユーザについて) という意味です。

history コマンドが他のコマンドと違う点は他にもあって、普通このコマン ドは作業コピー内で実行しますが、コマンド出力はその作業コピーのプロジェ クトの情報に限定されません。リポジトリ内の全てのプロジェクトの全ての イベント履歴を表示します。作業コピーは履歴データをどのリポジトリから 取ってくればよいかを CVS に指定するために使われるだけです。(上の例で はリポジトリの履歴データは myproj プロジェクトのものしかない ので、これで全てなのです)

出力の形式は通常このようです:

CODE DATE USER [REVISION] [FILE] PATH_IN_REPOSITORY ACTUAL_WORKING_COPY_NAME

コード文字は CVS のさまざまなオペレーションを指します Table 6.1 を参 照してください。

オペレーション(チェックアウトのような)は各ファイルではなくプロジェク ト全体についてのもので、リビジョンやファイル名は省略されています。イ コール記号の間にリポジトリ内パスが書いてあります。

history コマンドの出力はコンパクトなように、他のプログラムが解釈し易 いようにデザインされていますが、CVS still gives you a lot of control over its scope and content. Table 6.2 にあるオプションで、どんなタイ プのイベントを報告して欲しいかコントロールします。

Table 6.1  コード文字の意味

Letter	        Meaning
======          =========================================================
O		チェックアウト
T		タグ
F		リリース
W		アップデート(ユーザファイルがない、エントリファイルから削除)
U		アップデート(変更されていないユーザファイルを上書き)
G		アップデート(変更されたユーザファイルにマージ、成功)
C		アップデート(変更されたユーザファイルにマージ、コンフリクト)
M		コミット(ファイル変更)
A		コミット(ファイル追加)
R		コミット(ファイル削除)
E		エクスポート
Table 6.2  イベントタイプでフィルタリングするオプション

Option	        Meaning
==========      =========================================================
-m MODULE	MODULE に起こった履歴イベントを表示
-c		コミットイベントを表示
-o		チェックアウトイベントを表示
-T		タグイベントを表示
-x CODE(S)	CODE タイプのイベントを全て表示(OTFWUGCMARE から1つ以上)
-e		全タイプのイベント表示。以上。レポートして欲しいイベン
		トのタイプを選択したあと、Table 6.3 に示したオプション
		でさらにフィルタリングすることもできます。

Table 6.3  ユーザでフィルタリングするオプション

Option	        Meaning
==========      =========================================================
-a		全ユーザのアクションを表示
-w		この作業コピー内のアクションのみ表示
-l		ユーザが取ったそのアクションの最後の日付を表示
-u USER 	USER の記録を表示


Node:Annotations -- A Detailed View Of Project Activity, Next:, Previous:History -- A Summary Of Repository Activity, Up:Advanced CVS

Annotations - A Detailed View Of Project Activity

The annotate Command

history コマンドがプロジェクトの活動を表示するものだとすると、 annotate コマンドはそれにズームレンズをつけるものだと言えます。 annotate を使うとあるファイルの各行を最後に触った人が誰か、ど のリビジョンで触ったかまでわかるのです:

floss$ cvs annotate
Annotations for README.txt
***************
1.14         (jrandom  25-Jul-99): blah
1.13         (jrandom  25-Jul-99): test 3 for history
1.12         (qsmith   19-Jul-99): test 2
1.11         (qsmith   19-Jul-99): test
1.10         (jrandom  12-Jul-99): blah
1.1          (jrandom  20-Jun-99): Just a test project.
1.4          (jrandom  21-Jun-99): yeah.
1.5          (jrandom  21-Jun-99): nope.
Annotations for hello.c
***************
1.1          (jrandom  20-Jun-99): #include <stdio.h>
1.1          (jrandom  20-Jun-99):
1.1          (jrandom  20-Jun-99): void
1.1          (jrandom  20-Jun-99): main ()
1.1          (jrandom  20-Jun-99): {
1.15         (jrandom  25-Jul-99):   /* another test for history */
1.13         (qsmith   19-Jul-99):   /* random change number two */
1.10         (jrandom  12-Jul-99):   /* test */
1.21         (jrandom  25-Jul-99):   printf ("Hellooo, world!\n");
1.3          (jrandom  21-Jun-99):   printf ("hmmm\n");
1.4          (jrandom  21-Jun-99):   printf ("double hmmm\n");
1.11         (qsmith   18-Jul-99):   /* added this comment */
1.16         (qsmith   25-Jul-99):   /* will merge these changes */
1.18         (jrandom  25-Jul-99):   /* will merge these changes too */
1.2          (jrandom  21-Jun-99):   printf ("Goodbye, world!\n");
1.1          (jrandom  20-Jun-99): }
Annotations for a-subdir/whatever.c
***************
1.3          (jrandom  25-Jul-99): /* A completely non-empty C file. */
Annotations for a-subdir/subsubdir/fish.c
***************
1.2          (jrandom  25-Jul-99): /* An almost completely empty C file. */
Annotations for b-subdir/random.c
***************
1.1          (jrandom  20-Jun-99): /* A completely empty C file. */
floss$

annotate の出力はとても直観的です。左側は、その行が追加、または最後 に変更されたリビジョン番号、開発者、日付です。右側は現在のリビジョン のその行自体です。全ての行に注釈がついているので、そのファイルの内容 全体を見ようと思えば、注釈情報を無視して右側だけ見ればよいのです。

リビジョン番号かタグを指定すれば、そのリビジョンについての注釈が表示 されます。各行についてそのリビジョン以前の最も最近の変更が表示される、 ということです。ある1つのファイルの特定のリビジョンを調べ、どの開発 者がファイル中のどの部分で活動したかはっきりさせる、というのが annotate の最も普通の使い道です。

例えば上の例の出力で、hello.c の最新のリビジョンは 1.21 で、jrandom がこの行:

printf ("Hellooo, world!\n");

に何かした、というのがわかると思います。

なにをしたか調べるにはは、そのリビジョンと一つ前のリビジョンの diff を取ってみるのが1つの方法です:

floss$ cvs diff -r 1.20 -r 1.21 hello.c
Index: hello.c
===================================================================
RCS file: /usr/local/newrepos/myproj/hello.c,v
retrieving revision 1.20
retrieving revision 1.21
diff -r1.20 -r1.21
9c9
<   printf ("Hello, world!\n");
--
>   printf ("Hellooo, world!\n");
floss$

もう一つの方法は、現在の注釈を一つ前のリビジョンの注釈と比較すること です。ファイル全体での全員の活動を見たままで調べられます。

floss$ cvs annotate -r 1.20 hello.c
Annotations for hello.c
***************
1.1          (jrandom  20-Jun-99): #include <stdio.h>
1.1          (jrandom  20-Jun-99):
1.1          (jrandom  20-Jun-99): void
1.1          (jrandom  20-Jun-99): main ()
1.1          (jrandom  20-Jun-99): {
1.15         (jrandom  25-Jul-99):   /* another test for history */
1.13         (qsmith   19-Jul-99):   /* random change number two */
1.10         (jrandom  12-Jul-99):   /* test */
1.1          (jrandom  20-Jun-99):   printf ("Hello, world!\n");
1.3          (jrandom  21-Jun-99):   printf ("hmmm\n");
1.4          (jrandom  21-Jun-99):   printf ("double hmmm\n");
1.11         (qsmith   18-Jul-99):   /* added this comment */
1.16         (qsmith   25-Jul-99):   /* will merge these changes */
1.18         (jrandom  25-Jul-99):   /* will merge these changes too */
1.2          (jrandom  21-Jun-99):   printf ("Goodbye, world!\n");
1.1          (jrandom  20-Jun-99): }
floss$

diff ではテキストの変更がより簡潔に示されますが、annotate では the annotation may be preferable because it places them in their historical context by showing how long the previous incarnation of the line had been present (in this case, all the way since revision 1.1). That knowledge can help you decide whether to look at the logs to find out the motivation for the change:

floss$ cvs log -r 1.21 hello.c
RCS file: /usr/local/newrepos/myproj/hello.c,v
Working file: hello.c
head: 1.21
branch:
locks: strict
access list:
symbolic names:
       random-tag: 1.20
       start: 1.1.1.1
       jrandom: 1.1.1
keyword substitution: kv
total revisions: 22;    selected revisions: 1
description:
----------------------------
revision 1.21
date: 1999/07/25 20:17:42;  author: jrandom;  state: Exp;  lines: +1 -1
say hello with renewed enthusiasm
============================================================================
floss$

-r の他に -D DATE オプションを使って注釈をフィルタリングすることがで きます:

floss$ cvs annotate -D "5 weeks ago" hello.c
Annotations for hello.c
***************
1.1          (jrandom  20-Jun-99): #include <stdio.h>
1.1          (jrandom  20-Jun-99):
1.1          (jrandom  20-Jun-99): void
1.1          (jrandom  20-Jun-99): main ()
1.1          (jrandom  20-Jun-99): {
1.1          (jrandom  20-Jun-99):   printf ("Hello, world!\n");
1.1          (jrandom  20-Jun-99): }
floss$ cvs annotate -D "3 weeks ago" hello.c
Annotations for hello.c
***************
1.1          (jrandom  20-Jun-99): #include <stdio.h>
1.1          (jrandom  20-Jun-99):
1.1          (jrandom  20-Jun-99): void
1.1          (jrandom  20-Jun-99): main ()
1.1          (jrandom  20-Jun-99): {
1.1          (jrandom  20-Jun-99):   printf ("Hello, world!\n");
1.3          (jrandom  21-Jun-99):   printf ("hmmm\n");
1.4          (jrandom  21-Jun-99):   printf ("double hmmm\n");
1.2          (jrandom  21-Jun-99):   printf ("Goodbye, world!\n");
1.1          (jrandom  20-Jun-99): }
floss$


Node:Annotations And Branches, Next:, Previous:Annotations -- A Detailed View Of Project Activity, Up:Advanced CVS

Annotations And Branches

デフォルトでは annotate は開発のメイントランクの動きを表示します。ブ ランチの作業コピーで起動した場合でも指定しない限りはトランクのほうを 表示します。(トランクに固執するのはバグか仕様か…それはあなたの見方 によります。) ブランチタグを -r で渡すとそのブランチの annotate を取 ることができます。以下の例は hello.c が Brancho_Gratuito とい う名前のブランチにある作業コピーからの例です。このブランチ上では少な くとも1つの変更がコミットされました:

floss$ cvs status hello.c
===================================================================
File: hello.c           Status: Up-to-date

  Working revision:    1.10.2.2        Sun Jul 25 21:29:05 1999
  Repository revision: 1.10.2.2        /usr/local/newrepos/myproj/hello.c,v
  Sticky Tag:          Brancho_Gratuito (branch: 1.10.2)
  Sticky Date:         (none)
  Sticky Options:      (none)

floss$ cvs annotate hello.c
Annotations for hello.c
***************
1.1          (jrandom  20-Jun-99): #include <stdio.h>
1.1          (jrandom  20-Jun-99):
1.1          (jrandom  20-Jun-99): void
1.1          (jrandom  20-Jun-99): main ()
1.1          (jrandom  20-Jun-99): {
1.10         (jrandom  12-Jul-99):   /* test */
1.1          (jrandom  20-Jun-99):   printf ("Hello, world!\n");
1.3          (jrandom  21-Jun-99):   printf ("hmmm\n");
1.4          (jrandom  21-Jun-99):   printf ("double hmmm\n");
1.2          (jrandom  21-Jun-99):   printf ("Goodbye, world!\n");
1.1          (jrandom  20-Jun-99): }
floss$ cvs annotate -r Brancho_Gratuito hello.c
Annotations for hello.c
***************
1.1          (jrandom  20-Jun-99): #include <stdio.h>
1.1          (jrandom  20-Jun-99):
1.1          (jrandom  20-Jun-99): void
1.1          (jrandom  20-Jun-99): main ()
1.1          (jrandom  20-Jun-99): {
1.10         (jrandom  12-Jul-99):   /* test */
1.1          (jrandom  20-Jun-99):   printf ("Hello, world!\n");
1.10.2.2     (jrandom  25-Jul-99):   printf ("hmmmmm\n");
1.4          (jrandom  21-Jun-99):   printf ("double hmmm\n");
1.10.2.1     (jrandom  25-Jul-99):   printf ("added this line");
1.2          (jrandom  21-Jun-99):   printf ("Goodbye, world!\n");
1.1          (jrandom  20-Jun-99): }
floss$

ブランチ番号そのものを渡すこともできます:

floss$ cvs annotate -r 1.10.2 hello.c
Annotations for hello.c
***************
1.1          (jrandom  20-Jun-99): #include <stdio.h>
1.1          (jrandom  20-Jun-99):
1.1          (jrandom  20-Jun-99): void
1.1          (jrandom  20-Jun-99): main ()
1.1          (jrandom  20-Jun-99): {
1.10         (jrandom  12-Jul-99):   /* test */
1.1          (jrandom  20-Jun-99):   printf ("Hello, world!\n");
1.10.2.2     (jrandom  25-Jul-99):   printf ("hmmmmm\n");
1.4          (jrandom  21-Jun-99):   printf ("double hmmm\n");
1.10.2.1     (jrandom  25-Jul-99):   printf ("added this line");
1.2          (jrandom  21-Jun-99):   printf ("Goodbye, world!\n");
1.1          (jrandom  20-Jun-99): }
floss$

ブランチ上のリビジョン番号をフルに渡しても良いです:

floss$ cvs annotate -r 1.10.2.1 hello.c
Annotations for hello.c
***************
1.1          (jrandom  20-Jun-99): #include <stdio.h>
1.1          (jrandom  20-Jun-99):
1.1          (jrandom  20-Jun-99): void
1.1          (jrandom  20-Jun-99): main ()
1.1          (jrandom  20-Jun-99): {
1.10         (jrandom  12-Jul-99):   /* test */
1.1          (jrandom  20-Jun-99):   printf ("Hello, world!\n");
1.3          (jrandom  21-Jun-99):   printf ("hmmm\n");
1.4          (jrandom  21-Jun-99):   printf ("double hmmm\n");
1.10.2.1     (jrandom  25-Jul-99):   printf ("added this line");
1.2          (jrandom  21-Jun-99):   printf ("Goodbye, world!\n");
1.1          (jrandom  20-Jun-99): }
floss$

こうする場合には、そのリビジョン番号はそのファイルにだけ有効だという ことを忘れないようにして下さい。一般的にはできるだけブランチ名を使っ たほうが良いと思います。


Node:Using Keyword Expansion, Next:, Previous:Annotations And Branches, Up:Advanced CVS

Using Keyword Expansion

An Overview of CVSkeyword expansion について少し書 いたことを思い出せると思います。RCS キーワードはドル記号でくくった特 殊な単語で、CVS はテキストファイル中にそれらがないか探し、リビジョン 管理情報に展開します。例えばファイルにこういう文字列があった場合
$Author$

そのファイルがあるリビジョンへアップデートされる時に、CVS はこれをそ のリビジョンをコミットした人のユーザ名に展開します:

$Author: jrandom $

CVS は展開後の形も認識しますので、展開された後も適切に更新されつづけ ます。

Although keywords don't actually offer any information that's not available by other means, they give people a convenient way to see revision control facts embedded in the text of the file itself, rather than by invoking some arcane CVS operation.

良く使われるほかのキーワードを紹介します:

$Date$       ==>  最終コミット日時。展開後 ==>
$Date: 1999/07/26 06:39:46 $

$Id$         ==>  ファイル名、リビジョン、日時、著者。展開後 ==>
$Id: hello.c,v 1.11 1999/07/26 06:39:46 jrandom Exp $

$Revision$   ==>  あなたの考えた通りです、展開後 ==>
$Revision: 1.11 $

$Source$     ==> 対応するリポジトリファイルのパス。展開後 ==>
$Source: /usr/local/newrepos/tossproj/hello.c,v $

$Log$        ==>  そのファイルへのログメッセージを蓄積。展開後 ==>
$Log: hello.c,v $
Revision 1.2  1999/07/26 06:47:52  jrandom
...and this is the second log message.

Revision 1.1  1999/07/26 06:39:46  jrandom
This is the first log message...

$Log$ キーワードはこの中では唯一、展開後複数行にわたります。他のもの と違い、以前の展開結果が今回の展開結果で置き換えられるのではなく、最 新の展開結果に挿入されます。キーワードの直後に、空行1行とともに挿入 されます(従って、過去の展開結果は下へ押しやられていきます)。

さらに、行頭と $Log の間にある文字列は展開のプレフィクスになります (ログメッセージをプログラム内コメントにするために利用されます)。例え ばファイルにこう書くと

// $Log$

最初のコミットで次のように展開されるでしょう:

// $Log: hello.c,v $
// Revision 1.14  1999/07/26 07:03:20  jrandom
// this is the first log message...
//

2度目のコミットではこうなります:

// $Log: hello.c,v $
// Revision 1.15  1999/07/26 07:04:40  jrandom
// ...and this is the second log message...
//
// Revision 1.14  1999/07/26 07:03:20  jrandom
// this is the first log message...
//

以降も同じように:

// $Log: hello.c,v $
// Revision 1.16  1999/07/26 07:05:34  jrandom
// ...and this is the third!
//
// Revision 1.15  1999/07/26 07:04:40  jrandom
// ...and this is the second log message...
//
// Revision 1.14  1999/07/26 07:03:20  jrandom
// this is the first log message...
//

いつもいつもログ履歴全部をファイルに保存したりしたくはないとお思いで しょう。その場合、長くなったらいつでも古いほうを削除して下さい。 cvs log を実行するよりも便利ですし、みんながログをコンスタントに読んでい なくてはいけないようなプロジェクトにおいても役に立つでしょう。

ファイル中に $Revision$ を持たせ、それをプログラムのバージョン番号と して使用するのは、良く使われるテクニックです。このやり方はプロジェク トが本質的に1つのファイルで構成されているか、または頻繁にリリースさ れていて、リリースごとの更新が保証されているファイルが少なくとも一つ ある場合にうまくいきます。プログラムのコード中に値として RCS キーワー ドを使うこともできます:

VERSION = "$Revision: 1.114 $";

CVS はキーワードを他の場合と同じようにただ展開します; プログラミング 言語での意味を考慮したりはしませんし、文字列がダブルクオートで保護さ れていると仮定したりもしません。

キーワードの完全な一覧(あまり知られていないようなのがあといくつかあ るんです)は CVS Reference に書いてあります。


Node:Going Out On A Limb (How To Work With Branches And Survive), Next:, Previous:Using Keyword Expansion, Up:Advanced CVS

Going Out On A Limb (How To Work With Branches And Survive)

ブランチは CVS の最も重要な機能であると同時に、最も誤用されやすい機 能でもあります。危険または混乱するような変更を、その変更が安定するま で別の開発ラインに隔離しておけるのは便利です。しかしながら、ブランチ はきちんと管理しなければ、どの変更をいつマージしたかわからなくなり、 プロジェクトを容易に混乱に陥れることになります。


Node:Some Principles For Working With Branches, Next:, Up:Going Out On A Limb (How To Work With Branches And Survive)

Some Principles For Working With Branches

ブランチを使った作業を成功させるためには、開発グループはこれらの原則 を守るべきでしょう:

これらの原則を心に留めつつ、典型的なブランチ開発シナリオを見ていくこ とにしましょう。jrandom がトランク上、qsmith がブランチ上に居ること にします。が、トランク及び/またはブランチ上には複数の開発者が居る可 能性があることに注意して下さい。各開発ライン上には通常何人いても構い ません。しかし、タグづけとマージは各ラインについて一人だけが実行する のがよいでしょう。


Node:Merging Repeatedly Into The Trunk, Next:, Previous:Some Principles For Working With Branches, Up:Going Out On A Limb (How To Work With Branches And Survive)

Merging Repeatedly Into The Trunk

qsmith はトランクを jrandom と共有していて、それを不安定にさせたくな いのでしばらくブランチ上で開発をする必要があるとします。最初のステッ プはブランチの作成です。qsmith がまず通常の(ブランチでない)タグを作 成し、その後ブランチを作っていることに注意してください:

paste$ pwd
/home/qsmith/myproj
paste$ cvs tag Root-of-Exotic_Greetings
cvs tag: Tagging .
T README.txt
T foo.gif
T hello.c
cvs tag: Tagging a-subdir
T a-subdir/whatever.c
cvs tag: Tagging a-subdir/subsubdir
T a-subdir/subsubdir/fish.c
cvs tag: Tagging b-subdir
T b-subdir/random.c
paste$ cvs tag -b Exotic_Greetings-branch
cvs tag: Tagging .
T README.txt
T foo.gif
T hello.c
cvs tag: Tagging a-subdir
T a-subdir/whatever.c
cvs tag: Tagging a-subdir/subsubdir
T a-subdir/subsubdir/fish.c
cvs tag: Tagging b-subdir
T b-subdir/random.c
paste$

最初トランクにタグづけした点は、将来、ブランチを作成した時点のトラン クにアクセスするときに必要になるでしょう。そういう必要がでてきた場合 に、ブランチ自体を参照することなくトランクのスナップショットを参照す ることができます。ブランチタグでは、ブランチの根のあるトランクではな く、ブランチにアクセスしてしまうので使えません。ブランチが生えている その同じリビジョンに、通常のタグを作るしか方法はありません。(「ブラ ンチの原則4、ブランチポイントにはノン-ブランチタグを作成する」とでも 言うべきこのルールを忠実に守っている人も居ますが、多くのサイトではこ れが守られていませんし、まあかまわないようにも思えます。趣味の問題で すね) これ以降ではこのようなノン-ブランチタグをブランチポイント タグと呼びます。

ブランチポイントタグは Root-of- で始まり、ハイフンではなくア ンダスコアで単語を区切った実際のブランチ名を続ける、という命名規則に も注意してください。実際のブランチを作成する時には、そのタグ名の最後 は -branch とします。タグ名を見ればそのタグがブランチタグだと わかるようにするためです。(ブランチポイントタグ Root-of-Exotic_Greetings には -branch とは書いてありません、 これはブランチタグではありませんので。) もちろん、この命名規則を使う 必要は特にありませんが、何らかの規則を用いるべきだと思います。

ああ、ちょっとうるさく言いすぎましたね。小さなプロジェクトでは、誰が 何をしているかをみんながが知っていて、混乱が起こっても容易に回復でき るので、このような規則を使わねばならないわけではありません。ブランチ ポイントタグを使うとかタグに厳しい命名規則を課すかどうかというのは、 プロジェクトの複雑度やブランチのやりかたによります。(あとでいつでも 戻って古いタグを新しい規則に沿うように直すことができることを忘れない でください。古いタグのバージョンにアクセスして、新しいタグをつけ、古 いタグを削除すればよいのです)

さて、qsmith はブランチで作業を開始します:

paste$ cvs update -r Exotic_Greetings-branch
cvs update: Updating .
cvs update: Updating a-subdir
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir
paste$

ファイルをいくつか変更し、それをブランチへコミットします:

paste$ emacs README.txt a-subdir/whatever.c b-subdir/random.c
...
paste$ cvs ci -m "print greeting backwards, etc"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in README.txt;
/usr/local/newrepos/myproj/README.txt,v  <--  README.txt
new revision: 1.14.2.1; previous revision: 1.14
done
Checking in a-subdir/whatever.c;
/usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
new revision: 1.3.2.1; previous revision: 1.3
done
Checking in b-subdir/random.c;
/usr/local/newrepos/myproj/b-subdir/random.c,v  <--  random.c
new revision: 1.1.1.1.2.1; previous revision: 1.1.1.1
done
paste$
<<<<<<< j-chapter-6.texi =======

この間、jrandom はトランクでの作業を続行しています。qsmith が触った 3つのファイルのうち2つを変更しました。 >>>>>>> 1.4

<<<<<<< j-chapter-6.texi Meanwhile, jrandom is continuing to work on the trunk. She modifies two of the three files that qsmith touched. Just for kicks, we'll have her make changes that conflict with qsmith's work:

======= 一方、 jrandom はトランク上で作業を続行しています。qsmith が触った3 つのファイルのうち、2つを変更しました。試しに、qsmith とコンフリクト するような変更を施してみましょう:

>>>>>>> 1.4

floss$ emacs README.txt whatever.c
 ...
floss$ cvs ci -m "some very stable changes indeed"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in README.txt;
/usr/local/newrepos/myproj/README.txt,v  <--  README.txt
new revision: 1.15; previous revision: 1.14
done
Checking in a-subdir/whatever.c;
/usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
new revision: 1.4; previous revision: 1.3
done
floss$

コンフリクトがあるようには見えません。当たり前ですね、ブランチとトラン クをマージしようとしたわけではありませんから。ではここで jrandom が マージを実行します:

floss$ cvs update -j Exotic_Greetings-branch
cvs update: Updating .
RCS file: /usr/local/newrepos/myproj/README.txt,v
retrieving revision 1.14
retrieving revision 1.14.2.1
Merging differences between 1.14 and 1.14.2.1 into README.txt
rcsmerge: warning: conflicts during merge
cvs update: Updating a-subdir
RCS file: /usr/local/newrepos/myproj/a-subdir/whatever.c,v
retrieving revision 1.3
retrieving revision 1.3.2.1
Merging differences between 1.3 and 1.3.2.1 into whatever.c
rcsmerge: warning: conflicts during merge
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir
RCS file: /usr/local/newrepos/myproj/b-subdir/random.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
Merging differences between 1.1.1.1 and 1.1.1.1.2.1 into random.c
floss$ cvs update
cvs update: Updating .
C README.txt
cvs update: Updating a-subdir
C a-subdir/whatever.c
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir
M b-subdir/random.c
floss$

ファイルが2つ、コンフリクトを起こしました。たいした問題ではありません、 jrandom はいつものように機転を利かせてコンフリクトを解消し、コミット した後、トランクにマージ成功のタグをつけます:

floss$ emacs README.txt a-subdir/whatever.c
 ...
floss$ cvs ci -m "merged from Exotic_Greetings-branch (conflicts resolved)"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in README.txt;
/usr/local/newrepos/myproj/README.txt,v  <--  README.txt
new revision: 1.16; previous revision: 1.15
done
Checking in a-subdir/whatever.c;
/usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
new revision: 1.5; previous revision: 1.4
done
Checking in b-subdir/random.c;
/usr/local/newrepos/myproj/b-subdir/random.c,v  <--  random.c
new revision: 1.2; previous revision: 1.1
done
floss$ cvs tag merged-Exotic_Greetings
cvs tag: Tagging .
T README.txt
T foo.gif
T hello.c
cvs tag: Tagging a-subdir
T a-subdir/whatever.c
cvs tag: Tagging a-subdir/subsubdir
T a-subdir/subsubdir/fish.c
cvs tag: Tagging b-subdir
T b-subdir/random.c
floss$

qsmith は開発を続行するのにマージが終わるのを待つ必要はありません。 qsmith は jrandom がマージした後の一連の変更に対し、タグをつけておけ ばよいのです。(あとで jrandom はこのタグ名を知る必要があります; 一般 に、ブランチというものは開発者が頻繁かつ綿密に連絡を取り合って初めて 成り立つものなのです):

paste$ cvs tag Exotic_Greetings-1
cvs tag: Tagging .
T README.txt
T foo.gif
T hello.c
cvs tag: Tagging a-subdir
T a-subdir/whatever.c
cvs tag: Tagging a-subdir/subsubdir
T a-subdir/subsubdir/fish.c
cvs tag: Tagging b-subdir
T b-subdir/random.c
paste$ emacs a-subdir/whatever.c
 ...
paste$ cvs ci -m "print a randomly capitalized greeting"
cvs commit: Examining .
cvs commit: Examining a-subdir
cvs commit: Examining a-subdir/subsubdir
cvs commit: Examining b-subdir
Checking in a-subdir/whatever.c;
/usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
new revision: 1.3.2.2; previous revision: 1.3.2.1
done
paste$

もちろん、qsmith は変更が終わり次第タグをつけるべきです:

paste$ cvs -q tag Exotic_Greetings-2
T README.txt
T foo.gif
T hello.c
T a-subdir/whatever.c
T a-subdir/subsubdir/fish.c
T b-subdir/random.c
paste$

これをやっている間、qsmith が一連の編集で触ったのとは別のファイルを jrandom が変更したとします:

floss$ emacs README.txt
 ...
floss$ cvs ci -m "Mention new Exotic Greeting features" README.txt
Checking in README.txt;
/usr/local/newrepos/myproj/README.txt,v  <--  README.txt
new revision: 1.17; previous revision: 1.16
done
floss$

ここで qsmith は新たな変更をブランチにコミットし、jrandom は別のファ イルのコンフリクトしない変更をトランクにコミットします。jrandom がブ ランチをもう一度マージしようとしたときに何が起こるか見てみましょう:

floss$ cvs -q update -j Exotic_Greetings-branch
RCS file: /usr/local/newrepos/myproj/README.txt,v
retrieving revision 1.14
retrieving revision 1.14.2.1
Merging differences between 1.14 and 1.14.2.1 into README.txt
rcsmerge: warning: conflicts during merge
RCS file: /usr/local/newrepos/myproj/a-subdir/whatever.c,v
retrieving revision 1.3
retrieving revision 1.3.2.2
Merging differences between 1.3 and 1.3.2.2 into whatever.c
rcsmerge: warning: conflicts during merge
RCS file: /usr/local/newrepos/myproj/b-subdir/random.c,v
retrieving revision 1.1
retrieving revision 1.1.1.1.2.1
Merging differences between 1.1 and 1.1.1.1.2.1 into random.c
floss$ cvs -q update
C README.txt
C a-subdir/whatever.c
floss$

コンフリクトが起きてしまいました! これ、予測していましたか?

マージの意味に問題があるようです。先に An Overview of CVS で述 べた通り、作業コピー中で

floss$ cvs update -j BRANCH

を実行すると、CVS はブランチの根と先端の相違を作業コピーにマージする のです。今回の場合、それらの変更の大半は、jrandom が最初に行ったマー ジの時に既にトランクに組み込まれているので、この動作は問題になります。 CVS がその変更を再びマージしようとすると(その変更自身に上書きするよ うなことになります)、当然コンフリクトだと見なされるわけです。

jrandom が本当にやりたかったことは、ブランチのうち、一番最後に実行し たマージ時点と現在のブランチ先端の間の変更を作業コピーにマージする、 ということだったのです。これをするには update に -j フラグを2つ渡せ ばよいのです。An Overview of CVS を思い出して下さい。これをす るには、各フラグでどのリビジョンを指定すれば良いかを知っておかなけれ ばなりません。幸い、qsmith は最後のマージポイントにタグをつけておき ましたので(hurrah for planning ahead!)、これについて問題はありません。 まずは jrandom の作業コピーを元のきれいな状態に戻しましょう。そこか ら再マージするのです:

floss$ rm README.txt a-subdir/whatever.c
floss$ cvs -q update
cvs update: warning: README.txt was lost
U README.txt
cvs update: warning: a-subdir/whatever.c was lost
U a-subdir/whatever.c
floss$

さて、これでマージの準備ができました、今回は qsmith がつけてくれたタ グを使うことにしましょう:

floss$ cvs -q update -j Exotic_Greetings-1 -j Exotic_Greetings-branch
RCS file: /usr/local/newrepos/myproj/a-subdir/whatever.c,v
retrieving revision 1.3.2.1
retrieving revision 1.3.2.2
Merging differences between 1.3.2.1 and 1.3.2.2 into whatever.c
floss$ cvs -q update
M a-subdir/whatever.c
floss$

いいカンジです。qsmith の変更が whatever.c に組み込まれました; jrandom はコミットし、タグをつけます:

floss$ cvs -q ci -m "merged again from Exotic_Greetings (1)"
Checking in a-subdir/whatever.c;
/usr/local/newrepos/myproj/a-subdir/whatever.c,v  <--  whatever.c
new revision: 1.6; previous revision: 1.5
done
floss$ cvs -q tag merged-Exotic_Greetings-1
T README.txt
T foo.gif
T hello.c
T a-subdir/whatever.c
T a-subdir/subsubdir/fish.c
T b-subdir/random.c
floss$

qsmith がマージポイントにタグをつけるのを忘れても、望みがなくなって しまったわけではありません。 jrandom が qsmith の最初の変更を大体い つ頃コミットしたか覚えていれば、日付でフィルタリングしてみることがで きます:

floss$ cvs update -j Exotic_Greetings-branch:3pm -j Exotic_Greetings_branch

日付でフィルタリングするのは、最後の切り札にはなりますが、あまり理想 的な方法とは言えません。なぜなら、信頼に足る開発上の名称ではなく、人 の記憶に頼った方法だからです。qsmith の初回マージ分の変更が、1回のコ ミットではなく何度かに分けてコミットされていた場合、jrandom が間違っ て日付や時刻を指定すると、変更全てではなく一部分だけを取ってきてしま う可能性があります。

qsmith の変更のタグづけできる各ポイントが、1回のコミットでリポジトリ に送られなければならない理由はありません。この例でたまたまこうなって いるだけなのです。実際には、qsmith はタグとタグの間に何度かコミット するかもしれません。qsmith はブランチ上でひとりで作業したければ、そ うすることができます。タグというのはつまり、トランクへマージできると 思う点をブランチ上に連続的に記録していくことなのです。jrandom が常に -j フラグを2つ使ってマージし、qsmith のマージタグを正しい順序で注意深 く用いてそれぞれを1度だけ使う限り、トランクでダブルマージの問題が起 こることはないでしょう。コンフリクトは起こるかもしれませんが、それは 人の手で解決しなければならない、不可避なものなのでしょう。同じ領域に おいて、ブランチでもトランクでも変更があるような状況だというわけです。


Node:The Dovetail Approach -- Merging In And Out Of The Trunk, Next:, Previous:Merging Repeatedly Into The Trunk, Up:Going Out On A Limb (How To Work With Branches And Survive)

The Dovetail Approach - Merging In And Out Of The Trunk

ブランチからトランクへ何度もマージするというのは、トランク上に居る人々 にとっては良いことです。なぜなら、自分たちの変更も、ブランチからの変 更も全てわかるからです。しかしながら、ブランチ上の開発者はトランク上 での作業分を組み込めないのです。

これをするためには、ブランチ開発者は時々余分なステップを踏む必要があ ります。(最新のトランクの変更をマージしたいと思ったり、避けられない コンフリクトをどうにかしたいと思った時はいつでも、という意味です):

paste$ cvs update -j HEAD

特別に予約されたタグ HEAD は、トランクの先端を意味しています。こ のコマンドは、現在のブランチの根(Exotic_Greetings-branch)から、ト ランク上の現在の最大のリビジョンまでの変更をすべてマージします。もちろん、 qsmithはこれを実行したあと再度タグをつけなければなりません。トランク上の 開発者が qsmith の変更をマージしようとしたときに、誤って自分自身の変更を マージしてしまうことを避けられるようにするためです。

ブランチ開発者も同様に、最後にマージしてから現在状態までのトランクの変更 を正確にブランチにマージするために、トランクのマージタグを境界として使用 することができます(トランクがマージするのと同じ方法で)。例えば、jrandom がブランチからマージした後 hello.c に変更を施したとします:

floss$ emacs hello.c
 ...
floss$ cvs ci -m "clarify algorithm" hello.c
Checking in hello.c;
/usr/local/newrepos/myproj/hello.c,v  <--  hello.c
new revision: 1.22; previous revision: 1.21
done
floss$

その後、qsmith がこれらの変更をブランチにマージし、コミットし、タグをつ けます:

paste$ cvs -q update -j merged-Exotic_Greetings-1 -j HEAD
RCS file: /usr/local/newrepos/myproj/hello.c,v
retrieving revision 1.21
retrieving revision 1.22
Merging differences between 1.21 and 1.22 into hello.c
paste$ cvs -q update
M hello.c
paste$ cvs -q ci -m "merged trunk, from merged-Exotic_Greetings-1 to HEAD"
Checking in hello.c;
/usr/local/newrepos/myproj/hello.c,v  <--  hello.c
new revision: 1.21.2.1; previous revision: 1.21
done
paste$ cvs -q tag merged-merged-Exotic_Greetings-1
T README.txt
T foo.gif
T hello.c
T a-subdir/whatever.c
T a-subdir/subsubdir/fish.c
T b-subdir/random.c
paste$

jrandom は hello.c の変更をコミットした後にタグづけせず、qsmith がそれを したということに注意してください。ここでの原則では、ちょっとした変更のあ とにいちいちタグをつける必要はなくて、マージの後、及び自分の開発ラインの マージできる状態をコミットした後に常にタグをつけるべきなのです。こうすれ ば、他の人(別のブランチにいるのでしょう)に自分自身のマージの基礎となる参 照の点がわかります。


Node:The Flying Fish Approach -- A Simpler Way To Do It, Next:, Previous:The Dovetail Approach -- Merging In And Out Of The Trunk, Up:Going Out On A Limb (How To Work With Branches And Survive)

The Flying Fish Approach - A Simpler Way To Do It

albeit slightly limiting ちょっと制限してはいますが

前述のものよりも少々制限はありますが、より簡単な方法があります。ブラン チ開発者はトランクがマージしている間開発を停止し、トランク開発者は今 までのブランチを置き換えるような全く新しいブランチを作成します。ブラン チ開発者はその新しいブランチに移り、開発を続けます。そのサイクルはブ ランチ開発が必要なくなるまで続きます。こんな感じになります(要約です。 今まで通り、jrandom@floss がトランクに、 qsmith@paste がブランチに 居ることにします):

floss$ cvs tag -b BRANCH-1
paste$ cvs checkout -r BRANCH-1 myproj

トランク、ブランチともに作業を始めます。そして開発者たちは協議し、ブ ランチをトランクにマージする時であると決定します:

paste$ cvs ci -m "committing all uncommitted changes"
floss$ cvs update -j BRANCH-1

ブランチでの変更が全てマージされます: ブランチ開発者はトランク開発者 がコンフリクトを解消し、コミットしてタグをつけ、新しいブランチを作成 するまで作業を停止します:

floss$ cvs ci -m "merged from BRANCH-1"
floss$ cvs tag merged-from-BRANCH-1
floss$ cvs tag -b BRANCH-2

ここでブランチ開発者は自分の作業コピーを新しいブランチで上書きします; マージが起こった時には最新で、新しいブランチは古いブランチの変更が組 み込まれたトランクからのものですから、そうしても未コミットの変更を失っ たりしないのです。

paste$ cvs update -r BRANCH-2

BRANCH-1 を BRANCH-2 に、BRANCH-2 を BRANCH-3 に置き換えて、 限りなくこのサイクルは続いていきます。

筆者はこれを トビウオ テクニックと呼んでいます。ブランチがたえ ずトランクから生え、短く伸びてはまたトランクへ再接合するからです。こ のやりかたの利点は、シンプルであること(トランクは常に指定されたブラン チの全ての変更をマージする)、それとブランチ開発者はコンフリクトを解 消しなくてもいいということです(新しくてきれいなブランチをただ渡され て、その上で作業すればいいのです)。欠点は当然、トランクのマージを実 施している間、ブランチ開発者はただ座ってボケっとしていなければならな いということです(解決すべきコンフリクトがいくつあるかによって、それ なりの時間がかかるでしょう)。あまり重要ではないですが、もうひとつ欠 点があります。それは、使われないノンブランチタグの代わりに、使われて いない小さなブランチがたくさんできることです。しかし、小さくて使われ ていないブランチがたくさんあっても気にならなくて、トラブルのないマー ジを期待しているのなら、トビウオは一番簡単なやり方になるでしょう。心 に留めておいてもいいと思います。

どちらのやり方を取るにしろ、分離状態は出来るだけ短い間にするよう努力 したほうが良いでしょう。ブランチとトランクをずっとマージしないままに すると、ただのテキストのズレだけでは済まず、意味上のズレに悩まされる ことになります。テキスト上のコンフリクトを起こす変更は簡単に解消でき ますが、テキストではなく考え方においてコンフリクトを起こすような変更 は、見つけるのも解決するのも大変です。ブランチを隔離するというのは、 開発者を自由にしますが、他の人の変更からしばらくの間でも遮蔽されると いうのは危険なことです。ブランチを使う場合は普段よりも活発にコミュニ ケーションをとるようになります。全員がお互いの計画を検討し、同じ軌道 に乗っているかどうか確認する必要があります。


Node:Branches And Keyword Expansion -- Natural Enemies, Previous:The Flying Fish Approach -- A Simpler Way To Do It, Up:Going Out On A Limb (How To Work With Branches And Survive)

Branches And Keyword Expansion - Natural Enemies

ファイルに RCS キーワードがあると、ブランチ上とトランク上では違った ように展開されるので、マージするたびににせのコンフリクトが起こること になります。何も変更していなくてもキーワードはオーバラップし、展開結 果は一致しません。例えば README.txt に、トランクではこのように書いて あるとします

$Revision: 1.14 $

ブランチ上ではこうです

$Revision: 1.14.2.1 $

マージを実行した時に、以下のようなコンフリクトが起こるでしょう:

floss$ cvs update -j Exotic_Greetings-branch
RCS file: /usr/local/newrepos/myproj/README.txt,v
retrieving revision 1.14
retrieving revision 1.14.2.1
Merging differences between 1.14 and 1.14.2.1 into README.txt
rcsmerge: warning: conflicts during merge
floss$ cat README.txt
 ...
<<<<<<< README.txt
key $Revision: 1.14 $
=======
key $Revision: 1.14.2.1 $
>>>>>>> 1.14.2.1
 ...
floss$

こうならないようにするために、マージ時に一時的に -kk オプション(何の 略なのか知らないんですが、多分 "kill keywords" かな?)を渡して展開を 抑制することができます:

floss$ cvs update -kk -j Exotic_Greetings-branch
RCS file: /usr/local/newrepos/myproj/README.txt,v
retrieving revision 1.14
retrieving revision 1.14.2.1
Merging differences between 1.14 and 1.14.2.1 into README.txt
floss$ cat README.txt
 ...
$Revision$
 ...
floss$

しかし、気をつけることがひとつだけあります: -kk を使うと、そのファイ ルに設定してあるほかのキーワード展開モードを上書きしてしまいます。特 にバイナリファイルにおいては問題になります。バイナリファイルは普通 -kb になっているからです(-kb はキーワード展開と行末コード変換を全て 抑制します)。ブランチからバイナリファイルをマージする時には -kk を使 わないようにして、コンフリクトは手で直して下さい。


Node:Tracking Third-Party Sources (Vendor Branches), Next:, Previous:Going Out On A Limb (How To Work With Branches And Survive), Up:Advanced CVS

Tracking Third-Party Sources (Vendor Branches)

外部の配布元から取ってきたソフトウェアに、あるサイトでローカルな変更 が加えられることがあります。そのローカルな変更を配布元が受け入れない 場合(そうできないについて正当な理由はいくらでも考えられます)、そのサ イトはそのソフトウェアのアップグレードを受け取るたびにその変更をメン テせねばなりません。

CVS の ベンダブランチ(vendor branches) という機能がこの仕事に 役立ちます。ベンダブランチというのは cvs import のわけのわからない最 後の2つの引数の説明でした(今まではね)。An Overview of CVS でこ じつけたベンダタグとリリースタグのことです。

ここで、それがどのように動くか示します。最初のインポートは他の CVS プロジェクトの最初のインポートと同様です(ちょっと注意してベンダタグ とリリースタグを決めないといけませんが):

floss$ pwd
/home/jrandom/theirproj-1.0
floss$ cvs import -m "Import of TheirProj 1.0" theirproj Them THEIRPROJ_1_0
N theirproj/INSTALL
N theirproj/README
N theirproj/src/main.c
N theirproj/src/parse.c
N theirproj/src/digest.c
N theirproj/doc/random.c
N theirproj/doc/manual.txt

No conflicts created by this import

floss$

そしてどこかに作業コピーをチェックアウトして、ローカルな変更を施し、 その後コミットして下さい:

floss$ cvs -q co theirproj
U theirproj/INSTALL
U theirproj/README
U theirproj/doc/manual.txt
U theirproj/doc/random.c
U theirproj/src/digest.c
U theirproj/src/main.c
U theirproj/src/parse.c
floss$ cd theirproj
floss$ emacs src/main.c src/digest.c
 ...
floss$ cvs -q update
M src/digest.c
M src/main.c
floss$ cvs -q ci -m "changed digestion algorithm; added comment to main"
Checking in src/digest.c;
/usr/local/newrepos/theirproj/src/digest.c,v  <--  digest.c
new revision: 1.2; previous revision: 1.1
done
Checking in src/main.c;
/usr/local/newrepos/theirproj/src/main.c,v  <--  main.c
new revision: 1.2; previous revision: 1.1
done
floss$

1年後、そのソフトウェアの次のバージョンが Them, Inc. から届きます。 ローカルの変更をそのバージョンにも取り入れなければなりません。配布元 での変更とローカルの変更は、少しだけ重なっているところがあります。配 布元では新しいファイルを一つ追加し、あなたの触ってないファイル2つを 変更し、あなたが触ったファイル2つにも変更を加えています。

まずやることは、もう1度インポートすることです。今回は新しいソースか らインポートします。初回インポートとほとんど同じです。リポジトリ中、 以前と同じプロジェクトの、同じベンダブランチにインポートします。違う のはリリースタグだけです:

floss$ pwd
/home/jrandom/theirproj-2.0
floss$ cvs -q import -m "Import of TheirProj 2.0" theirproj Them THEIRPROJ_2_0
U theirproj/INSTALL
N theirproj/TODO
U theirproj/README
cvs import: Importing /usr/local/newrepos/theirproj/src
C theirproj/src/main.c
U theirproj/src/parse.c
C theirproj/src/digest.c
cvs import: Importing /usr/local/newrepos/theirproj/doc
U theirproj/doc/random.c
U theirproj/doc/manual.txt

2 conflicts created by this import.
Use the following command to help the merge:

       cvs checkout -jThem:yesterday -jThem theirproj

floss$

おやまあ、CVS がこんな風に助けようとしてくれたのは今まで見たことがあ りませんね。変更をマージするためにどんなコマンドを実行するか教えてく れています。それに、ほとんど正しいですよ! 実際、示されたコマンドは動 きますが(yesterday のところを、最初のインポートを含んでいるが2番目の インポートを含んでいない時間間隔に調整したとして)、著者は代わりにリ リースタグを使うほうが好きです:

floss$ cvs checkout -j THEIRPROJ_1_0 -j THEIRPROJ_2_0 theirproj
cvs checkout: Updating theirproj
U theirproj/INSTALL
U theirproj/README
U theirproj/TODO
cvs checkout: Updating theirproj/doc
U theirproj/doc/manual.txt
U theirproj/doc/random.c
cvs checkout: Updating theirproj/src
U theirproj/src/digest.c
RCS file: /usr/local/newrepos/theirproj/src/digest.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.2
Merging differences between 1.1.1.1 and 1.1.1.2 into digest.c
rcsmerge: warning: conflicts during merge
U theirproj/src/main.c
RCS file: /usr/local/newrepos/theirproj/src/main.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.2
Merging differences between 1.1.1.1 and 1.1.1.2 into main.c
U theirproj/src/parse.c
floss$

インポートでコンフリクトが2つあるというふうに表示されたのに、マージ ではコンフリクトが1つしかないことに注意して下さい。インポートとその 他ではコンフリクトが少し違うものだと CVS が思っているように見えます。 インポートでは基本的に、前回のインポートと今回のインポートの間に、あ なたとベンダ両方が変更したファイルをコンフリクトとして報告します。し かしマージの時点になると、「コンフリクト」の通常の定義に忠実なのです。 つまり、変更が重なっている、という意味になります。重なり合っていない 変更は通常の方法でマージされ、そのファイルはただの変更として示されま す。

ちょっと diff を取ってみると、ファイルのうち1つだけにコンフリクトマー カがあることがわかります:

floss$ cvs -q update
C src/digest.c
M src/main.c
floss$ cvs diff -c
Index: src/digest.c
===================================================================
RCS file: /usr/local/newrepos/theirproj/src/digest.c,v
retrieving revision 1.2
diff -c -r1.2 digest.c
*** src/digest.c        1999/07/26 08:02:18     1.2
-- src/digest.c        1999/07/26 08:16:15
***************
*** 3,7 ****
-- 3,11 ----
 void
 digest ()
 {
+ <<<<<<< digest.c
   printf ("gurgle, slorp\n");
+ =======
+   printf ("mild gurgle\n");
+ >>>>>>> 1.1.1.2
 }
Index: src/main.c
===================================================================
RCS file: /usr/local/newrepos/theirproj/src/main.c,v
retrieving revision 1.2
diff -c -r1.2 main.c
*** src/main.c  1999/07/26 08:02:18     1.2
-- src/main.c  1999/07/26 08:16:15
***************
*** 7,9 ****
-- 7,11 ----
 {
   printf ("Goodbye, world!\n");
 }
+
+ /* I, the vendor, added this comment for no good reason. */
floss$

こうなると他のマージと同じように、ただコンフリクトを解消すればいいだ けの話になります:

floss$ emacs  src/digest.c  src/main.c
 ...
floss$ cvs -q update
M src/digest.c
M src/main.c
floss$ cvs diff src/digest.c
cvs diff src/digest.c
Index: src/digest.c
===================================================================
RCS file: /usr/local/newrepos/theirproj/src/digest.c,v
retrieving revision 1.2
diff -r1.2 digest.c
6c6
<   printf ("gurgle, slorp\n");
--
>   printf ("mild gurgle, slorp\n");
floss$

そして変更をコミットします

floss$ cvs -q ci -m "Resolved conflicts with import of 2.0"
Checking in src/digest.c;
/usr/local/newrepos/theirproj/src/digest.c,v  <--  digest.c
new revision: 1.3; previous revision: 1.2
done
Checking in src/main.c;
/usr/local/newrepos/theirproj/src/main.c,v  <--  main.c
new revision: 1.3; previous revision: 1.2
done
floss$

あとはベンダからの次のリリースを待つだけです。(もちろん、ローカルの 変更がちゃんと動くかどうかテストしたいだろうと思いますけれど!)

-------------------------------------------------------------


Node:Exporting For Public Distribution, Next:, Previous:Tracking Third-Party Sources (Vendor Branches), Up:Advanced CVS

Exporting For Public Distribution

CVS は開発者にとっては良い配布機構なのですが、大部分のユーザがソフト ウェアを取ってくる時にはダウンロードできるパッケージを取ってくると思 います。このパッケージは一般には CVS の作業コピーではなくただのソー スツリーで、ユーザのシステムに合わせて簡単にコンフィギュレーション・ コンパイルができるようになっているものです。

しかし、CVS はパッケージを作るのに便利な機構を提供しています。 cvs export コマンドです。プロジェクトをエクスポート するというのは、プロジェクトの作業コピーをチェックアウトするのと同じ ようなものですが、CVS 管理サブディレクトリを抜きにして プロジェクトツリーをチェックアウトするというところが違います。これは つまり、作業コピーではなくソースツリーだけを取得したということになり ます。そのソースツリーはどこから来たものなのか、それらのファイルがど の CVS バージョンなのかわかりません。つまり、エクスポートされたコピー はダウンロードしてディストリビューションをほどいたときの状態と同じな のです。そのプロジェクトが作業コピーから直接コンパイルできるようになっ ているとすると(そしてそうなっているべきなのです!)、エクスポートされ たコピーもコンパイルできるはずです。

export コマンドは checkout と同じように動作しますが、 タグ名か日付を指定しなければいけないというところが違います。プロジェ クトにリリース名でタグをつけて、それに基づいてエクスポートしてみましょ う:

floss$ pwd
/home/jrandom/myproj
floss$ cvs -q tag R_1_0
T README.txt
T hello.c
T a-subdir/whatever.c
T a-subdir/subsubdir/fish.c
T b-subdir/random.c
floss$ cd ..
floss$ cvs -d /usr/local/newrepos -q export -r R_1_0 -d myproj-1.0 myproj
U myproj-1.0/README.txt
U myproj-1.0/hello.c
U myproj-1.0/a-subdir/whatever.c
U myproj-1.0/a-subdir/subsubdir/fish.c
U myproj-1.0/b-subdir/random.c
floss$ cd myproj-1.0
floss$ ls
README.txt  a-subdir  b-subdir  hello.c

export コマンドは作業コピーの中ではないところで起動されるので、 どのリポジトリを使うかを -d グローバルオプションで指定してや らなければならないことに注意して下さい。またこの例では、ディレクトリ 名をデフォルトのプロジェクト名ではなく、明示的に指定して (myproj-1.0)エクスポートしています。その名前で作業コピーが既 に存在しているからです。この状況は珍しいことではないと思います。

上の例のようにエクスポートコピーが作成されたあとは、プロジェクトが単 純なものの場合、次のようにすればリリースが完了します:

floss$ tar cf myproj-1.0.tar myproj-1.0
floss$ gzip --best myproj-1.0.tar
floss$ ls
myproj/   myproj-1.0/   myproj-1.0.tar.gz
floss$ rm -rf myproj-1.0
floss$ mv myproj-1.0.tar.gz /home/ftp/pub/myproj/

もちろん、これらのコマンドを全部手で走らせたりすることはめったにあり ません。cvs export はリリースやパッケージの処理をするスク リプトから呼ばれることが多いでしょう。公のリリースに先立っていくつか の「テスト」リリースをするような場合は、リリースパッケージを作成する 処理は高度に自動化されていることが望ましいと思います。


Node:The Humble Guru, Previous:Exporting For Public Distribution, Up:Advanced CVS

The Humble Guru

この章を全て読んで理解したら(そして実際やってみるとなお良い)、CVS に ついてとても驚くようなことはもうなにも残っていないと自信を持ってよい でしょう。少なくとも、誰かが新しい機能を CVS に追加するまでは。CVS を大きなプロジェクトで使うために知る必要のあることはすべて述べました。

うぬぼれる前に、4章で言ったことを繰り返しますが info-cvs@gnu.org メーリングリストを購読することをお勧めし ます。インターネットのメーリングリストの大部分は S/N 比が低下している にもかかわらず、届く情報のほとんどは待つに値するものです。筆者がこの 章を書いている間ずっと購読していたのですが(実はそれより前の章でもそ うだったんですが)、CVS の動作の細部の重要なところについて、他の人の 投稿から学んだことがどんなにたくさんあるか知ったら、きっと驚くと思い ます。CVS を本気で使う気があるのなら、また、開発者グループの CVS 管 理者の人は特に、他の真面目なユーザの人たちと知識を共有することによっ て恩恵をたくさん受けることが出来ると思います。


Node:Tips And Troubleshooting, Next:, Previous:Advanced CVS, Up:Top

Tips And Troubleshooting

以前の章でわたしは、CVS は「ブラックボックス」ソフトウェアではないと いいました。ブラックボックスは中身を覗かせてくれません。内部にアクセ スさせてくれないので、直したり(壊したり)はできません。ブラックボック スというのは普通、何も直す必要がないという前提に立っています。たいが いはちゃんと動いているので、ユーザは内部にアクセスする必要はありません。 しかし、動かないとなると全く動きません。直すための選択肢はそう多くな いので、どんな問題でも showstopper なのです。

CVS はというと、それはもう完璧に透明な箱のようです。箱がないことを除 けば。部品は密閉されたりせず、環境に直接さらされており、環境の一部 (予期しないファイルのパーミッションや、中断されたコマンド、競合する プロセス、などなど)はその機構に介入してダメにしてしまうこともありま す。しかし、たとえ CVS が完璧に動かないとしても、全く動かないという ことはまれです。It has the advantage of graceful degradation; どのくらい動作しないかというのは通常、その環境における問題の数と深刻 度に比例します。CVS が何をしようとしているか、またどのようにしようと しているかについてよく知っていれば、うまくいかない時に何をすべきかが わかるでしょう。

graceful 礼儀正しい degradation 品格を下げる悪くする退化する

遭遇する可能性のある問題をすべて書くことはできませんが、ここでは比較 的よくあるたぐいのことを書いてみました。この章は2つの部分に分かれて います: 1つめでは、環境のうち CVS にとって特にデリケートな部分につい て説明します(主にリポジトリのパーミッションと作業コピー中の管理領域 を扱います)。2つめではよくある問題とその解決法をいくつか説明します。 これらよくある状況をどう扱うかがわかれば、CVS で予期しない問題が起こっ てもどういうアプローチを取ればいいか大体わかるのではないかと思います。


Node:The Usual Suspects, Next:, Up:Tips And Troubleshooting

The Usual Suspects

あなたが CVS の管理者("field doctor"と読んで下さい)なら、ユーザの問 題の9割は矛盾した作業コピーが原因で、他の9割はリポジトリのパーミッション が間違っていることが原因だというのをご存知でしょう。従って、特定の状 況を見ていく前に作業コピーの管理領域についての概要と、リポジトリのパー ミッションについての重要な点をいくつか、手短に見ておくことにします。


Node:The Working Copy Administrative Area, Next:, Up:The Usual Suspects

The Working Copy Administrative Area

作業コピーの構造の基本的なところについては、An Overview of CVS で既に見てきました。この章ではもう少し詳細のところを見ます。詳細の大 部分は CVS/ 管理サブディレクトリ中のファイルに関連することです。 Entries, Root, Repository ファイルについては見てきたと思いますが、 CVS/ サブディレクトリには状況により他のファイルがあることもあります。 それらのファイルをここでは説明します。そういうファイルを見つけた時に 驚かないためであり、それらのファイルが原因の問題が起こった時になおす ことができるためです。

CVS/Entries.Log

時々、CVS/Entries.Log という名前のファイルが現れることがあり ます。このファイルは、Entries ファイルを書き換えるに足るような重要な 操作をするまで、Entries ファイルへの変更を一時的にキャッシュしておく ために使われます。CVS は Entries ファイルの適切な箇所だけを書き換え ることができません。変更するには、全部読んでから全部書き戻さなければ なりません。これを避けるため、Entries ファイルを書き換える必要が出て くるまでの間、少々の変更を Entries.Log に記録することがあるのです。

Entries.Log のフォーマットは Entries とほぼ同じですが、各行の冒頭に 一文字付け加わっています。A は Entries ファイルに追加すべき行 を意味し、R は削除すべき行を意味します。

Entries.Log ファイルは、たいていは無視して構いません; 欠いてある情報 を人間が理解しなければならないことはめったにありません。しかし、デバッ グのため作業コピー中の Entries ファイルを読んでいる場合には Entries.Log ファイルも調べるべきでしょう。

CVS/Entries.Backup

CVS/Entries.Backup ファイルは CVS が実際に新しい Entries ファイルを 書く場所です。書いたあと Entries にリネームされます(これは CVS がリポジトリ内に RCS ファイルを書く時に、一時的な RCS ファイルを 書いて、完成したら適切な名前に変える、というやりかたと同じです)。 完成し次第 Entries にリネームされるので、Entries.Backup ファイルはめっ たに見ることはないと思います。もし見ることになったとしたら、それは きっと、CVS がオペレーションの最中に中断されたということを意味します。

CVS/Entries.Static

CVS/Entries.Static ファイルが存在している場合、それはそのディレクト リ全体はリポジトリから取得されたものではないということを意味します。 (作業コピーが不完全な状態であるということを CVS がわかっている場合は、 そのディレクトリの中に余分なファイルを作ったりはしません)

Entries.Static ファイルはチェックアウト中やアップデート中に現われ、 オペレーションが完了し次第、すぐに削除されます。Entries.Static が見 えたとしたら、それは CVS が中断されたことを意味します。そのファイル があると、CVS は作業ディレクトリに新しいファイルを作ることができません。 (通常、cvs update -d を実行すれば Entries.Static が削除さ れ、問題は解決できます)

Entries.Static がなければ、作業コピーはプロジェクトのファイルをすべ て持っている、とは必ずしも言えません。プロジェクトのリポジトリ中に新 しいディレクトリが作成されて、誰かが作業コピーを -d フラグなしでアッ プデートした場合は、作業コピーに新しいディレクトリは作成されません。 この場合 CVS はリポジトリ中に新しいディレクトリがあるということに気 づかないので、作業コピー中に新しいディレクトリがなくても、アップデー トの完了時に Entries.Static ファイルを削除します。

CVS/Tag

CVS/Tag が存在する場合、ある意味、タグ名をディレクトリに関連づける役 割を果たします。ある意味、と言ったのはつまり、CVS はディレクトリに関 するリビジョン履歴を保存しませんし、厳密に言えばディレクトリにタグを つけることもできないからです。タグというのは通常のファイルのみに、正 確に言えば通常のファイルの特定のリビジョンにつけられるものなのです。

しかしながら、あるディレクトリ中の全ファイルが特定のタグをつけられて いるような状況では、CVS はそのディレクトリ全体にそのタグがつけられて いるとも考えるのです。たとえば、あるブランチ上の作業コピーをチェック アウトしたとしましょう:

floss$ cvs co -r Bugfix_Branch_1

ここにファイルを1つ追加する場合、その新しいファイルの最初のリビジョン は、そのブランチ上のリビジョンであって欲しいのではないでしょうか。 CVS も同じような理由で、そのディレクトリにブランチでないスティッキー タグや日付がセットされているかどうかを知る必要があります。

Tag ファイルには1行しかありません。その行の最初の文字はそのタグがどん な種類かを1文字で示すようなコードです。残りはタグ名です。現状、CVS はその1文字コードを3種類使っています:

(他の1文字コードがあったとしたらそれは、この章が書かれたより後に CVS に新しい種類のタグを追加されたということですね)

Tag ファイルを手で削除したりすべきではありません、かわりに cvs update -A を使用して下さい。

Rarities

他に CVS/ サブディレクトリ中にあるかもしれないファイルを記します:

これらのファイルが何か問題を起こすことは通常ありません、ただ並べてみ ただけです(詳しい説明は CVS Reference を参照のこと)。

Portability And Future Extension

CVS に新しい機能が追加されたとすると、作業コピーの管理領域に新しい (ここに書いてない)ファイルが現れるかもしれません。新しいファイルが追 加されれば、Cederqvist マニュアルの Working Directory Storage ノードにドキュメントされることと思います。また、コードから何か得たい と思ったら、ソースディストリビューション中の src/cvs.h を見てみると よいでしょう。

最後に、 全ての CVS/* ファイルは、現時点においても将来においても、作 業コピーの存在するシステムの改行コードを使用します(たとえば、Unix で は LF、Windows では CRLF)。このことは、作業コピーをあるマシンから他 のマシンへ移すと CVS はそれを扱えなくなるかもしれない、ということを 意味します(でもそういう時には、リビジョン管理下にあるファイル自体の 改行コードが新しい環境と不一致を起こすので、別の問題が起きるでしょう)。


Node:Repository Permissions, Previous:The Working Copy Administrative Area, Up:The Usual Suspects

Repository Permissions

CVS はリポジトリのパーミッションについてなにか特定の規則を要求したり はしません。幅広く、さまざまなパーミッションの配置を扱うことが出来ま す。しかし、わけの分からない動作を避けたければ、少なくとも以下に述べ る基準を満たすようリポジトリをセットアップするべきです:


Node:General Troubleshooting Tips, Next:, Previous:The Usual Suspects, Up:Tips And Troubleshooting

General Troubleshooting Tips

この章は問いと答えをつなげていく形式になっています。インターネットの FAQ (Frequently Asked Questions; よくある質問集) と同じような感じで す。すべて、実際に CVS を使用した経験に基づいたものです。個別の例を 見ていく前に、もう少し一般的な観点から CVS のトラブルシューティング について見ておきましょう。

通常、CVS で起きた問題を解決する最初のステップは、それが作業コピーの 問題なのか、それともリポジトリの問題なのかを見分けることです。これを 見分けるには(別に驚くような方法でもないのですが)、最初に問題の起こっ た作業コピーとは別の作業コピーで同じ問題が起こるかどうかを見ることで す。起こるようならリポジトリの問題で、そうでなければ多分局所的な問題 なのです。

作業コピーの問題のほうがよく起こりがちです。それは作業コピーがリポジ トリに比べてなにか信頼性が落ちるからとかではなくて、各リポジトリに対 して作業コピーはたくさんあるからです。作業コピーのもつれをほどくのが イヤになったら、その作業コピーを消してしまって新しいやつをチェックア ウトするほうが時間の無駄にならずに済む場合もあると思います。

もう一度チェックアウトするのにすごく時間がかかるとか、作業コピーにな くしたくない未コミットの変更があったり、またはなんでうまくいかないの かをただ知りたい場合でも、問題の原因を探る価値はあります。探り始める 時に最初にみるべきところの1つは CVS/ サブディレクトリです。その中の ファイルの内容とパーミッションをチェックして下さい。たまにですが、リー ドオンリーになっていたり、または読むことさえできなくなっていたりしま す。(これについては、CVS が間違えたというよりもむしろユーザが Unix コマンドをミスタイプしたことが原因ではないかと筆者は疑っています)

リポジトリの問題はたいていの場合、ファイルやディレクトリのパーミッション が間違っていることが原因です。リポジトリのパーミッションが原因だと思っ たら、まずトラブルのあった人の実効リポジトリユーザIDを確認して下さい。 ローカルユーザ全て及び大部分のリモートユーザに関しては、これは通常の ユーザ名か、作業コピーをチェックアウトする時に指定したユーザ名です。 pserver メソッドのユーザエイリアシング( Repository AdministrationAnonymous Access を参照のこ と)を使っている場合には、実効ユーザIDは CVSROOT/passwd ファイルの右 側に書いてあるものになります。早い時期にこれを見つけておかないと、間 違ったところをデバッグすることになってしまい、時間を無駄にしてしまう ことがあります。

さてさて、くだらないことをいうのはこれくらいにして…


Node:Some Real Life Problems (With Solutions), Previous:General Troubleshooting Tips, Up:Tips And Troubleshooting

Some Real Life Problems (With Solutions)

これらの状況はすべて、筆者が CVS トラブルシューターとして実際に体験 したものばかりです(それと、実は問題ではないのですがよく訊ねられる質 問も、答えとともに付け加えておきました)。このリストはとても広範囲に 渡るもので、以前の章で見たことでももう一度出てきます。

状況は起こりやすい順に並べました、最も一般的なものが初めに来ています。


Node:CVS says it is waiting for a lock; what does that mean?, Next:, Up:Some Real Life Problems (With Solutions)

CVS says it is waiting for a lock; what does that mean?

CVS が「ロック待ち中」って言うんですが、どういう意味?

このようなメッセージを見たら

cvs update: [22:58:26] waiting for qsmith's lock in /usr/local/newrepos/myproj

これは、その時点でほかの CVS プロセスにロックされているリポジトリ中 のサブディレクトリをアクセスしようとしている、という意味です。そのディ レクトリ内でプロセスが実行中なので、他の CVS プロセスが使うには一貫 性のない状態になっているかもよ、ということです。

しかし、そのメッセージが長い間出ているようなら、何らかの理由で CVS プロセスが後始末しないで失敗に終わってしまったことを示します。CVS が 突然、予期しないでお亡くなりになった時、たとえばリポジトリのあるマシン の電源が落ちたとか、そういう時に起こり得ます。

解決法は、問題のリポジトリサブディレクトリから、手でロックファイルを 取り除くことです。リポジトリの該当部分に移って、#cvs.lock と いう名前か、#cvs.wfl#cvs.rfl で始まる名前のファイ ルを探してください。ファイルのタイムスタンプを今実行中の CVS プロセ スの起動時刻と比較してください。ファイルがそれらのプロセスに生成され たのでないようならば、それを削除しても安全です。待機中の CVS プロセ スはロックファイルがなくなったと告げて(30秒ほどかかります)、要求され たオペレーションを続行できるようになります。

詳しくは Cederqvist マニュアルの Locks ノードを参照してくださ い。


Node:CVS claims a file is failing Up-To-Date check; what do I do?, Next:, Previous:CVS says it is waiting for a lock; what does that mean?, Up:Some Real Life Problems (With Solutions)

CVS claims a file is failing Up-To-Date check; what do I do?

CVS に、あるファイルが Up-To-Date チェックに失敗しましたと言われまし た。どうしたらいいの?

パニックにならないで。これは単に、あなたが最後にチェックアウトやアッ プデートしたよりあとに、リポジトリ中のそのファイルが変更されたことを 示しているだけです。そのファイルについて cvs update を実 行し、リポジトリから変更分をマージしてください。受け取った変更がロー カルの変更とコンフリクトした場合はファイルを編集してコンフリクトを解 消してください。そのあと、もう一度コミットしてください。成功するはず です。マージするのに忙しくしている間に他の人が別のリビジョンをコミッ トする可能性を除けば。


Node:The pserver access method is not working, Next:, Previous:CVS claims a file is failing Up-To-Date check; what do I do?, Up:Some Real Life Problems (With Solutions)

The pserver access method is not working

pserver アクセスメソッドが動きません。

この問題の、最も一般的で、でもちょっとわかりにくい原因は、 inetd の設定ファイル中に --allow-root オプションでリポジトリ を書くのを忘れた、というやつです。 Repository Administration で述べた、/etc/inetd.conf のこの例を 思い出してください:

cvspserver stream tcp nowait root /usr/local/bin/cvs cvs \
          --allow-root=/usr/local/newrepos pserver

(実際のファイルではこれはひとつの長い行で、バックスラッシュはありま せん)

--allow-root=/usr/local/newrepos の部分はセキュリティ上の制限 で、外部へ提供するつもりのないリポジトリへ pserver アクセスされない ようにするためのものです。pserver 経由でアクセスさせるリポジトリは 全部、--allow-root に書いておかなければなりません。 あなたのシステムのリポジトリを、必要な数だけ --allow-root オ プションで書いてください(inetd の引数の制限に達するまで、いくらでも たくさんかけます)。

パスワード認証サーバについて、詳しくは Repository Administration を参照してください。


Node:The pserver access method is STILL not working, Next:, Previous:The pserver access method is not working, Up:Some Real Life Problems (With Solutions)

The pserver access method is STILL not working

pserver アクセスメソッド、まだ動かないんですけど…

はいはい、--allow-root を書かなかったのが原因でなかった場合、 可能性は他にもいくつかあります:


Node:My commits seem to happen in pieces instead of atomically, Next:, Previous:The pserver access method is STILL not working, Up:Some Real Life Problems (With Solutions)

My commits seem to happen in pieces instead of atomically

コミットがアトミックにではなく、バラバラにおこなわれるようです

それは CVS がコミットをアトミックにでなく、バラバラにおこなっている からです。:-)

もっと言うと、CVS のオペレーションはディレクトリごとにおこなわれます。 複数のディレクトリにわたるコミットを実行したとき(あるいはアップデー トやなんか(or an update, or anything else, for that matter))、CVS は 順々に各リポジトリディレクトリ中でオペレーションを実行する間、そのディ レクトリにロックをかけます。

小〜中サイズのプロジェクトでは、これが問題になることはめったにありま せん。CVS は各ディレクトリ中での処理を素早くおこなうので、ユーザはそ の非アトミック性に気づいたりはしないでしょう。しかしあいにく大きなプ ロジェクトでは次の筋書きのようなきおとが起こり得ます(これが深くてファ イルのたくさんあるサブディレクトリを少なくとも2つ(A と B)持つプロジェ クトで起こる、と想像してください):

  1. ユーザ qsmith がコミットを始めます。そのコミットには両方のサブディレ クトリ中のファイルを含んでいます。CVS はまず B のなかのファイルをコ ミットします(おそらく qsmith はコマンドライン中でその順にディレクト リを指定したのでしょう)
  2. ユーザ jrandom がアップデートを始めます。アップデートは何らかの理由 で作業コピーのディレクトリAから始まりました(CVS はディレクトリやファ イルをどの順で処理するかについては何の保証もしません、if left to its own devices)。qsmith はまだ A では作業していませんので、ロックは問題 ではないことに注意してください。
  3. そして qsmith が B のコミットを終え、A に移り、A を終えます。
  4. 最後に jrandom が B に移り、アップデートを終えます。

全部終わった時点で、jrandom の作業コピーには qsmith の B の変更は反 映されている一方 A の変更が反映されていないことは明らかです。qsmith がそれらの変更をひとつの単位としてコミットしたつもりだったとしても、 実際はそのようにはなりませんでした。jrandom の作業コピーは qsmithが 予想もしない状態になってしまっています。

もちろん、jrandom がもう一度 cvs update して、さっき取って来れなかっ た qsmith のコミット分の変更を取って来れば、問題は解決します。しかし それは、最初に qsmith の変更のうち一部分しか取って来なかったことを知 るすべがあれば、の話です。

この quandary に容易な答はありません。ただ、作業コピーが一貫性のない 状態になっていることがどうにかして明らかになることを祈るほかありませ ん(そのソフトウェアがビルドできないかもしれませんし、何が起こったの かを認識するまで jrandom と qsmith が混乱した会話をすることになるか もしれません)。

一般に、CVS がアトミックなトランザクションを保証できていない についてはバグだと考えられています。ロックがリポジトリのトップレベル に作られないただひとつの理由というのは、開発者が大勢いる大きなプロジェ クトではロックの競合が耐えられないくらい頻繁に起こるであろうというこ とです。従って、CVS は2つの害のうち小さなほうを選びました。競合の頻 度を減らすかわりに、とびとびに読んだり書いたりする可能性を許した、と いうことです。そのうち誰かが CVS を改良し(たとえばリポジトリオペレー ションの速度を上げるなどして)、2つの害のどちらけを選んだりしなくても よくなるかもしれません。それまでは、ノンアトミックな動作に甘んじるこ とになるでしょう。

より詳しいことを知るためには、 Cederqvist マニュアルの Concurrency を参照してください。


Node:CVS keeps changing file permissions; why does it do that?, Next:, Previous:My commits seem to happen in pieces instead of atomically, Up:Some Real Life Problems (With Solutions)

CVS keeps changing file permissions; why does it do that?

CVS がファイルのパーミッションを変更しつづけるんです; なんでそんなこ とをするんでしょうか?

一般に、 CVS はファイルのパーミッションを保存するについてはあまり良 い仕事をしません。プロジェクトをインポートしたあと、それをチェックア ウトする場合、新しい作業コピーのファイルのパーミッションが、インポー トされたときと同じである保証はありません。作業コピー中のファイルが、 普通にファイルを新しく作ったときの標準パーミッションと同じ、というこ とのほうがありそうです。

しかし、例外が少なくともひとつあります。プロジェクト内に実行可能なシェ ルスクリプトを保存したい場合には、対応するリポジトリのファイルを実行 可能にしておけば、全ての作業コピーでそのファイルを実行可能に保つこと ができます:

floss$ ls -l /usr/local/newrepos/someproj
total 6
-r--r--r--   1 jrandom  users         630 Aug 17 01:10 README.txt,v
-r-xr-xr-x   1 jrandom  users        1041 Aug 17 01:10 scrub.pl,v*
-r--r--r--   1 jrandom  users         750 Aug 17 01:10 hello.c,v

そのファイルは実行可能ですが、リポジトリ中のファイルがすべてそうであ るように、読み出し専用になっているままです(CVS は RCS ファイルの一時 的なコピーを作って作業し、そのコピーの中ですべてをおこなってから、準 備ができたらそのコピーをオリジナルと置き換える、ということを思い出し てください)。

実行可能ファイルをインポートまたは追加した場合には、CVS は実行可能ビッ トを保存しますので、ファイルが最初から正しいパーミッションならばなに も心配することはありません。ですがたまたま、実行可能にする前にファイ ルを追加してしまったら、リポジトリ内に移って、RCS ファイルのパーミッ ションを手で実行可能にしてください。

リポジトリのパーミッションのほうがつねに優先されます。リポジトリ内の そのファイルが実行可能ではなくて、作業コピー中では実行可能だった場合、 アップデート後には作業コピーのそのファイルは実行可能ではなくなってし まいます。自分のファイルのパーミッションが黙って変更されてしまうとい うのは非常にいらだたしいことです。これが起こったら、まずリポジトリを チェックし、対応する RCS ファイルを適切なパーミッションに設定して解 決するかどうか見てみてください。

最近、CVS に PreservePermissions という機能が追加されました。 この機能はこのような問題を少しは軽減するかもしれません。しかし、この 機能を使うと、考えもしなかった他の結果をうむことになるかもしれません (無条件にこの機能を推薦しなかったのはこれが理由です)。CVSROOT/config に PreservePermissions=yes を書く前に、Cederqvist マニュアル の configSpecial Files のノードをよく読んでくださ いね。


Node:CVS on Windows complains it cannot find my .cvspass file; why?, Next:, Previous:CVS keeps changing file permissions; why does it do that?, Up:Some Real Life Problems (With Solutions)

CVS on Windows complains it cannot find my .cvspass file; why?

Windows 上の CVS に、.cvspass ファイルが見つからないといって怒られま した。どうして?

pserver 接続時に、クライアント側の CVS はホームディレクトリにある .cvspass ファイルを探そうとします。Windows マシンはもともとはホーム ディレクトリを持っていないので、CVS は %HOME% 環境変数を見に いきます。しかし、HOME を設定するについては非常に慎重でなければなり ません。これは動きます:

set HOME=C:

これは動きません:

set HOME=C:\

最後の余分なバックスラッシュが CVS を混乱させてしまい、 C:\\.cvspass がオープンできない、ということになってしまうので す。

ですから、すばやくて不変の解決法は、autoexec.bat に

set HOME=C:

と書いてリブートすることです。こうすれば CVS の pserver は動くでしょう。


Node:My working copy is on several different branches; help?, Next:, Previous:CVS on Windows complains it cannot find my .cvspass file; why?, Up:Some Real Life Problems (With Solutions)

My working copy is on several different branches; help?

わたしの作業コピーはいくつかの別々のブランチの上にいます。どうすれば いいですか?

作業ディレクトリの別々のサブディレクトリが、どういうわけか別のブラン チにある、という意味でしょうか? おそらく作業コピーのトップレベルでは ないところで、 -r フラグをつけてアップデートをしてしまったのでしょう ね。

たいしたことではありません。トランクに戻りたければ、トップディレクト リでこれを実行してください

cvs update -r HEAD

またはこれです

cvs update -A

あるいは、作業コピー全体をひとつのブランチ上のものにしたければ、こう してください:

cvs update -r Branch_Name

あるディレクトリをブランチにあげて一時的な作業をする必要があるのなら、 作業コピーのなかのサブディレクトリが1つ2つ、他と違うブランチになって いてもべつに構わないと思います。でも、普通は終わったら元に戻しておい たほうがいいでしょう。作業コピー全体が同じ開発ライン上にいるほうが、 混乱がうんと少ないですから。


Node:When I do export -d I sometimes miss recent commits, Next:, Previous:My working copy is on several different branches; help?, Up:Some Real Life Problems (With Solutions)

When I do export -d I sometimes miss recent commits

export -d した時に、最近のコミット分がないことがあるんです。

これはリポジトリマシンとローカルマシンの時計がずれているためです。片 方あるいは両方の時計を合わせ直すか、-D の引数に日付を指定すれば解決 します。未来の日付を指定しても(-D tomorrow など)受け付けられますので、 時刻のずれの分を補正できます。


Node:I get an error about val-tags; what should I do?, Next:, Previous:When I do export -d I sometimes miss recent commits, Up:Some Real Life Problems (With Solutions)

I get an error about val-tags; what should I do?

val-tags に関するエラーが起きたようなんですが、どうすればいいですか?

このようなエラーが起きたら:

cvs [export aborted]: cannot write /usr/local/myproj/CVSROOT/val-tags: \
   Operation not permitted

CVSROOT/val-tags ファイルに書く権限のないユーザが CVS を実行している という意味です。このファイルには有効なタグ名が書いてあって、どのタグ が有効かを素早く調べるためのものです。あいにく、リポジトリに関して読 み込み専用(例えばプロジェクトをチェックアウトするなど)のオペレーション をした時でも、CVS はこのファイルを変更することがあります。

これは CVS のバグで、これを読んでいる頃には多分解決されていると思い ます。それまでは val-tags ファイルをワールドライタブルにするか、それ ができなければそのファイルを削除するか、またはオーナを変更して CVS オペレーションを実行するユーザにするかしてください。(パーミッション を変更するだけでいいと思うかもしれませんが、いくつかの事例ではオーナ も変更する必要がありました。)


Node:I am having problems with sticky tags; how do I get rid of them?, Next:, Previous:I get an error about val-tags; what should I do?, Up:Some Real Life Problems (With Solutions)

I am having problems with sticky tags; how do I get rid of them?

スティッキータグで困っています; どうやって取り除けばよいでしょう?

さまざまな CVS オペレーションが、作業コピーに スティッキータグ を設定します。各ファイルの各リビジョンに対応しているタグを意味します。 (ブランチの場合は作業コピーに新しく追加されたファイルすべてにスティッ キータグが適用されます。) タグか日付を指定してチェックアウトやアップ デートを行った場合、作業コピーにスティッキータグが設定されます。例え ば:

floss$ cvs update -r Tag_Name

あるいは

floss$ cvs checkout -D '1999-08-16'

日付かノンブランチタグ名が指定された場合には、作業コピーはプロジェク ト履歴中のその時点で凍結したスナップショットになります。ですので当然、 そこから変更をコミットしたりはできないのです。

スティッキータグを除去するには、-A フラグ付きでアップデートを実行し てください

floss$ cvs update -A

こうすればスティッキータグはすべてクリアされ、各ファイルは最新のトラン クのリビジョンに更新されます。


Node:Checkouts/updates exit with error saying cannot expand modules, Next:, Previous:I am having problems with sticky tags; how do I get rid of them?, Up:Some Real Life Problems (With Solutions)

Checkouts/updates exit with error saying cannot expand modules

チェックアウト/アップデートがモジュールを展開できないと言って、エラー で終了します。

これは CVS が悪いエラーメッセージを出す一例ですね。そのうち誰かが直 すと思いますが、しばらくは困りますね。このエラーメッセージはこのよう な感じで出ます:

floss$ cvs co -d bwf-misc user-space/bwf/writings/misc
cvs server: cannot find module `user-space/bwf/writings/misc' - ignored
cvs [checkout aborted]: cannot expand modules

CVS は CVSROOT/modules ファイルが何かおかしいといっているように見え ます。が、本当はリポジトリ内のパーミッションの問題なのです。チェック アウトしようとしたディレクトリか、その親ディレクトリのうちのどれかが 読み出し可能になっていないのです。今回の場合は親ディレクトリでした:

floss$ ls -ld /usr/local/cvs/user-space/bwf

drwx------  19 bwf      users        1024 Aug 17 01:24 bwf/

ひどく間違ったこのエラーメッセージには惑わされないで下さい、これはリ ポジトリのパーミッションの問題なのです。


Node:I cannot seem to turn off watches, Next:, Previous:Checkouts/updates exit with error saying cannot expand modules, Up:Some Real Life Problems (With Solutions)

I cannot seem to turn off watches

監視をオフにできないみたいなんですけど…。

おそらく、全てのファイルに対して

floss$ cvs watch remove

とやったのでしょうけれど、これを実行するのを忘れたんじゃないですか:

floss$ cvs watch off

監視についての問題を診断する時のヒント: リポジトリに移って、 CVS/fileattr ファイルを直接見れば、なにもかもすっかり明らかになるこ とがあります。詳しくは Repository Administration を参照してく ださい。


Node:My binary files are messed up, Next:, Previous:I cannot seem to turn off watches, Up:Some Real Life Problems (With Solutions)

My binary files are messed up

バイナリファイルがむちゃくちゃになってしまいました。

そのファイルを追加した時に -kb オプションをつけるのを忘れたりしません でしたか? 忘れている場合、 CVS はそのファイルに対し改行コード変換や RCS キーワード展開を施してしまいます。一番簡単な解決法は通常、そのファ イルをバイナリとしてマークして、

floss$ cvs admin -kb foo.gif

そのあと、直してあるそのファイルをコミットします。CVS はそれがバイナ リファイルだということをもう知っているので、新しくコミットしたものや それ以降コミットしたものを CVS が台無しにしてしまうことはないでしょ う。


Node:CVS is not doing line-end conversion correctly, Next:, Previous:My binary files are messed up, Up:Some Real Life Problems (With Solutions)

CVS is not doing line-end conversion correctly

改行コードの変換が正しくおこなわれないようなのですが。

CVS クライアントを Unix でないプラットフォームで実行していて、作業コ ピー中のファイルの改行コード変換がおこなわれていないようならば、きっ とそれは知らない間に -kb オプションつきで追加されてしまったんじゃな いかと思います。リポジトリの問題は以下のコマンドで解決できます。

floss$ cvs admin -kkv FILE

-kkv というのは、通常のキーワード展開をし、通常の改行コード変換もお こなう、という意味です。(内部的な話をすると、 CVS はキーワード展開と 改行コード変換の違いについて少々混同しているところがあります。-k オ プションで両方をコントロールできるあたりにそれが現れています。)

あいにく、admin コマンドではリポジトリ内のファイルしか直せません。作 業コピーは相変らずそのファイルをバイナリだと思っています。 CVS/Entries のそのファイルの行を手で編集して -kb を削除すればよいの ですが、それで他の作業コピーの問題まで解決するわけではありません。


Node:I need to remove a subdirectory in my project; how do I do it?, Next:, Previous:CVS is not doing line-end conversion correctly, Up:Some Real Life Problems (With Solutions)

I need to remove a subdirectory in my project; how do I do it?

プロジェクト内のサブディレクトリを削除しなければいけないのですが、ど うやればいいですか?

そうですね、サブディレクトリを完全に削除することはできないのですが、 そのディレクトリの中のファイルをすべて削除することはできますよね(ま ずファイルを削除し、cvs remove して、コミットしてください)。ディレク トリが空になったら、-P フラグをつけてアップデートすると作業コピー中 のそのサブディレクトリが自動的に刈り込まれます。


Node:Can I copy .cvspass files or portions of them?, Next:, Previous:I need to remove a subdirectory in my project; how do I do it?, Up:Some Real Life Problems (With Solutions)

Can I copy .cvspass files or portions of them?

.cvspass ファイルや、その一部をコピーしても構いませんか?

はい、構いませんよ。.cvspass ファイルをマシン間でコピーしても 構いませんし、各行を個別にコピーしても大丈夫です。サーバの待ち時間が 大きいような場合には作業コピーマシンから cvs login を実行するより、 この方法を取るほうが早いと思います。

.cvspass を改行コードの異なる2つのマシン間で転送すると動かないという ことに注意してください(もちろん、手で改行コードを変換してもかまいま せん、そんなに手間がかかるわけではないでしょうからね)。


Node:I just committed some files with the wrong log message, Next:, Previous:Can I copy .cvspass files or portions of them?, Up:Some Real Life Problems (With Solutions)

I just committed some files with the wrong log message

ファイルをいくつか、間違ったログメッセージでコミットしてしまいました。

これを解決するのに、リポジトリを手で編集する必要はありません。admin コマンドを -m フラグ付きで実行すれば直ります。-m と引数の間には空白 を入れないということと、置き換えるログメッセージをクオートで囲むこと を忘れないようにして下さい:

floss$ cvs admin -m1.17:'I take back what I said about the customer.' hello.c


Node:I need to move files around without losing revision history, Next:, Previous:I just committed some files with the wrong log message, Up:Some Real Life Problems (With Solutions)

I need to move files around without losing revision history

リビジョン履歴をなくさないでファイルを移動したいんですが。

リポジトリ内で、プロジェクトの中のお望みの新しい場所に RCS ファイル をコピーしてください(移動はしないで下さい)。古いほうの場所にも残して おかなければだめです。次に、作業コピー中でこのようにしてください:

floss$ rm oldfile1 oldfile2 ...
floss$ cvs remove oldfile1 oldfile2 ...
floss$ cvs commit -m "removed from here" oldfile1 oldfile2 ...

これ以降にアップデートをすると、CVS は古いファイルを消し、通常の方法 でリポジトリに加えた時と同じように、新しいファイルを作業コピー中に持っ てきます(普通よりリビジョン番号が高いところは、新しいファイルを加え た時と違います)。


Node:How can I get a list of all tags in a project?, Next:, Previous:I need to move files around without losing revision history, Up:Some Real Life Problems (With Solutions)

How can I get a list of all tags in a project?

プロジェクト中のタグの一覧が欲しいんですけどどうすればよいですか?

現在の CVS では簡単にこれを実現する方法はありません。ユーザみんなが これがないことで困っているので、この機能を実現するべく作業が進行中だ と思います。あなたがこれを読んでいる頃までには cvs tags コマンドかそれに似たようなものが使えるようになっているでしょう。

Until then, there are workarounds. cvs log -h を実行し、結果出力のな かの symbolic names: のあとを読んでください。あるいは、リポジ トリマシン上にログインしているならリポジトリ中の RCS ファイルの冒頭 を読めばわかると思います。タグが全部(ブランチタグもノンブランチタグ も) symbols フィールドにリストしてあります:

floss$ head /usr/local/newrepos/hello.c,v
head	2.0;
access;
symbols
	Release_1_0:1.22
	Exotic_Greetings-2:1.21
	merged-Exotic_Greetings-1:1.21
	Exotic_Greetings-1:1.21
	merged-Exotic_Greetings:1.21
	Exotic_Greetings-branch:1.21.0.2
	Root-of-Exotic_Greetings:1.21
	start:1.1.1.1
	jrandom:1.1.1;
locks; strict;
comment	@ * @;


Node:How can I get a list of all projects in a repository?, Next:, Previous:How can I get a list of all tags in a project?, Up:Some Real Life Problems (With Solutions)

How can I get a list of all projects in a repository?

リポジトリ中のプロジェクト全部の一覧はどうやれば見ることが出来ますか?

タグのリストと同じように、CVS の現在のバージョンでは実装されていません。 が、多分すぐ実装されるんじゃないかと思います。多分そのコマンドは cvs list (省略形式だと cvs ls)で、modules ファイルとリポジトリのサブディ レクトリの両方を解釈するようなものになるでしょう。

今のところはとりあえず(直接見るか cvs checkout -c を実行するかして) CVSROOT/modules ファイルを調べるのが多分一番いいと思います。ただし、 誰もプロジェクトに対してモジュールを明示的に指定していない場合には そこには何も示されていないでしょう。


Node:Some commands fail remotely but not locally; how should I debug?, Next:, Previous:How can I get a list of all projects in a repository?, Up:Some Real Life Problems (With Solutions)

Some commands fail remotely but not locally; how should I debug?

リモートで失敗するコマンドがあるんですが、ローカルでは失敗しません; どうデバッグするべき?

クライアントとサーバ間の通信に問題が存在することがあります。もしそう ならそれは CVS のバグなんですが、さてそれをどうやって追跡すればよい のでしょうか?

CVS ではクライアントとサーバ間のプロトコルを監視する方法があります。 ローカル(作業コピーのある)マシン上でコマンドを動かす前に、 CVS_CLIENT_LOG 環境変数をセットしてください。Bourne シェルで は次のように設定します:

floss$ CVS_CLIENT_LOG=clog; export CVS_CLIENT_LOG

CVS はこの変数がセットされると、クライアントとサーバの間の通信を、ファ イル2つにすべて記録します。ファイル名はその環境変数に設定された値に 基づいたものになります:

floss$ ls
CVS/        README.txt    a-subdir/    b-subdir/    foo.gif     hello.c
floss$ cvs update
? clog.in
? clog.out
cvs server: Updating .
cvs server: Updating a-subdir
cvs server: Updating a-subdir/subsubdir
cvs server: Updating b-subdir
floss$ ls
CVS/              a-subdir/    clog.in     foo.gif
README.txt        b-subdir/    clog.out    hello.c
floss$

clog.in ファイルにはクライアントがサーバに送信したもの全てが、 clog.out ファイルにはサーバがクライアントに送り返してきたもの 全てが記録してあります。プロトコルがどんな感じに見えるのか示すために、 clog.out の内容をお見せします:

Valid-requests Root Valid-responses valid-requests Repository           \
Directory Max-dotdot Static-directory Sticky Checkin-prog Update-prog   \
Entry Kopt Checkin-time Modified Is-modified UseUnchanged Unchanged     \
Notify Questionable Case Argument Argumentx Global_option Gzip-stream   \
wrapper-sendme-rcsOptions Set expand-modules ci co update diff log add  \
remove update-patches gzip-file-contents status rdiff tag rtag import   \
admin export history release watch-on watch-off watch-add watch-remove  \
watchers editors init annotate noop
ok
M ? clog.in
M ? clog.out
E cvs server: Updating .
E cvs server: Updating a-subdir
E cvs server: Updating a-subdir/subsubdir
E cvs server: Updating b-subdir
ok

clog.in ファイルのほうはリビジョン番号や他のファイルごとの情報をサー バに送らなければならないので、もう少し複雑です。

クライアント/サーバのプロトコルを書くには紙幅が足りませんが、CVS と 一緒に配布されている cvsclient という info ページに完全な解 説が載っていますので読んでみてください。また、生のプロトコルを読むと いろいろと分かると思います。問題の原因の他の可能性を全て除去するまで クライアントロギングを使う気分になれなかったとしても、クライアントと サーバの間で何が起こっているか探るにはとても価値のあるツールです。


Node:I do not see my problem covered in this chapter, Next:, Previous:Some commands fail remotely but not locally; how should I debug?, Up:Some Real Life Problems (With Solutions)

I do not see my problem covered in this chapter

この章にはわたしの直面している問題は載っていないようですが。

正確かつ完全に問題を記述し、CVS のメーリングリスト info-cvs@gnu.org にメールを送ってください。メンバーはいろい ろなタイムゾーンに散らばっているので、いつも筆者が問題を送ってから1, 2時間で答えが返ってきます。 info-cvs-request@gnu.org にメー ルを送ってメーリングリストに加入してください。質問に答えてあげること もできます。


Node:I think I have discovered a bug in CVS; what do I do?, Next:, Previous:I do not see my problem covered in this chapter, Up:Some Real Life Problems (With Solutions)

I think I have discovered a bug in CVS; what do I do?

CVS のバグを見つけたと思うんですが; 何をすればいいですか?

CVS は完璧から程遠いです。マニュアルを読んで、メーリングリストに質問 を投稿しても、それでもバグがあるように思うなら、多分それはバグなので しょう。

バグを完全に記述したものを bug-cvs@gnu.org に送ってください (このメーリングリストに加入することもできます、 bug-cvs-request@gnu.org にメールを送ってください)。 CVS のバージョン番号と(クライアントのもサーバのも)、バグを再現する方 法を書いてくださいね。

もしそのバグを直すパッチを書いたのならそれを同封し、メールのサブジェ クトにパッチがあることを書いてください。メインテナはきっととても喜ぶ と思います。

(Cederqvist マニュアルの BUGS ノードと、ソースディストリビュー ションの HACKING ファイルに、これらの手続きについて詳しく書いてあり ます。)


Node:I have implemented a new feature for CVS; to whom do I send it?, Next:, Previous:I think I have discovered a bug in CVS; what do I do?, Up:Some Real Life Problems (With Solutions)

I have implemented a new feature for CVS; to whom do I send it?

CVS の新しい機能を実装しました; 誰に送ればよいでしょう?

バグを送る時と同じです: bug-cvs@gnu.org にパッチを送ってく ださい。でもまず HACKING ファイルをちゃんと読んでくださいね。


Node:How can I keep up with changes to CVS?, Previous:I have implemented a new feature for CVS; to whom do I send it?, Up:Some Real Life Problems (With Solutions)

How can I keep up with changes to CVS?

CVS の変更はどうやって追いかければよいのでしょうか?

この章で説明したトラブルシューティングのテクニックと既知のバグは、 バージョン 1.10.7 時点の CVS では正しいです。でも CVS では物事の動き が速いのです。最近の数章を書いている間に、CVS の非公式な保守責任が Cyclic Software から SourceGear, Inc (http://www.sourcegear.com) に引き継がれました。SourceGear が Cyclic を買収したのです。SourceGear は CVS の保守に関して積極的にやっ ていくつもりであることをアナウンスし、Cyclic の承認を得ましたので、 which is more or less enough to make it the "lead maintainer" of CVS as of right now. (http://www.cyclic.com のアドレスは引き続き有効 ですので、この本で今までに述べた URL は有効です)

現時点、SourceGear はそのへんに散らばっていたいろいろなパッチをまと めてきれいにするのに忙しいようです。それらの大半を CVS に組み込むつ もりのようですから。そのなかにはこれまでに挙げてきたバグを直すパッチ もありますし、CVS ユーザに新しいトラブルシューティングツールを提供す るパッチもあります。

最新のところで何が起こっているかに追いつくための一番よい方法は、CVS のディストリビューション中にある NEWS ファイルを読み、メーリングリス トを見て、Cederqvist マニュアルとこの本のオンラインバージョン (http://cvsbook.red-bean.com)の変更を探すことです。


Node:CVS Reference, Next:, Previous:Tips And Troubleshooting, Up:Top

CVS Reference

この章は CVS のコマンド、リポジトリの管理ファイル、キーワード展開、 実行時制御ファイル、作業コピー、環境変数、についての完全なリファレン スです。すべてバージョン 1.10.7 時点の CVS に基づいています(正確には 1999/8/20 時点)。


Node:Commands And Options, Next:, Up:CVS Reference

Commands And Options

この節はすべての CVS コマンドのリファレンスです。CVS のコマンドほとん どに共通する書式の約束事がまだよくわからない場合には、個々のコマン ドを調べる前に関連する節を読んでおいたほうがいいと思います。


Node:Organization And Conventions, Next:, Up:Commands And Options

Organization And Conventions

コマンドやオプションを調べやすくするため、この節はアルファベット順に 並べてあります。以下の約束事があります:


Node:General Patterns In CVS Commands, Next:, Previous:Organization And Conventions, Up:Commands And Options

General Patterns In CVS Commands

CVS コマンドの形式は次のようになります:

cvs [GLOBAL_OPTIONS] COMMAND [OPTIONS] [FILES]

オプションのうち、2つめのまとまりのほうは、コマンドオプション と呼ばれることがあります。が、あまりにもたくさんありますので、場所の 節約のため、筆者はただ「オプション」と呼ぶことのほうが多いです。

コマンドの多くは作業コピー中で実行されますので、ファイル引数なしで起 動されるでしょう。このようなコマンドではデフォルトで、カレントディレ クトリ以下全てのファイルについて実行されます。ですので説明の中で「ファ イル」とあればそれは、CVS が動作している対象のファイルのことだと思っ てください。CVS をどのように起動するかによって、これらのファイルはコ マンドラインで明示的に指定されたりされなかったりします。


Node:Date Formats, Next:, Previous:General Patterns In CVS Commands, Up:Commands And Options

Date Formats

多くのオプションが日時の引数を取ります。CVS は実にさまざまな形式の日 時を理解します。多すぎるのでここに一覧できません。自信がない時には、 標準の ISO 8601 フォーマットを守ってください:

1999-08-23

これは 1999年8月23日を意味します(実は "23 August 1999" でも全く有効 ですが、ダブルクオートで括ることをお忘れなく)。時刻も指定したければ、 このようにできます:

"1999-08-23 21:20:30 CDT"

"now", "yesterday", "12 days ago" などの、ごく普通の英語の構文も使え ます。一般に、日時のフォーマットは安全に実験できます。CVS があなたの 言っている形式を理解できるなら、あなたの言うとおりに解釈するでしょう。 理解できないならすぐエラーで終了します。


Node:Global Options, Next:, Previous:Date Formats, Up:Commands And Options

Global Options

CVS のすべてのグローバルオプションを以下に記します。

--allow-root=REPOSITORY

アルファベット順で最初に来るこのオプションは、実際にはコマンドラインで 使用されることはありません。-allow-root オプションは pserver コマンドと共に使用して、アクセスを許可するリポジトリを指定します。指定 は /usr/local/newrepos のようにリポジトリレベルで行います。 /usr/local/newrepos/myproj のようなプロジェクトサブディレクトリ の指定はできません。

このグローバルオプションがコマンドラインで使用されることはありません。 通常は /etc/inetd.conf ファイルの中でのみ用いられるものです (Repository Administration 参照)。pserver コマンドが使用 されるのもそこだけです。

cvs pserver 経由でアクセスするリポジトリについては、対応す る -allow-root オプションをそのホストの /etc/inetd.conf 中に 指定しておく必要があります。これはセキュリティのための仕組みで、プライ ベートなリポジトリに pserver 経由のアクセスをさせないようにするもので す。

(The Password-Authenticating Server と、Cederqvist マニュアルの Password Authentication Server ノードを参照してください。)

-a

サーバとの通信すべてを認めます。このオプションは GSSAPI サーバ (gserver) 経由で接続している場合以外には意味はありません。GSSAPI はま だあまり使われていないと思われますので、この本で説明していません(その うち使われるようになるとは思うのですが)。(詳細は Cederqvist マニュアル の Global OptionsGSSAPI Authenticated を参照のこと。)

-b (Obsolete)

以前はこのオプションで、RCS のバイナリファイルのあるディレクトリを指定 していました。現時点で CVS は RCS の機能を内部的に実装していますので、 このオプションに意味はありません(互換性のために残してあるだけです)。

-d REPOSITORY

リポジトリを指定します。絶対パスであったり、あるいは接続方法・ユーザ名・ ホスト名・パスを含んだ複雑なものである場合もあります。接続方法を指定す る場合、構文は次のとおりです:

:METHOD:USER@HOSTNAME:PATH_TO_REPOSITORY

各接続方法について例を示します:

-e EDITOR

コミットメッセージが -m オプションで指定されない場合、コミットメッセー ジを書くためのエディタとして EDITOR を起動します。普通は -m でのメッセ ージ指定を行わなかった場合、CVS は $CVSEDITOR$VISUAL$EDITOR 環境変数をこの順でチェックし、その結果に基づいてエディ タを起動します。これでうまくいかなかったときには Unix 上のポピュラーな エディタ、vi を起動します。

Invokes EDITOR for your commit message, if the commit message was not specified on the command line with the -m option. Normally, if you don't give a message with -m, CVS invokes the editor based on the $CVSEDITOR, $VISUAL, or $EDITOR environment variables, which it checks in that order. Failing that, it invokes the popular Unix editor vi.

コミット時に -e グローバルオプションと -m オプション両方を渡した場合に は、 コマンドラインで指定したコミットメッセージを優先し、-e は無視され ます(ですので .cvsrc ファイルで -e を指定しておいも安全です)。

-f

このグローバルオプションを指定すると .cvsrc ファイルを読みませ ん。

--help [COMMAND] or -H [COMMAND]

これら2つのオプションは同義のものです。 COMMAND が指定されない場合、基 本的な使用法が標準出力に出力されます。COMMAND が指定された場合はそのコ マンドの使用法が出力されます。

--help-options

CVS の全グローバルオプション一覧を短い説明つきで出力します。

--help-synonyms

CVS コマンドの一覧を省略形つきで出力します(update は up だとか、そうい うやつです)。

-l

このコマンドをリポジトリ内の CVSROOT/history ファイルに記録しま せん。コマンドは通常どおり実行されますが、history ファイルに記録が残り ません。

-n

作業コピー内、リポジトリ内のファイルを一切変更しません。つまりそのコマ ンドは「ドライラン」(dry run) で実行するということです。CVS はそのコマ ンドのほとんどの手順を踏みますが、実際に実行する手前で中止します。

そのコマンドが何をするのか、実際には実行しないで知りたい場合に便利です。 自分の作業ディレクトリ中でどのファイルを変更したんだったか知りたいけど、 アップデートしたいわけじゃない(リポジトリから変更分を持ってきちゃうか ら)、というような話はよくあることです。cvs -n update を実行 すれば、作業コピー中でファイルを変えることなく、ローカルで何をしたか 概要がわかります。

-q

あまり重要でないメッセージの出力を止めて、適当に静かに動作するよう CVS に指示します。何が「重要」かはコマンドによって違います。たとえば、アッ プデート中の作業コピーの各サブディレクトリへの移動(通常は出力される)に ついては出力されませんが、変更されたファイル、アップデートされたファイ ルについての情報は出力されます。

-Q

コマンドを実行完了するために絶対必要なもの以外は出力せず、非常に静かに 動作するよう CVS に指示します。出力を生成することが唯一の目的であるよ うなコマンド(diffannotate など)は出力をやめたりしま せんが、出力するメッセージとコマンドの効果が関係ないようなコマンド (updatecommit など)は何のメッセージも出力しません。

-r

新しく作る作業ファイルを読み込み専用にします($CVSREAD 環境変数 を設定するのと同様の効果)。

このオプションを指定した場合、チェックアウト・アップデートすると作業コ ピー中のファイルが読み込み専用になります(OSが許せば)。率直に言って筆者 はこのオプションを使いたいような理由を思いつきません。

-s VARIABLE=VALUE

変数名 VARIABLE の CVS の内部変数の値を VALUE に設定します。

リポジトリ側の CVSROOT/*info トリガファイルはこのような変数を -s オプションで代入された値に展開することができます。たとえば、 CVSROOT/loginfo に次のような行があったとします:

myproj  /usr/local/bin/foo.pl ${=FISH}

ここで誰かが myproj の作業コピーから、次のようにコミットしたとすると

floss$ cvs -s FISH=carp commit -m "fixed the bait bug"

foo.pl スクリプトは carp を引数として起動されます。ただ し書式には注意してください、ドルマーク、イコール、中括弧すべて必要です。 一つでも欠けると展開されません(少なくとも思ったようにはなりません)。変 数名には英数字とアンダスコアが使用できます。すべてを大文字にする必要は ないのですが、そのようにする人が大多数です。

ひとつのコマンド中に -s フラグを何回使ってもかまいません。しかし、トリ ガスクリプト中で参照されている変数を CVS の起動時に指定しなかった場合、 コマンド自体は成功したとしても変数展開は行われず、警告メッセージが出力 されます。たとえば loginfo にこのようなエントリがあって、

myproj  /usr/local/bin/foo.pl  ${=FISH}  ${=BIRD}

前述と同様のコマンドが実行された場合には

floss$ cvs -s FISH=carp commit -m "fixed the bait bug"

このコマンドを実行したユーザに対して次のような警告が出力され(最後のほ うに出力されます)、

loginfo:31: no such user variable ${=BIRD}

この後 foo.pl スクリプトが引数無しで起動されます。ですが、以下 のようにコマンドを実行した場合には

floss$ cvs -s FISH=carp -s BIRD=vulture commit -m "fixed the bait bug"

loginfo 中の ${=FISH}${=BIRD} は両方正しく展開さ れ、警告も出力されません。どちらの場合でもコミットそのものは成功するは ずです。

これらの例ではすべて commit を使用しましたが、変数展開は CVSROOT/ のトリガファイルで書ける CVS コマンドならどれでも変数 展開できます。ですから -s オプションはグローバルオプションなのです。

(このあとこの章に出てくる Repository Administrative Files の節で、 トリガファイル内での変数展開について詳しく述べます)

-T DIR

テンポラリファイルを、普通 CVS が置く場所ではなく DIR に置きます。(と りわけ $TMPDIR 環境変数の値より優先されることに注意してください) DIR は絶対パス指定でなければなりません。

このオプションは、通常のテンポラリディレクトリへの書込み権限をあなたが (and, therefore, CVS doesn't either)持っていない場合に便利です。

-t

CVS コマンドの実行をトレースします。そのコマンドを完遂するまでに通過す る各ステップについてのメッセージを出力します。-n オプションと合わせて 使うと、まだ良くわかっていないコマンドの動作を実際に動作させる前に確認 できて便利です。また、あるコマンドが失敗する時、その原因を調べたい場合 にも使えます。

-v or --version

バージョンと著作権情報を表示し、エラーなしで終了します。

-w

$CVSREAD 環境変数

作業ファイルを読み書きできるファイルとして生成します($CVSREAD 環境変数より優先されます)。デフォルトで読み書きできるファイルが生成さ れますので、このオプションはめったに使用されません。

-r と -w 両方を指定した場合には -w のほうが優先されます。

-x

サーバとの通信内容をすべて暗号化します。このオプションは GSSAPI サーバ (gserver) 経由で接続している場合以外は意味がありません。 GSSAPI 接続は まだめったに使われていませんので、この本では説明していません。 (詳しくは Cederqvist マニュアルの Global OptionsGSSAPI Authenticated を参照のこと)

-z GZIPLEVEL

サーバとの通信で使用する圧縮のレベルを指定します。GZIPLEVEL 引数は 1 から 9 の数字です。レベル1は最小限の圧縮(とても速いですがあまり圧縮さ れません)、レベル9は最大限の圧縮です(CPU時間を食いますがデータは確実に 圧縮されます)。レベル9はネットワーク接続が非常に遅い場合にしか役に立た ないでしょう。通常は3から5あたりが妥当だと思われます。

-z と引数の間の空白はあってもなくてもかまいません。


Node:add, Next:, Previous:Global Options, Up:Commands And Options

add

Synopsis: add [OPTIONS] FILES

既存のプロジェクトにファイルを追加します。確認のためリポジトリに接続は しますが、そのファイルが実際にリポジトリに現れるのは commit が行われて からです。(removeimport も参照してください。)

オプション:


Node:admin, Next:, Previous:add, Up:Commands And Options

admin

Synopsis: admin [OPTIONS] [FILES]

このコマンドはさまざまな管理作業のためのインタフェースです。とりわけ、 キーワード展開のモードを変更したり、コミットしてしまったあとにログメッ セージを変更したりするような、リポジトリ内の個別のRCSファイルを対象と する作業を受け持ちます。

引数にファイルを指定しなければ admin は(カレントディレクトリから)再帰 的に動作するのですが、ファイル名を明示的に指定するのが普通です。あるプ ロジェクト内あるいはディレクトリ内のすべてのファイルに対して、1回の admin コマンドで意味のある作業はまず行えないでしょう。ですから、以下の 説明の中で「ファイル」とあったら、admin コマンドの引数として指定したフ ァイルのことを指していると考えてください。

リポジトリマシンのシステムに cvsadmin という名前のグループがあ る場合、そのグループのメンバーのみが admin コマンドを実行できます (cvs admin -k は例外で誰でも実行できます)。ですから、メンバー を持たない cvsadmin グループを作成すれば、すべてのユーザに admin コマ ンドを実行させないよう設定できます。

オプション:


Node:annotate, Next:, Previous:admin, Up:Commands And Options

annotate

Synopsis: annotate [OPTIONS] [FILES]

各ファイルの各行を誰がいつ変更したか、という情報を表示します。出力結果 の1行はそのファイルの1行に対応します。各行の内容を左から右へ順に言うと、 その行が変更された最新のリビジョン番号、その変更を行ったユーザと変更日 付を括弧でくくったもの、コロン、その行の内容、となります。

例を挙げると、あるファイルが次のようだったとすると

this is a test file
it only has too lines
I mean "two"

このファイルのアノテーションは次のようになります。

1.1          (jrandom  22-Aug-99): this is a test file
1.1          (jrandom  22-Aug-99): it only has too lines
1.2          (jrandom  22-Aug-99): I mean "two"

これを見ると、最初の2行は最初のリビジョンのままで、最後の行はリビジョ ン1.2で追加または変更された(jrandom による)ということがわかります。

Options:


Node:checkout, Next:, Previous:annotate, Up:Commands And Options

checkout

Synopsis: checkout [OPTIONS] PROJECT(S)

モジュールをリポジトリから作業コピーへチェックアウトする。作業コピーが 存在しない場合は生成し、既に存在する場合はアップデートします。 (updateもご参照ください。)

Options:


Node:commit, Next:, Previous:checkout, Up:Commands And Options

commit

Synopsis: commit [OPTIONS] [FILES]

作業コピーの変更をリポジトリへコミットします。

Options:


Node:diff, Next:, Previous:commit, Up:Commands And Options

diff

Synopsis: diff [OPTIONS] [FILES]

2つのリビジョン間の相違を表示します(Unix の diff 形式で)。オプションな しで起動した場合は、リポジトリのベースリビジョンと、(おそらくは未コミ ットの)作業コピーの内容を比較します。ベースリビジョンとは、その 作業コピーをリポジトリから取得した時点のリビジョンを指します。誰か他の 人が変更をコミットしたにもかかわらずこの作業コピーでアップデートを行っ ていない場合、リポジトリ中にはベースリビジョンより新しいリビジョンが存 在する、ということに注意してください。(rdiffも参照のこと)

Options:

Diff 互換オプション

今まで述べたオプションに加え、cvs diff は GNU バージョンの標準 diff プ ログラムと共通のオプションを指定できます。このようなオプションの全一覧 を、よく使われるものについては説明をつけて下記に示します。(その他につ いては GNU diff のドキュメントをご参照下さい。)

-0 -1 -2 -3 -4 -5 -6 -7 -8 -9
    --binary
    --brief
    --changed-group-format=ARG
    -c
      -C NLINES
      --context[=LINES]
    -e --ed
    -t --expand-tabs
    -f --forward-ed
    --horizon-lines=ARG
    --ifdef=ARG
    -w --ignore-all-space
    -B --ignore-blank-lines
    -i --ignore-case
    -I REGEXP
       --ignore-matching-lines=REGEXP
    -h
    -b --ignore-space-change
    -T --initial-tab
    -L LABEL
      --label=LABEL
    --left-column
    -d --minimal
    -N --new-file
    --new-line-format=ARG
    --old-line-format=ARG
    --paginate
    -n --rcs
    -s --report-identical-files
    -p
    --show-c-function
    -y --side-by-side
    -F REGEXP
    --show-function-line=REGEXP
    -H --speed-large-files
    --suppress-common-lines
    -a --text
    --unchanged-group-format=ARG
    -u
      -U NLINES
      --unified[=LINES]
    -V ARG
    -W COLUMNS
      --width=COLUMNS

下記は cvs diff でよく使用される GNU diff オプションです。


Node:edit, Next:, Previous:diff, Up:Commands And Options

edit

Synopsis: edit [OPTIONS] [FILES]

監視されているファイルの編集を始める合図をします。また、自分をそのファ イルの監視リストに一時監視者として加えます(cvs unedit した時点で監視リ ストから外れます)。(watch, watchers, unedit, editors 参照のこと)

Options:


Node:editors, Next:, Previous:edit, Up:Commands And Options

editors

Synopsis: editors [OPTIONS] [FILES]

監視中のファイルを現在誰が編集しているか表示します。(watch, watchers, edit, unedit を参照のこと。)

Options:


Node:export, Next:, Previous:editors, Up:Commands And Options

export

Synopsis: export [OPTIONS] PROJECT(S)

リポジトリからファイルを取り出して、プロジェクトのファイルツリー(作業 コピーではなく、CVS/ 管理サブディレクトリも存在しない)を作ります。主に ディストリビューションパッケージを作るために使われます。

Options:


Node:gserver, Next:, Previous:export, Up:Commands And Options

gserver

Synopsis: gserver

これは GSSAPI (Generic Security Services API) サーバです。このコマンド がユーザによって直接起動されることは通常ありません。ユーザが :gserver: アクセスメソッド経由のクライアントから接続しようとし た時点でサーバ側で起動されるものです。

cvs -d :gserver:floss.red-bean.com:/usr/local/newrepos checkout myproj

GSSAPI はケルベロスバージョン5用です。ケルベロスバージョン4には :kserver: を使用してください。

GSSAPI ライブラリの設定、使用についてはこの本の範囲の外です。 (Cederqvist マニュアルの GSSAPI Authenticated に有用なヒントが あります。)

Options: None.

history [OPTIONS] [FILENAME_SUBSTRING(S)]

リポジトリ内での活動履歴を表示します。具体的には、checkout, commit, rtags, update, release についての記録を表示します。このオプションはデ フォルトで checkout のみを表示します(ただし、-x オプションを参照してく ださい)。CVSROOT/history ファイルが存在しない場合このコマンドは動作し ません。

history コマンドは他の CVS コマンドとはいくつかの点で異なっています。 まず、何らかの意味のあることをしようとするとオプションを指定せねばなら ないという点(加えて、history のいくつかのオプションは他の CVS コマンド とは違う意味を持ちます)。次に、引数としてファイル名全体を指定するので はなく、ファイル名の一部にマッチする部分文字列を1つ以上指定する点(その 部分文字列のうち少なくとも1つにマッチする記録はすべて表示されます)。 3つめに、history の出力は読み方を覚えるまで雑音のようにしか見えない点 です。出力の形式については、オプションの説明のあとに特別な節を設けて解 説します。(logを参照のこと)

Options:

History 出力

history コマンドの出力は、行の連なったものです。各行が1つの「履歴イベ ント」を示し、イベントの種類を表わすアルファベットの1文字(コード文字) で始まります。例を示します:

floss$ cvs history -D yesterday -x TMO
M 08/21 20:19 +0000 jrandom 2.2              baar       myproj == <remote>
M 08/22 04:18 +0000 jrandom 1.2              README     myproj == <remote>
O 08/22 05:15 +0000 jrandom myproj =myproj= ~/src/*
M 08/22 05:33 +0000 jrandom 2.18             README.txt myproj == ~/src/myproj
O 08/22 14:25 CDT jrandom myproj =myproj= ~/src/*
O 08/22 14:26 CDT jrandom [99.08.23.19.26.03] myproj =myproj= ~/src/*
O 08/22 14:28 CDT jrandom [Exotic_Greetings-branch] myproj =myproj= ~/src/*

コード文字はさきほど説明した -x と同じです。コード文字のあとに、そのイ ベントの日時、そのイベントの責任者(ユーザ)、と続きます。

ユーザの次にリビジョン番号かタグか日時のうち、適切なものいずれかが表示 されます(上の例に見えるように、日時かタグの場合は角カッコで囲んであり ます)。コミットの場合は新しいリビジョン番号が表示されます。-D か -r で チェックアウトを行うと、スティッキー日付かタグが角カッコで囲まれて表示 されます。通常のチェックアウトでは何も表示されません。

次に対象のファイルの名前、モジュールについてのイベントならばモジュール 名が表示されます。前者の場合、その次の2つはモジュール/プロジェクト名、 そのユーザのホームディレクトリ内での作業コピーの場所、になります。後者 の場合はチェックアウトされた作業コピーのモジュール名(イコールで囲まれ ます)、そのユーザのホームディレクトリ内での作業コピーの場所、が続きま す。(-d オプションを使用した場合には、チェックアウトされた作業コピーの 名前はモジュールの名前とは違うものになります。)


Node:import, Next:, Previous:gserver, Up:Commands And Options

import

Synopsis: import [OPTIONS] REPOSITORY VENDOR_TAG RELEASE_TAG(S)

リポジトリに新しいソースをインポートします。新規のプロジェクトを生成す るか、または既存のプロジェクトのベンダブランチに新しいベンダリビジョン を生成します。(インポートにおけるベンダブランチの基本的な説明は Advanced CVS をご覧ください、以下の説明がわかりやすくなると思い ます)

一度にたくさんのファイルやディレクトリを追加するときや、新しいプロジェ クトを作成するときには通常 import を使います。ファイルを1つだけ追加し たい場合には add を使います。

Options:


Node:init, Next:, Previous:import, Up:Commands And Options

init

Synopsis: init NEW_REPOSITORY

新規のリポジトリ(いろいろなプロジェクトをおさめるためのルートリポジト リのことです)を作成します。大抵はグローバルオプション -d をつけて使 うと思います:

floss$ cvs -d /usr/local/yet_another_repository init

CVSROOT 環境変数を設定してあったとしても、それは多分既存のリポジトリを 指していると思われますし、もし実際そうだった場合、このコマンドに関して は意味がないですし危険でさえあります。(新規のリポジトリを初期化した後 の手順については Repository Administration をご覧ください。)

Options: None.


Node:kserver, Next:, Previous:init, Up:Commands And Options

kserver

Synopsis: kserver

これはケルベロスサーバです。(ケルベロスライブラリがバージョン4以下の場 合に使います。バージョン5では GSSAPI を使います、gserver を参照 のこと。) このコマンドは通常ユーザが直接起動したりはせず、クライアント 側から :kserver: メソッドでユーザが接続してきたときに、サーバ側 で起動されます。

cvs -d :kserver:floss.red-bean.com:/usr/local/newrepos checkout myproj

ケルベロスの設定と使用法についてはこの本の範囲外です。(Cederqvist マニ ュアルの Kerberos Authenticated ノードに役に立つヒントがあると 思います。)

Options: None.


Node:log, Next:, Previous:kserver, Up:Commands And Options

log

Synopsis: log [OPTIONS] [FILES]

あるプロジェクトについて、またはプロジェクト中のファイルについてのログ メッセージを出力します。log コマンドの出力は、古い RCS プログラム(rlog) をもとにしているので、他の CVS コマンドの出力形式とは少々違います。ま ずヘッダとして、そのファイル自体についてリビジョンに関係のない情報を出 力し、次にログメッセージを出力します(リビジョン順)。各リビジョン部分は リビジョン番号とログメッセージに加え、著者(変更したユーザ)、変更日時、 追加・削除された行数からなります。時刻は地方時ではなくすべて UTC (GMT) で表示されます。

log コマンドの出力はファイル単位なので、複数ファイルを含む1回のコミッ トについて、そのまとまりが1回の変更であるというようなことは直接表示さ れません。しかし、ログメッセージと日時をきちんと読めば、何が起こったか を再構成できると思います。(複数のログ出力を読みやすい形に整形し直すツ ールについて、詳しくは Third-Party Toolscvs2cl - Generate GNU-Style ChangeLogs を参照してください。) (historyも参照のこと)

Options:

以下のフィルタリングオプションをすべて読んでも、組み合わせたときにどの ような動作をするかはわからないと思います。正確に言うと、log コマンドの 動作は次のようになります。-d, -s, -w 各々で選択されたリビジョンの集合 の共通部分(intersection)と、-b, -r 各々で選択されたリビジョンの集合を 合わせたもの(union)の、共通部分(intersection)が対象になります。


Node:login, Next:, Previous:log, Up:Commands And Options

login

Synopsis: login

CVS サーバに接続し、特定のリポジトリについて認証情報を確認します。この コマンドは作業コピーにもリポジトリにも影響を及ぼしません。あるリポジト リについてパスワードを確認し、以後のためにホームディレクトリの.cvspass ファイルにパスワードを保存するだけです。これ以降、同じユーザ名で同じリ ポジトリにアクセスするコマンドを使用しても、login を再度要求されること はありません。クライアント側の CVS は .cvspass ファイルからパスワード を取ってくるからです。

このコマンドを使うときは、pserver 接続方法を使用してリポジトリを指定し てください。こんな感じです:

floss$ cvs -d :pserver:jrandom@floss.red-bean.com:/usr/local/newrepos

または CVSROOT 環境変数を設定しておいてください。

サーバ側のパスワードを変更したら、再度 login を実行してください。

Options: None.


Node:logout, Next:, Previous:login, Up:Commands And Options

logout

Synopsis: logout

login の反対です。このリポジトリのパスワードを .cvspass から削除します。

Options: None.


Node:pserver, Next:, Previous:logout, Up:Commands And Options

pserver

Synopsis: pserver

これはパスワード認証サーバです。このコマンドは通常ユーザが直接起動した りはせず、クライアント側から :pserver: メソッドでユーザが接続し てきたときに、サーバ側で /etc/inetd.conf から起動されます。 (login, logout コマンドや、この章の Run Control Files 節の .cvspass ファイルについてを参照のこと。パスワード認証のCVS サーバを設定する際の詳細については Repository Administration を 参照のこと。)

Options: None.


Node:rdiff, Next:, Previous:pserver, Up:Commands And Options

rdiff

Synopsis: rdiff [OPTIONS] PROJECTS

diff コマンドと同様ですが、直接リポジトリで動きますので作業コピーが必 要ありません。このコマンドでは、あるリリースから別のリリースまでの間の 相違を、patch プログラムへの入力に使える形式で取得できます(アップグレ ードを行いたいユーザ用のパッチファイルを配布することができます)。

patch プログラムの操作はこの本の範囲外です。が、パッチファイルにサブデ ィレクトリ中のファイルの diff も含まれている場合、違いを正しく適用する ためには patch に -p オプションをつける必要があるかもしれないことに注 意してください。(詳しくは patch のドキュメントを参照してください。) (diff も参照のこと)

Options:


Node:release, Next:, Previous:rdiff, Up:Commands And Options

release

Synopsis: release [OPTIONS] DIRECTORY

チェックアウトを取り消します(作業コピーがもう使用されていないと表示し ます)。CVS コマンドの多くが作業コピーで実行されるのに対し、このコマン ドは作業コピー内ではなく、その直上のディレクトリ(親ディレクトリ)で実行 します。作業コピーからリポジトリの場所の情報を得ることができませんので、 CVSROOT 環境変数をセットするか、-d グローバルオプションでリポジトリを 指定する必要があります。

リリースは必ずしも使用する必要はありません。CVS はロックをかけませんの で作業コピーをただ削除するだけでもよいのです。

しかし、作業コピー中に未コミットの変更があったり、CVSROOT/history ファ イルに作業の停止を記録したい場合には release を使うべきです。CVS はま ず未コミットの変更がないかどうかをチェックします。もしあればその旨警告 し、続けてよいかどうか訊ねます。作業コピーが実際にリリースされたら、 リポジトリの CVSROOT/history ファイルにそのことが記録されます。

Options:

作業コピー中に新しいディレクトリを作り、それをリポジトリに追加しなかっ た場合に -d フラグを指定すると、作業コピーと一緒にそのディレクトリも削 除されてしまいます。


Node:remove, Next:, Previous:release, Up:Commands And Options

remove

Synopsis: remove [OPTIONS] [FILES]

プロジェクトからファイルを削除します。通常、このコマンドを起動したとき に、そのファイルそのものがディスクから削除されます(ただし -f を参照の こと)。このコマンドはデフォルトで再帰的に動作しますが、削除するファイ ルの名前を明示的に指定するのが普通です。Note the odd implication of the previous sentence: 普通は自分の作業コピー中にもう存在しないファイ ルに対し cvs remove を実行します。 Removes a file from a project. Normally, the file itself is removed from disk when you invoke this command (but see -f). Although this command operates recursively by default, it is common to explicitly name the files being removed. Note the odd implication of the previous sentence: Usually, you run cvs remove on files that don't exist anymore in your working copy.

確認のためにリポジトリに接続しますが、この後に commit が実行されるまで そのファイルは実際に削除されたりはしません。また、RCS ファイルはリポジ トリから本当に削除されるわけではありません。トランクから削除された場合、 Attic/ サブディレクトリに移動するだけで、ブランチ上ではまだ存在します。 ブランチから削除された場合は、場所は変わりませんが状態が dead の新しい リビジョンがブランチ上に追加されます。(add を参照のこと)

Options:


Node:rtag, Next:, Previous:remove, Up:Commands And Options

rtag

Synopsis: rtag [OPTIONS] TAG PROJECT(S)

リポジトリ内のモジュールに直接タグをつけます(作業コピーは不要です)。 このコマンドを動作させるためには、CVSROOT 環境変数を指定するか、-d グ ローバルオプションを指定する必要があると思います。(tag を参照の こと)

Options:


Node:server, Next:, Previous:rtag, Up:Commands And Options

server

Synopsis: server

CVS サーバを起動します。このコマンドをユーザが起動することはありません (クライアント/サーバプロトコルをデバッグするような時以外は)。ですから これについて触れたことは忘れてください。

Options: None.


Node:status, Next:, Previous:server, Up:Commands And Options

status

Synopsis: status [OPTIONS] [FILES]

作業コピー中のファイルの状態を表示します。

Options:


Node:tag, Next:, Previous:status, Up:Commands And Options

tag

Synopsis: tag [OPTIONS] TAG [FILES]

プロジェクトの特定のリビジョンまたはリビジョンの集合に名前をつけます。 プロジェクトの「スナップショットを取る」とも言います。このコマンドは CVS のブランチを作成するときにも使用されます。(-b オプションを参照のこ と。rtag も)

Options:


Node:unedit, Next:, Previous:tag, Up:Commands And Options

unedit

Synopsis: unedit [OPTIONS] [FILES]

監視者に対し、ファイルの編集が終わったことを合図します。 (watch, watchers, edit, editors を参照のこと)

Options:


Node:update, Next:, Previous:unedit, Up:Commands And Options

update

Synopsis: update [OPTIONS] [FILES]

リポジトリの変更を自分の作業コピーへマージします。副作用として、自分の 作業コピー中で変更のあるファイルを表示します(但し、-Q グローバルオプシ ョンが指定されている場合はこの表示はありません)。(checkout も参 照のこと)

Options:


Node:watch, Next:, Previous:update, Up:Commands And Options

watch

Synopsis: watch on|off|add|remove [OPTIONS] [FILES]

1つ以上のファイルを監視するよう設定します。他の CVS コマンドとは違い、 watch で意味のあることをしようとすると、さらにもうひとつサブコマンドを 指定する必要があります。(watchers, edit, editors, unedit, users を参照のこと)

Subcommands:

オプション(すべての watch サブコマンドに使用できるものです)。オプショ ンは3つあって、すべて edit のオプションと同様の意味です:


Node:watchers, Previous:watch, Up:Commands And Options

watchers

Synopsis: watchers [OPTIONS] [FILES]

どのファイルを誰が監視しているかを表示します。

Options - これらは edit の同じオプションと同様の意味です:


Node:Keyword Substitution (RCS Keywords), Next:, Previous:Commands And Options, Up:CVS Reference

Keyword Substitution (RCS Keywords)

CVS はファイル中のテキスト置換を行うことができ、ファイル中のある種の情 報を自動的に最新に保てます。置換はすべてドルマークで括った特定のキーワ ードパターンにより起動されます。All of the substitutions are triggered by a certain keyword pattern, surrounded by dollar signs. たとえばファ イル中の

$Revision$

は、こんな風に展開されます:

$Revision: 1.5 $

新しいリビジョンがコミットされても CVS はリビジョン文字列を最新のもの に保ち続けます。


Node:Controlling Keyword Expansion, Next:, Up:Keyword Substitution (RCS Keywords)

Controlling Keyword Expansion

CVS はデフォルトでキーワード展開を行い、止めるよう指示しなければ止めま せん。あるファイルのキーワード展開をずっとやめようと思えば、そのファイ ルをプロジェクトに追加する際に -k オプション付きにします。追加したあと でやめようと思えば admin コマンドを -k オプション付きで実行します。キ ーワード制御のモードはいくつかあって、-k オプションで指定できます。通 常は o か b モードを使用します。たとえば

floss$ cvs add -ko chapter-9.sgml

このコマンドは chapter-9.sgml というファイルを、キーワード展開 なしでプロジェクトに追加します。ファイルのデフォルトキーワード展開モー ドを o 、つまり展開なしのモードにセットしています。(実際は「o」 というのは「古い」(old)の略で、文字列を古い値、つまりその文字列自体と 置換する、という意味です。結果として何も変わらないことになります。 I'm sure this logic made sense to somebody at the time.)

各ファイルのデフォルトキーワード展開モードはリポジトリに保存してありま す。が、各作業コピーごとにその作業コピーローカルのキーワード展開モード を設定することも可能です。チェックアウトかアップデートの際に -k オプシ ョンをつければ設定できます。コマンド1回を実行する間だけモードを有効に したい場合には diff コマンドの -k オプションを使えます。

指定できるモードを、その前に -k オプションをつけた形で(コマンドライン でタイプするような形で)ここに一覧します。これらのオプションはキーワー ド展開モードとして、デフォルトでもローカルでも使用できます。


Node:List Of Keywords, Previous:Controlling Keyword Expansion, Up:Keyword Substitution (RCS Keywords)

List Of Keywords

CVS が解釈する、ドルマークで囲まれたキーワードの一覧を記します。キーワ ード、短い説明、展開後の例の一覧です: