RCS 中の上級講座

-- you will eventually want to do this.

[RCS超入門] [RCS初級講座] [RCS中級講座] [RCS 中の上級講座] [RCS邪級講座]

そう簡単に「オレは上級者だぜすごいだろへへん」と思っちゃあいけません。

$Id: rcs4.html,v 1.30 2010-07-21 12:49:53+09 kabe Exp $


ファイルの更新時刻を変えずに登録

単に ci -u とした場合は

なので、いじくっていると更新時刻が一貫せず *.html なんかでは ちょっと不都合な場合もあります。

	% ci -u -d -M filename
とすれば というのができます。更新時刻が変わらないので便利。

便利なので RCSINIT に "-M" を突っ込んでおきたくなりますが、 rcsdiff なんかが文句を言うのでダメです。 毎回手で指定するかaliasしましょう。

標準出力へ取り出す

単に diff をとるだけなら rcsdiff で十分なんですが、

といった場合に備えてか、 標準出力に吐き出す というオプションがあります。

index.html の第1.2版 を index.html.old として吐き出す:

% co -p1.2 index.html > index.html.old

JIS から EUC に変換してしまったので、 rcsdiff が役に立たなくなった:

% co -p manual.txt | nkf -e | diff - manual.txt
(単なるインデントの変更は rcsdiff -w で何とかなります)
バージョン番号を明示しなければ、デフォルト枝(通常は幹; x.x)の 最新版が使われます。

いじりたいのだが、 今 co されているファイルはそのままで:

% co -p -l script.cgi > tmp.cgi
(rcs -l; co -p の省略形)

これで取り出したファイルは内容こそ普通に co -l したのと同じですが、 mode は たいてい -rw-r--r-- となるので注意。 修正後に ci する方法がよくわかんなかったりするんですが、たぶん →別の名前の作業ファイルを登録


枝を生やす

単に枝(branch)を ci しただけだと、 次の co では幹(trunk)が取り出されます。

	【失敗例】
	% ci -u1.51.1 index.html	#枝1.51.1.1を生やす
	index.html,v  <--  index.html
	new revision: 1.51.1.1; previous revision: 1.51
	enter log message, terminated with single '.' or end of file:
	>> branch 1
	>> .
	done
	% co index.html
	index.html,v  -->  index.html
	revision 1.51			#これ幹だよね…
	done

rcs -bを使って、「デフォルトの枝はこっちだよ」と 教えておく必要があるのです。

	% rcs -b1.51.1 index.html
	RCS file: index.html,v
	done
	% co index.html
	index.html,v  -->  index.html
	revision 1.51.1.1		#そうそう、そっちそっち
	done

もちろん、デフォルトで幹を取り出すのでよければ rcs -b はいりません。複数人でいじっている場合はデフォルト枝を変更すると 混乱するので、毎回枝を明示した方がいいかと思いますが。

複雑な状況下では生のRCSは使わずに、 CVS のような もっと高級なツールをかぶせた方が良い。 (CVSでは現在の作業領域がどの枝かがどっかに記録されていた、と思う)

rcsfile(5) のマニュアルに図(!)が載っていますが、

[rcsfile(5)の図]
幹と違って枝の先っぽの最新版は すぐに取り出せるようにはなっていないので、 あまり枝を長々と伸ばすのは効率が悪くなります。 早めに幹と合流させましょう。 適宜幹から生やし直すというのもアリです。 (CVSには そういう機能があったような気がする)


枝を幹にする

しばらく枝の上でいじっていた物を、単純に幹とする場合です。 幹も同時進行でいじられていた場合は rcsmerge で合流させる必要がありますが、 一人でいじっている場合はあまりそういうことはないので 簡単な方法で幹を入れ換えられます。

	% ci -u [-r1.5.1] httpd.conf	# 編集中の枝(1.5.1)はとりあえず登録
	% rcs -b httpd.conf		# デフォルト枝を幹(1.X)へ戻す
	% rcs -l httpd.conf		# 幹(1.5)をロック
	% ci -u httpd.conf		# 幹へ登録

	1.3 --- 1.4 --- 1.5 - - - - - - - - - - - <1.6> - -
	                 \                         //
	                  1.5.1.1--1.5.1.2--1.5.1.3
どこが簡単だコラ
幹に登録する場合は、幹(1.5)にロックをかける必要があります。 枝をいじっていた場合はロックは枝(1.5.1) にしか かかっていません。 ので、幹に対して明示的にロック(rcs -l)をかけます。 普段使う co -l で かけると幹の古いファイル(1.5)が取り出されてしまうので×。


作業ファイルだけ消す

閲覧終了とかで 作業ファイルがいらなくなったら、 rm で消してしまっても別にいいんですが、 なんとなく気持悪さがつきまとうのは

こういう認知的ねじれは、 RCS管理下のファイルは通常と違って 「書込権なし」=「登録済」=「消しても被害なし」 なため。

RCS的に正式な消し方は

	% rcsclean workfile
消えるのは、最新版と同じのもの (今 co file しても被害がないもの) だけです。 編集中の作業ファイルは消えないので安心できます。

最新版で ない ファイルは、登録済であっても消えないので、 これらは rm -f で消すしかありません。

ファイル名を指定せず単に rcsclean とやると カレントディレクトリの作業ファイルが 全部 消えるので注意。 本来は make clean 用のギミックらしい。

万が一やってしまったら co RCS/*,v で何とかなる場合もあります。 make すれば自動的に co してくれますが…

ある修正だけなかったことにする

・編集中の index.m4 に対し、1.21版と1.22版の間で行なった修正を取り消す:
% rcsmerge -r1.22 -r1.21 index.m4

これは、「1.22 から 1.21 への修正を index.m4に適用する」と読みます。

	1.20 ----- 1.21 --<<-- 1.22 ----- 1.23 -----
	                    \
	                     \
	          index.m4' --<<-- index.m4
diff と patch を使うなら
	% rcsdiff -r1.22 -r1.21 index.m4 | patch -p0
(rcsmergeが内部でどうやってるかは いまいち不明)

index.m4 は、今そこに転がっているものが書き替わるので、 必要なら事前に登録しておくべし。 とりあえず出力を眺めるなら -p をつければ書き換えずに標準出力に出せます。

	% rcsmerge -r1.22 -r1.21 -p index.html | less
合流し損ねた箇所 (patchなら *.rejになるところ) は "<<<<<<" なんかでマーキングされるので、結果を使う前にはきちんと始末しておきます。

どの範囲の版番をundoしようかと選ぶ際には 常日頃の ci毎のコメントが たよりですので、普段えーかげんに入力している人は ばっちり後悔して下さい(大笑)

RCSの中でも rcsmerge と co -j は鬼門で、マニュアルを読んでも なんかわかったような気にならないです。 とにかく「この場合はこう」という事例を通して理解するしかない?


更新履歴のコメントを修正する

ci -u で、がさがさとコメントを入力して .<CR> を押したあとに、ミスタイプに気づくことは実は結構あったりします。

コメントは一行だ

一行ぽっきりのコメントであれば、-mでわりと簡単に修正できます。

	% rcs -m:新しいコメント filename
新しいコメントはコマンドラインに直接置きます。 最新版以外のを修正したければ版番を明示して
	% rcs -m1.2:新しいコメント filename
ロックがかかってなくてもコメントは修正できちゃうので、版番を間違えると 状況が悪化します。慎重に。 追加とかではなく総とっかえなので、長いコメントだった場合は めんどくさいです。画面上コピーペースト必須。

▽コメントは複数行だだだ

さて複数行に渡ったコメントではもっと面倒です。 今のRCS (v5.7) には、更新履歴コメントをファイルで流し込むといったことが できない のです。 しかたがないので、

○シングルクオートでむりやり注入
	$ rcs -m1.2:'
	> 新しいコメントを
	> 全部打ち直します
	> ' filename
	
この場合は末尾の .<CR> は不要なので注意。
○バッククオートでむりやり注入
	$ rcs "-m1.2:`cat`" filename
	
あらかじめ更新ログを書いたファイルを用意しておけば、 < filename`cat filename` でもたたき込めます。
困ったことに csh ではバッククオートの中の改行が保存されず 問答無用で空白に置き換わるので、上の方法が使えません(なえ〜) これをする時だけ sh に切替えるしかないですかね

RCSでは更新ログに複数行になるような長ったらしいものを 入れるな (ChangeLogを書け) という思想なのかもしれない

なお、ci -u でのコメント注入はリダイレクトを使えば なんとかなるので、

	% ci -u filename < new_log
そそっかしい人はやってみる価値はあります。

なお、ごていねいに入力された行末のスペース・タブは rcs によって 全部削り落とされる (rcsgen.c:cleanlogmsg()) ので、 行末のスペースに意味を持たせている人は rcs 自体を改造する必要があります。

text/plain; format=flowed という、 あまし有名でなさそうな規格 (RFC2646: The Text/Plain Format Parameter) では 行末のスペースに意味があります

ああめんどくせえ

ので、実際には直接管理ファイルをいじくりまわしてしまったほうが 簡単でしょう。管理ファイルを直接覗けばすぐわかりますが、更新コメントは 以下のように格納されているので、 強制的に chmod +w RCS/file,vしてから修正しちゃいます。

	log
	@texttexttext
	texttexttext
	@

ただし、RCS管理ファイルは "@" を "@@" に エスケープするので、テキスト本体や更新ログををJISやBig5で書いていると 腐ります。 直接 less RCS/file.txt,v で見てみるだけでも 所々変になっているのがわかる。 直接いじりはASCII文字だけか、EUC系で書いている場合だけに限りましょう。


一番最初のコメントを修正する

初めて ci した際のコメントは「ファイル全体の説明」で、 更新毎のコメントとは差別されています。これの修正はなぜか 簡単にできるようになっていて、

	% rcs -tdescfile filename	;#ファイルから読む
	% rcs -t-desctext filename	;#直接打つ


rootでは誰だかわからん

su root で RCS管理下のファイルを修正・登録すると、 登録者名が root として記録されて誰がやったんだか わかんなくなることがあります。

	# rlog sendmail.cf
	...
	----------------------------
	revision 4.33
	date: 2002-07-01 22:38:33+09;  author: root;  state: Exp;  lines: +3 -1
	...

ので、登録時に誰の修正かを明示したい場合は

	% ci -u -wmyname sendmail.cf

「ことがある」というのは、場合によるから。 RCSでは、ロック者などのユーザー名をひねり出す際には 以下の順に探す(上が優先; rcsutil.c:getusername())

  1. 環境変数 LOGNAME
  2. 環境変数 USER
  3. getlogin()
  4. getpwuid(getuid())->pw_name
ので、suの仕方によって root になるか myname になるかは変わってきたりする。 まあだから普通は LOGNAME を保存しとくなり再設定すればいいんですが…

Windows なんかではどの環境変数もセットされないので、 LOGNAME か USER を %USERNAME% に設定しておくのを忘れずに。

◇sudoの場合

まじめにsudo を使う場合は

	% sudo ci -u -w$LOGNAME /etc/aliases
にて、LOGNAMEが地のシェルで展開されるので、 自分の実ユーザで登録されます。 sudo -s でシェル起動している場合はLOGNAMEは rootにリセットされるので、
	# ci -u -w$SUDO_USER /etc/aliases
なんて方法もあります。


/bin/sh スクリプトの保護

実行属性 の項で述べたように、 スクリプトなどの実行ファイルは RCS 管理ファイル (*,v) の実行属性も 立てておくようにします。

すると、RCS管理ファイル自体も実行できてしまうことがあるんですねぇ。

	% ./rotate,v
	1.3: No such file or directory
	access: Need exactly two arguments.
	Try `access --help' for more information.
	./rotate,v: symbols: not found
	./rotate,v: locks: not found
	./rotate,v: strict: not found
	./rotate,v: comment: not found
	....

RCS管理ファイルの先頭は "head X.Y" で始まっている ("#"がない) ので、exec時は /bin/sh スクリプトとして判定されます。 続く項目は大抵コマンドとしては存在しないので上記のようにエラー出まくりですが、 いずれスクリプト本体まで到達します。 RCSでは最新版をベタに近い形で格納しているので、それっぽく 実行されてしまいます。

万が一を考える必要のあるスクリプト(管理用の物とかCGI)では、 なるべく先頭の方に

	## This script shouldn't be an RCS file
	## (not a rock-solid check...)
	case "$0" in
		*,v|*/RCS/*)
			echo ERROR: executing an RCS file !
			exit 1;;
	esac
て感じでポカ防止を仕込んでおくといいかもしれません。

…実際にはそれほど神経質になることもないようですが、 「どこでもCGI」な人などは、ちゃんと RCS/ にたたき込んだ上で 保護 するのは必須です。

DOS/Windowsで RCS を使っている人は管理ファイルに ",v" がついていない 場合があり、*.bat や *.cmd では真面目に対策する必要が あるかもしれません。

もうちょっとひねった方法として、

	rcs -nisRCS=: file
とすると、 管理ファイルの頭の方に " isRCS=:1.5;" てのが残るので、 スクリプト本体で test -n "$isRCS" && exit 1 とできます。 こっちのほうが楽かな?

-nisRCS=: は、RCS的には「最新版に "isRCS=" というタグをつける」です。

(shスクリプト以外ではどーやって対策しましょうかね…)


かべ@sra-tohoku.co.jp