cvs-checkout-d - CVS の "checkout -d" と module の関係

目的

CVS の checkout コマンドに -d を付けた時の挙動を理解する。

結果

意味のある結果は出なかったが、強いていえば、CVS modules の設定でおかしな(自己参照するような)ことはやらない方が良い、ということだろうか。

以下、詳細。

"-d" オプション付きでのチェックアウト

とある CVS リポジトリを、モジュール名とは別のディレクトリにチェックアウトしようとして以下のように実行したところ、エラーで途中終了した。

% cvs co -d tmp foo
# => exit 1

"-d tmp" を付けない場合は正常にチェックアウトできるのだが、なぜだろう?

現象が再現するリポジトリを作る

問題のリポジトリを元に、現象が再現する最小の構成を作った。

モジュールの構成
foo/foo.c
foo/bar/bar.c
foo/baz/baz.c

ファイルが3つ、うち2つはそれぞれサブディレクトリの中にあるという構成。

CVSROOT/modulesの設定
__foo_alias     -a      foo/foo.c
__bar_alias     -a      foo/bar
__baz_alias     -a      foo/baz
foo_alias       -a      __foo_alias __bar_alias __baz_alias

foo は foo 自身の中にある2つのディレクトリをaliasとして含んでいる。

(このmodules設定が変則的と言われればその通りだが…作ったのが私ではないので、なんとも答えられない)

checkout 時の動作

このモジュールをcheckoutした時に、以下のエラーのエラーが発生した。

% cvs -d ~/spikelet/repos/cvsroot co -d tmp foo
U tmp/bar.c
cvs checkout: existing repository /home/taiyo/spikelet/repos/cvsroot/foo does not match /home/taiyo/spikelet/repos/cvsroot/foo/bar
cvs checkout: ignoring module foo/bar
cvs checkout: existing repository /home/taiyo/spikelet/repos/cvsroot/foo does not match /home/taiyo/spikelet/repos/cvsroot/foo/baz
cvs checkout: ignoring module foo/baz
# => exit 1

なお、RHEL4のCVS(1.11.17)と、最新stable(1.11.23)で試しても現象は同じ。

考察

CVS の info に、以下の記述がある("Node: checkout options")。

`-d DIR'
     Create a directory called DIR for the working files, instead of
     using the module name.  In general, using this flag is equivalent
     to using `mkdir DIR; cd DIR' followed by the checkout command
     without the `-d' flag.

     There is an important exception, however.  It is very convenient
     when checking out a single item to have the output appear in a
     directory that doesn't contain empty intermediate directories.  In
     this case _only_, CVS tries to "shorten" pathnames to avoid those
     empty directories.

超訳すると、こんな感じか:

checkout コマンド の "-d DIR" オプションは、module 名の代わりに DIR にワーキングファイルを置く。
単一のアイテムを、中間ディレクトリ無しで checkout するのに便利。

おそらく、以下のような処理になっているのではないだろうか。

  1. __foo_alias のチェックアウト
  2. この時、リポジトリパスの foo -> tmp の置換え実施
  3. __bar_alias のチェックアウト
  4. この時、リポジトリパスの foo/bar を置き換えようとするが、既に置き換えられており、foo/bar が見つからない
  5. エラーメッセージを表示
  6. (foo/bazについても同様にパスが見つからず、エラー)

対策

このmoduleについて、"-d" オプション有りでも無しでも正しくチェックアウトできるmodule設定が無いか、試してみる。

総当たり戦を試してみる

CVSのmodulesは、alias / regular / ampersand の3種類がある。
この3種類それぞれに対して、fooモジュール本体の定義と、各subdir(bar,baz)を変化させて、チェックアウト時の動作を観察する。

foo本体\各subdir regular alias ampersand
regular ※ / ※ ※ / ※ ※ / ※
alias △ / × ○ / × △ / △
ampersand △ / △ △ / △ △ / △

( '/' の前後は、"-d"オプションありと、オプション無し の結果)
なお、表中の記号の意味は下記の通り:

  • ※ = CVS Error (there is no repository ...)
  • × = CVS Error (existing repository ... does not match ...)
  • △ = checkout result differs from expected
  • ○ = checkout reseult matches expected

この結果から、望みがありそう(CVSが処理できそう)なのは、以下の△マークの箇所のみ:

*foo本体\各subdir *regular *alias *ampersand
regular × × ×
alias × × △(1)
ampersand △(2) △(3) △(4)
成功する可能性がありそうなものについて、詳細検討

上の表で△がついたものについて、期待するチェックアウト結果と実際の差を整理すると、以下のようだった:

# foo本体/各subdir "-d"オプション無し "-d"オプション有り
1 alias / ampersand foo/bar -> __bar_amper/foo/bar tmp/bar -> tmp/foo/bar
2 ampersand / regular foo/bar -> foo_amper_regular/__bar_regular tmp/bar -> tmp/__bar_regular
3 ampersand / alias foo/bar -> foo_amper_alias/foo/bar tmp/bar -> tmp/foo/bar
4 ampersand / ampersand foo/bar -> foo_amper_amper/__bar_amper/foo/bar tmp/bar -> tmp/__bar_amper/foo/bar

表から、"-d" オプション有り/無しどちらの場合も、余計なディレクトリ名が付いてしまうことがわかる。

ここで、CVS modules の ampersand module の定義を見ると(info cvs の Node: Ampersand modules)、

Ampersand modules
-----------------

   A module definition can refer to other modules by including
`&MODULE' in its definition.
     MNAME [ options ] &MODULE...

   Then getting the module creates a subdirectory for each such module,
in the directory containing the module.  For example, if modules
contains

     ampermod &first-dir

then a checkout will create an `ampermod' directory which contains a
directory called `first-dir', which in turns contains all the
directories and files which live there.  For example, the command

ampersandモジュールは、必ずディレクトリが付けられることになっている。

すなわち、上記の表で、"foo/bar" に対して2つ以上のディレクトリが付いているものは、それ以上ディレクトリを削除できないので、どうやっても差分が出る。
これに該当しないのは #2(本体:ampersand、各subdir:regular) のみ。

これをヒントに、なんとかならないかを試してみる。

foo本体:ampersand、各subdir:regular な modules 設定

下記の設定をmodulesに書いて試してみた。

bar     -d bar foo/bar
baz     -d baz foo/baz
foo     -d foo &baz &bar

"-d"オプション無し

% cvs -d /home/taiyo/spikelet/cvs/cvsroot co foo
U baz/baz.c
U bar/bar.c
% find . -name CVS -prune -o -type f -name '[a-z_]*[^~]' -print
./foo/bar/bar.c
./foo/baz/baz.c

"-d"オプション有り

cvs -d /home/taiyo/spikelet/cvs/cvsroot co -d tmp foo
U baz/baz.c
U bar/bar.c
% find . -name CVS -prune -o -type f -name '[a-z_]*[^~]' -print
./tmp/bar/bar.c
./tmp/baz/baz.c

ひとまず、これで希望の設定はできたようだ。

つまり、以下のような設定をmodulesに書き込む:

  • 各subdir は、出力先dirを指定して、subdir と同じ名前の regular module として定義する
  • fooモジュール本体は、出力先dirを指定して、各subdirを ampersand module として含める
fooモジュール直下のファイルについては?

先程の例では、foo直下のファイルについて含めていなかった。
というのも、先程と同じ方法では、foo/foo.c がどうやってもディレクトリ付きで(foo/foo/foo.cなどと)チェックアウトされてしまう。
これを回避するには、foo.c の定義を alias module でするしか無いのだが、CVS の modules では ampersand module と alias module を混在させることができない。

なので、fooモジュール直下のファイルについては、希望のチェックアウトを実現させる設定は無い、ようだ。

まとめ

長い(時間も、文章も)わりに、意味があまり無く、結果も面白くない投稿になってしまった。
もっと有意義な事に時間を使いたい…