▼CVSのタグと枝を理解する▼

[CVS超入門] [タグと枝とひっつきと]

$Id: cvsbranch.html,v 2.11 2013-02-26 22:41:51+09 kabe Exp $


点タグと枝タグ

CVSで「タグ」と呼ばれている物には2種類あります。

点タグ

注意:「点タグ」てのはここだけの用語です。 一般には通じないので注意。

点タグは、静的な版に対応します。

【例】ある時点で cvs tag REL_1_0 と点タグを打つと、 その後 -rREL_1_0 でcheckoutしたりすれば、 いつでも常に同じものがとり出されます。

点タグは、普通は cvs tag POINT_TAG で打ちます。

ファイルが異なれば、同じタグ名でも RCS版番は違うことがあります。

枝タグ

横文字の文献では "branch tag" と書かれているもの。

枝タグは、一定のRCS版番は指さず、その時点での枝の先端 (成長点) の版を指します。 具体的には、枝タグ自体は 1.5.2 といった数字が奇数個の版番に割り当てられ、 checkout時などに自動的に先端を指すように最後の数字が補完されます。

【例】main.c の RCS枝 1.5.2.x に対し、REL_1_0_1 と枝タグがついていれば、 -rREL_1_0_1 などはその時点でリポジトリに入っている 1.5.2.x の 最新版 (たとえば 1.5.2.6) に対する操作になります。

枝タグを「打つ」、というより「生やす」には、 cvs tag -b BRANCH_TAG を使います。

成長させられるのは、枝だけである

cvs commit にて伸ばすことができるのは枝の成長点だけである、 てことが重要です。

幹の1.x 系列は最初から用意された枝、と解釈します。 従って、枝を全然生やさなくても 1.x 系列に関しては最初から commit が 可能になっています。

枝や幹の途中からバージョンを分岐させるには、 いったん cvs tag -b で枝(成長点)をこしらえてから、 そこへ commit します。

点か枝かは、すぐにはわからない

与えられたタグ名が、点タグなのか枝タグなのかを判定する 一般的な手段はありません(ええ〜)。

現実的には、どの版にも含まれていそうなファイル (READMEとか)に対し、 cvs log -r0 README で出てくる RCSタグから予測をつけます。 1.5.0.4 といった 0のつく版番(magic branch)についているタグが CVSの枝タグ。

ので、タグを打つ際は点・枝とも、人間が適当なメモを残しておく 必要があります。どのみちタグ名にはRCS版番のような構造がないので、 各タグの相関関係は人間が 記録として残しておかないと、 あとで必ず混乱します。

	   ---------------------------------[EXP_HOGE_HEAD]---* (MOD_HOGE)
	  /                /                         \
	--- [REL_1_0] --[EXP_HOGE]--- [REL_1_1] ----[MERGE_HOGE]---* (HEAD)
	       \
	        ---[REL_1_0_1]----[REL_1_0_2]----* (REL_1_0_x)

特に各枝相互のマージ記録は CVS は 全く 関知しないので、 複雑な枝を生やしていくと地図や命名方針は必須です。 混乱しやすいので、「枝は早めに幹と合流させろ」が 枝に対する一般的なアドバイスです。

いつタグを打つのが良いか

枝タグを生やしたときに、根元に点タグを打つ。
% cvs tag BRANCH_POINT	# 根元に点タグを打ってから
% cvs tag -b BRANCH	# 枝を生やす
分岐点からの変更分だけ知りたい、分岐点まで戻りたい、 といった場合に根元のタグが必要です。 checkout -jは作業領域との共通の分岐点しか 探してくれないので、頼れるのは一度だけです。

別の枝や幹にマージ作業を行ったとき、取込元の枝に点タグを打つ。
枝での変更を何度か継続して取り込む 場合には、 枝側に「ここまでは取込済」というしるしが無いと2回目以降に 往生します。 取込先ではなく取込に打つことに注意。


要するにsticky tagて何なのよ

ひっつきタグ (sticky tag) が何なのか、 つーより、それが CVS の動作にどう影響するのかという 帰納的説明のほうが妥当かと。

ひっつきタグと作業ファイル

・ひっつきタグは、作業領域にあるファイル毎に割り当てられています。 (作業領域のディレクトリに割り当てられている、のではない。) 確認するには、cvs stat fileSticky Tag: の 部分を見る。

ひっつきタグと commit

・枝タグにひっついているファイルは、その枝(の成長点)に対してだけ commitが可能です。 他の枝にcommitするには、いったん cvs update で他の枝に移る (ひっつけ直す)必要があります。

・点タグにひっついているファイルはcommitできません。 作業領域内でいじくる分には自由ですが、 commitで変更を登録することはできません。

タグのひっつけかた

・cvs checkout -rTAG でタグを明示して取り出した 作業領域(の各ファイル)は、TAG にひっついています。

・cvs update -rTAG にて作業領域を 別のタグに移動させると、その新しい TAG にひっつきます。 (作業領域内ローカルの変更は保存されます)

タグの剥がし方

・幹(1.x)には明示的なタグがありません。 幹に属するファイルは「タグが剥がれて」います。

・実際には、幹1.xという枝にひっついている、と解釈します。 従ってこれを commit すると幹1.xが伸びます。 任意の枝にcommitできる、わけではないことに注意!

cvs update -A とすると、 作業領域は幹の最先端に移動し、タグが全部剥がれます。 単にタグが剥がれるのではなく、 作業領域内で変更しなかったものは 幹の最新版に置換されることに注意。

論理的には update -rHEAD と update -A は同じはずなのだが、 歴史的事由からか -rHEAD は "HEAD" というタグにひっついてしまう。 やってはいけない。 -rHEAD と書けないから -A がある、という解釈でいいと思う。
実際には -rHEAD は「枝の先端」なので、 枝にひっついていると -rHEAD と -A (幹の先端) は異なる。 (TRUNKという仮想枝はなぜか用意されていない。)

ひっつきタグの確認

	% cvs status file

ひっつきタグは作業領域毎ではなくファイル毎なので、 作業領域のタグを確認する際は 手頃なファイルを指定してcvs status で確認します。

同じ作業領域内に違うタグにひっついたファイルが混ざっている状態も可能。 作業領域丸ごとではなく、ファイルを指定して違うタグで checkout -r すればそういう状態になりますが、 混乱必至なので普通は絶対やらない。
(用途がないわけではない。 リリース版には含めたくないファイルとか)


sticky tagに邪魔されてcommitできない!

% cvs commit
cvs commit: Examining .
cvs commit: sticky tag `REL_1_0' for file `Makefile' is not a branch
cvs [commit aborted]: correct above errors first!

REL_1_0 版をちょっと修正しようと、 cvs checkout -rREL_1_0 で作業領域を作り、修正してcommitすると 怒られます。これは、

	% cvs stat Makefile
	===================================================================
	File: Makefile          Status: Locally Modified

	   Working revision:    1.5     Tue Dec 24 22:41:53 2002
	   Repository revision: 1.5     /autofs/home/kabe/cvsroot/rika/Makefile,v
	   Sticky Tag:          REL_1_0 (revision: 1.5)
	   Sticky Date:         (none)
	   Sticky Options:      (none)
タグ名を見ただけでは点なのか枝なのかはわかりませんが、 cvs status にて revision: が偶数個の数字の版番にひっついていれば点タグです。

-rREL_1_0 で取り出したファイルが毎回異なるようでは困るので、 点タグで取り出して点タグにひっついているファイルは 変更 (commit) ができないようになっているわけです。 あなたが困らなくても、REL_1_0 を後から参照したい人が困る。

commitしたいよう

解決法の1つは、枝を新しく生やし、枝タグにひっつけ直すことです。

FAQ類では単に 「"cvs update -A" で解除すればよい」と 書かれていることが多いのですが、 わけもわからず解除すりゃいいってもんでもないです。 大抵は意図と違う。

◇新しい成長点を作る

まず、元となる版から枝を生やします。

	## 枝 REL_1_0_x を生やす(-b)
	## 枝の根本は、指定がなければ現在の作業領域がひっついている所  REL_1_0)

	% cvs tag [-rREL_1_0] -b REL_1_0_x
	cvs tag: Tagging .
	T Makefile
                        [REL_1_0]
	               /
	--- 1.4 --- 1.5 --- 1.6 --* (HEAD)
	               *
	                (REL_1_0_x)

	* == 枝の先端(成長点)
cvs tag 系のコマンドは作業領域ではなくリポジトリに対して 操作を行うので、 作業領域のファイル (例ではMakefile) がいじくられていても、 この段階では関知しません。

◇作業領域のタグを枝に貼り替える

作業領域を新しくつくった枝 (REL_1_0_x) にひっつけ直します。 点タグのままだといつまでたってもcommitできない。

ひっつきタグの貼り替えには、必ず cvs update を使います。 これ以外のコマンドでひっつきタグが変わることはありません。 枝を新しく生やしたばかりの時は、点タグ REL_1_0 も枝タグ REL_1_0_x も 同じ版番 (1.5) を指しているので、update -rREL_1_0_x をしても 作業領域内のファイルは変更されません。 (ローカルで行った変更は保存されるので安心せよ)

	% cvs update -rREL_1_0_x
	cvs update: Updating .
	M Makefile		ローカル変更があると"M"と表示される

確認のため、タグが貼り替わったかを cvs status で調べます。

	% cvs status Makefile
	===================================================================
	File: Makefile          Status: Locally Modified

	   Working revision:    1.5     Tue Dec 24 22:41:53 2002
	   Repository revision: 1.5     /autofs/home/kabe/cvsroot/rika/Makefile,v
	   Sticky Tag:          REL_1_0_x (branch: 1.5.2)
	   Sticky Date:         (none)
	   Sticky Options:      (none)
枝タグであれば branch: 部分が枝タグの版番(数字が奇数個) になっているはずです。

◇新しい枝に commit

んで commit します。上ではMakefileに関しては枝として 1.5.2 が割り当てられたので、 新しい版は 1.5.2.1 となります。 (違うファイルでは違う番号になることもある)

	% cvs commit
	cvs commit: Examining .
	Checking in Makefile;
	/autofs/home/kabe/cvsroot/rika/Makefile,v  <--  Makefile
	new revision: 1.5.2.1; previous revision: 1.5
	done
                        [REL_1_0]
	               /
	--- 1.4 --- 1.5 --- 1.6 --* (HEAD)
	               \
	                1.5.2.1 --* (REL_1_0_x)

そうぢゃなくて、REL_1_0 自体を修正したいんだが

…やめたほうがいいです。 他の人が -rREL_1_0 を参照した際に 不整合が出たりして困ります。 あきらめて known bugs に書き足しましょう。

つまり時期によって REL_1_0 の版番が違っている、が欲しかったのであれば タグ REL_1_0 は点タグではなく枝タグとして打つべきだったと いうことになります。

点タグを枝タグに変えたりするのは(可能ですが)むっちゃめんどくさいので、 ここは素直に反省して別の名前の枝を生やしましょう。

どうしても点タグのつけかえだけがしたい場合は 一応 cvs admin で直接 RCS管理ファイルをいじくることで可能ではありますが…

そうぢゃなくて、最新版を REL_1_0 時代のものに戻したいんだが

であれば HEAD の作業領域にて、古い版のもので上書き後 commit します。 普通の checkout や update ではタグがひっついてきてよろしくないので、 ここでは -j か -p (標準出力に出す) を 使う、ようです。

	% cvs update -A		(HEADへ移動)
	% cvs update -rREL_1_0 -p Makefile > Makefile
RCS みたいに -p1.5 とはできないので注意。


仮想タグ

CVSには、この他に HEAD と BASE という 仮想タグが用意されています。 仮想というのは、 -r の引数には使えるが、 明示的に打たれているタグではない(リポジトリ中には残らない)ということ。

通常の点タグや枝タグは誰が参照しても同じ版や枝を指しますが、 BASE と HEAD が指す具体的な版は作業領域によって違うことがあります。

普通は BASE と HEAD は同じですが、 誰かが自分と同じ枝に新しい版を commit すると HEAD だけが先へ進みます。

	【例】 1.5.2 枝にひっついて作業中。
	       自分が checkout して修正しているのは 1.5.2.1 だが、
	       誰かが新しい版を同じ枝に commit した

	--- 1.4 --- 1.5 --- 1.6 --*
	               \
	                1.5.2.1 -- 1.5.2.2 [HEAD]
	                [BASE] \
	                        (作業領域にある修正版)

注意: 「自分の作業領域に転がっている修正ファイル」を指す 明示的なタグは存在しません。 タグはリポジトリに登録されている版しか指せないため。

わかりにくいからと cvs tag などで "HEAD" "BASE" といったタグを明示的に打つと CVS が発狂するので絶対やめましょう。

「幹」を指す明示的な仮想タグはなぜか用意されていません。 自分で作れんこともないですが。 (update -A があるので実際にはあまり困らないらしい)

開発者間のメールとかで単に 「HEAD版が…」 と言っている場合は 大抵 幹の 先端のことを指しています。が、 枝にひっついている作業領域では -rHEAD は 枝の先端であって幹ではありません。注意。


タグ地図を書こう

CVS ではタグ間の関係や、タグ全部の一覧というものを 管理していません (RCSと同じ)。 従って、

といった事項は、ぜんぶ 人間が管理する必要があります

ので、タグを打つようになったらタグ地図を 自分で作りましょう。 「最初は簡単だからいいや」とサボっていると必ず後で 後悔するので、最初から作っておくこと。 (他のプロジェクトではあまり見掛けないのはなぜ?)

他人の地図を見たことがないのでアレですが、 私は以下のよーな地図をテキストで書いて使っています。 (HEADがほとんど伸びてないというちょっと特殊な例)

リスト.タグ地図の例
CVS tag map
GRUB093	(unbranched 1.1 version)
	|	+---- GRUB 	vendor branch
	|		|<  GRUB_CVS_20030220	vendor cvs import
	|
	\-----QNX_PKG branch for QNX things
	|	|
	HEAD	|
		|<  QNX_PRE_030220_MERGE
		|	stamp before 2003/02/20 vendor cvs merging
		/--merged GRUB_CVS_20030220 official cvs head
		|
		|< (stage2/) QNX4FS_ROOT branchpoint
		\-----	QNX4FS
		|	|	-DFSYS_QNX4=1 added manually in stage2/Makefile
		|	|
		|	: merge to QNX_PKG 
		|		TODO: Makefile.in
		|
		|< QNX_PRE_QNX4FS
		/-- merged QNX4FS branch
		|	Now primary difference of -rGRUB and -rQNX_PKG
		|	is qnx4 filesystem.

タグ一覧は ないわけでもない

実は $CVSROOT/CVSROOT/val-tags ファイルに、 使ったことのあるタグの一覧のようなものはあります。

	% more ~/cvsroot/CVSROOT/val-tags
	FREEWNN y
	FREEWNN_1_10_PL020 y
	TAGMAP y
ただ、枝かどうかはわかりません。 リポジトリ毎のファイルなので、 複数のプロジェクト($CVSROOT/のサブディレクトリのこと)を収容しているCVSROOTだと 全部ごっちゃになっているので、どのプロジェクト用のタグかもわかりません。

Next: 枝の併合


かべ@sra-tohoku.co.jp