ノード:RCS Format, 次:, 前:Repository Structure, 上: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 してやる必要があると思います。 リポジトリのパーミッションをどう構成するかについて、難しい固定した規則 はありません、単にどのプロジェクトで誰が作業しているかによります。