[CVS超入門]
$Id: cvs_rtips.html,v 2.17 2017-10-05 20:47:55+09 kabe Exp $
作業領域でいじくったファイルが気に入らなくなって、 元に戻したくなった時の一般解は
% cvs update -p -rBASE screen.c > screen.c
-p
てのは「標準出力に吐く」オプションで、
こんな場合に使うのは妙な気がしますが、
他の方法ではうまくいきません。
誰も commit していないことがわかっている (自分一人とか)のであれば、
「リポジトリ中の最新版で上書き」(ローカル変更と合成しない)の
-C
オプションで
% cvs update -C screen.c上書きして戻すことが可能です。
なんのオプションもつけずに
% cvs update screen.cとしても、元には戻りません。 何が起こるかというと、
つまり、update [-A]
というのは
(現在の枝の) 最新版と合成せよ
なので、「元に戻す」という用途には使えません。
-C オプションは、「合成をしない」指定であって、 「最新版」の意味はそのままなので、 cvs update [-A] -C は 「[幹の]最新版に上書する(戻す)」という意味になります。
cvs update -A file
では、
副作用でそのファイルのひっつきタグが剥がれる
(==幹にひっつく)ので、
枝で作業していた場合は望ましくありません。
他のファイルは枝のままなのに
そのファイルだけ幹に属している状態になるので、あとで人間が混乱します。
結局、update -p file > file で上書きするのがもっとも安全なわけで…
単に cvs update -C -rBASE
(「BASE版で上書きする」) とすると、
update -rBASE
てのは
原則禁止です。
ではと番号版番で cvs update -C -r1.4 screen.c
とすると、
以後そのファイルは点タグ 1.4 にひっついた状態になる
(cvs stat で確認すべし)ので、
以後commitができなくなります。
この状態を是正するのに美しい方法はなく、しょうがないので
CVS/Entries を直接いじくってひっつきタグを消す。こうすると、1.4 が最新版だった頃に
具体的には該当行の末尾の ....//T1.4 を ....// に変える。
checkout
した状態と
似た感じになんとか戻せます。
(完全に同じではないようだが…実用上は問題なさそう)
複数の枝を同時開発していると、
ディレクトリ的に近いところに複数の作業領域を置きたい
ことがあります。
普通に cvs checkout
projectY とすると モジュール名の
projectY/ でディレクトリが掘られるので、一段余計な階層をかまさないと
いけない気がしますが、
+- projectY-en/ | +- projectY/ これ以下作業領域 +- projectY-ja/ | +- projectY/ これ以下作業領域
実は作業領域の名前はモジュール名(projectY)である必要はありません。 ので直接 projectY-en/ とかを作業領域にしてもかまいません。 モジュール名は各作業領域の CVS/ に記録されているので cvs update などはこれでも正常に動きます。
+- projectY-en/ これ以下作業領域 +- projectY-ja/ これ以下作業領域
いったん ./tmp/ を掘ってそこに checkout し、
mv tmp/projectY ./projectY-en
とかしてもいいんですが、
% cvs checkout -d projectY-en projectYで直接 ./projectY-en/ に作業領域を展開できます。
既存のプログラム類を取ってきてとりあえず import でCVS管理下にした後、
*.BAK
やら *~
やら backup.tar.gz
などに気づいて掃除したくなることが非常によくあります。
CVS的に正しい方法は cvs remove を使って除く方法ですが
% rm backup.tar.gz % cvs remove backup.tar.gz % cvs commit backup.tar.gzこれだとリポジトリでは Attic/ 以下に収容され、容量削減などには貢献しません。 ベンダ枝を指定して checkout した際も(正しく)残ったままです。
最初から証拠隠滅するには、リポジトリから消します。 (リポジトリを直接いじれることが条件)
% rm $CVSROOT/module/backup.tar.gz,vリポジトリ側ではファイル一覧のようなものは持っていないので、 *,v を消しただけでも不整合は特に起きません。
作業領域のほうでは古いファイルは残ったままですが、 cvs update すれば消えてくれます。
% cvs update cvs update: Updating . cvs update: backup.tar.gz is no longer in the repository 消えたときはメッセージあり % cvs update cvs update cvs update: Updating . 消えていれば何も出ない % _
今は不要になったけど前の版では使っていた、ようなファイルは ちゃんと cvs remove で Attic/ に収容しましょう。 過去版を取り出したときにファイルが足らなくなります。
タグ地図のように、 開発用の作業領域では版管理しておきたいけど、 配布時は不要、というファイルがある場合、 単純に cvs add で追加すると cvs export での配布版にも含まれてしまいます。
ので、幹ではない専用の枝にファイルを追加し、 幹からは消しておく、という手を使います。
1.1 -----X (幹から消去) \ 1.1.2.1 ---* (TAGMAP)
## まずはCVSに登録 % cvs add Makefile cvs add: scheduling file `Makefile' for addition cvs add: use 'cvs commit' to add this file permanently % cvs commit Makefile (エディタ起動 ) RCS file: $CVSROOT/projectY/Makefile,v done Checking in Makefile; $CVSROOT/projectY/Makefile,v <-- Makefile initial revision: 1.1 done ## 枝を生やす % cvs tag -b TAGMAP Makefile T Makefile ## 作業領域のファイルをTAGMAP枝にひっつける % cvs update -r TAGMAP Makefile ## 強制的(-f)に枝を伸ばす % cvs commit -f Makefile (エディタ起動) Checking in Makefile; $CVSROOT/projectY/Makefile,v <-- Makefile new revision: 1.1.2.1; previous revision: 1.1 done ## 幹からは消すので、いったん幹に移る % cvs update -A Makefile U Makefile ## 幹から消す % rm Makefile % cvs remove Makefile cvs remove: scheduling `Makefile' for removal cvs remove: use 'cvs commit' to remove this file permanently % cvs commit Makefile (エディタ起動 ) Removing Makefile; $CVSROOT/projectY/Makefile,v <-- Makefile new revision: delete; previous revision: 1.1 done ## 対象ファイルだけ枝に戻す % cvs update -r TAGMAP Makefile U Makefile
新しい作業領域を作るために cvs checkout をしても、 やはり専用枝のファイルは出てこないので、 個別に cvs update -r TAGMAP Makefile で取り出しておく必要があります。
単純に cvs update -r TAGMAP (ファイル指定なし) とすると、
作業領域にあった幹ファイルは全部掃除されてしまい、
TAGMAP枝にあるファイルしか残りません。
専用枝には、専用ファイル以外は登録されていないため。
この状態はよろしくないのですが、ファイルを消さないというcheckout/updateの
オプションはないので、
ファイル名を明示して update -r TAGMAP しましょう。
ファイル名の目処は、cvs -n update -r TAGMAP の出力でつけます。
## 新しい作業領域を作るには % cvs checkout projectY % cd projectY % cvs update -r TAGMAP Makefile
これをやると、作業領域には幹所属のファイルと TAGMAP枝所属の ファイルが混じることになりますが、CVSはこのような状態を 想定していません。多少の不便やおかしな動作は覚悟しよう。
また、専用枝は枝なので、リポジトリ中の更新情報は 前向き、つまり 1.1版からのdiffをずっと重ねたものになります。 幹と違い、更新を大量に重ねていくと取り出しが遅くなってしまいます。
TAGMAP枝専用ファイルは他の枝では存在しない扱いなので、
% cvs update -A # 幹に移動など、ファイル指定なしの全体更新updateすると ファイルが消えます。 updateしたあとは、必ず
cvs update -rTAGMAP Makefile
で
明示的にTAGMAP枝のファイルだけ取り出しておきます。
他人や自分が新しいサブディレクトリを追加して commit した場合、
他の作業領域で cvs update
しても、デフォルトでは
新規ディレクトリが伝播しません。
エラーも何も出ません。
つまり、新規ディレクトリが追加されても気づかない可能性がある!
新規ディレクトリも引き込むには -d を指定します。
% cvs update -d
マニュアルでは、デフォルト動作 (新規ディレクトリを引き込まない) は
「欲しくないディレクトリを避けてcheckoutしているのを台無しにしないため
」
とありますが、あんまそういう使い方はしないと思います。
cvs update -d
を癖にするのがよさそうです。
複数人での開発などでリポジトリが変化していく環境では、
定期的に cvs update
でリポジトリと作業領域を同期
(追っかけ)する必要があります。
いきなり update
すると何が上書きされるかわからんので、
まずは cvs -n update
で様子をみるわけですが、
cvs -n update
で U
と表示されるファイルは、
リポジトリ側で(別の人によって)新たな commit が行われたので、
作業領域の更新が必要であることを示します。
今そこに転がっているものは古くなった、てこと。
% cvs -n update cvs update: Updating . U hello.c
cvs update
[file] すれば更新されますが、
その前に何が変わったのかだけ知りたい。
% cvs diff -rBASE -rHEAD
【例:MAIN_HELLO枝で作業中、誰かが新しい版を同じ枝にcommitした】 % cvs status =================================================================== File: hello.c Status: Needs Merge Working revision: 1.2.2.1 Fri Jan 24 19:01:56 2003 === BASE Repository revision: 1.2.2.2 /root6.1/xx/cvsroot/p1/hello.c,v === HEAD Sticky Tag: MAIN_HELLO (branch: 1.2.2) ひっつきタグ Sticky Date: (none) Sticky Options: (none) ※自分の作業領域にあるのは 1.2.2.1版が元になっている ※リポジトリ中の最新版は 1.2.2.2 になっている
自分の作業領域にあるファイルは、1.2.2.1 が元になってはいますが、 修正していれば 1.2.2.1 でも 1.2.2.2 でもない別物であることに注意。 commit するまでRCS版番はつきません。
diffで「作業領域」を明示したい時は-rを省略するしかありません。 たまに不便なこともある。
開発者間のメールとかで単に 「HEAD版が…」 と言っている場合は 大抵 幹の 先端のことを指しています。が、 枝にひっついている作業領域では -rHEAD は 枝の先端であって幹ではありません。注意。
正しい方法は
cvs export -rHEAD
で幹の全ファイルを取り出し、
diff -aru --exclude=CVS
で手動比較
CVSで予約されている仮想タグは BASE と HEAD だけで、 幹に割り当てられた仮想タグちうのはありません。しょうがないので
% cvs diff -r1 #幹 1.x の .x を省略した形
幹の先端→現在の作業領域、の diff になります(逆diff)。 幹の版番が 1.x のままで、整数部が変わってないことが前提です。
ただ、ベンダ枝から変更されていないファイルは 1.1.1.x を参照しなければならない のですが、上の設定は全ファイル 1.x を参照するので、 リポジトリ初回登録時に cvs admin -b で 枝リセット していない限り正しく幹を示しません。
結論:使えね〜
逆向きの「幹の先端を取り込んだらどう変わるのか」
(現状→幹のdiff)をとりたいこともありますが、
-r
では「今そこに転がっているもの」を明示できないので不可能です。
(BASEとの比較で良いなら -rBASE -r1
で何とかなる)
実際には cvs update -A としても diff の結果が 丸ごとあたるわけではなく、 「作業領域ローカルの変更は保存される」に従って更新されます。 -rBASE -r1 のほうが近いかも。
要するに幹を指す仮想枝タグが無いのが悪い。
幹の整数部がたまに変わるとか、
もっとかっちょよく幹を参照したいて場合は、
幹に対して枝タグを明示的に打っとけばいい。
んですが cvs tag -b
では枝を新規に生やすことしかできないので、
最初から存在する幹には cvs admin
(RCS直接いぢり) で打ちます。
かなり変則的な設定なのでおすすめしません。
% cvs admin -nTRUNKTAG:1 [file]整数部が変わったらまた全部打ち直します。 "HEAD" と打つと枝ひっつきで発狂するので別の名前にするように。
CVSではRCS版番にこだわる理由が少なく、
RCSでも整数部を変えると別の枝と見なされて
-d
(日付で指定して取り出し) とかが使いにくくなるので、
強烈な理由がないなら整数部は 1.x のままのほうが便利。
ベンダ枝から変更がないファイルを 1.1.1.x にすべきところが 1.1 (==幹の先端ではなく、最初の版)になってしまう問題も残ります。
結論:使えね〜
その機能、CVSにはないんですよ。
CVSは1回のコミットに対してIDを割り振る、といったことをしていないので、 「前回のコミット」がどれとどれの何、を記録していません。
% cvs diff -D yesterdayで似たようなことはできるといえばできるんですが
実は $CVSROOT/CVSROOT/history
にコミット記録自体はあるのですが、
1回のまとまったコミットに対しての一意のIDが振られてない
(時刻かなんかかなぁ)ので、
自動で1回のコミットを取り出すのも難しそうです。
commit前に cvs tag で点タグを打っておけば cvs diff -r 点タグ で 簡単に比較できますが、小さいcommitでそんなのやってる人いないと思います。
Subversion (svn) や git だとコミット毎にID番号が振られるので、 誰のいつのコミットだけのdiffをとる、戻す、のは可能です。 そういうことをしたい回数が多くなってきたらgitに換えどきかもしれません。
なりません。
CVSは内部的にとにかく GMT に統一して動くようになっています。
RCS では使えていた RCSINIT=-zLT
環境変数も無効です。
ありません。 RCS をインストールしてください。 ident 自体は CVS管理下のファイルでもちゃんと使えます。
ただ現実には CVS管理下でメンテしているようなソースコードは
RCS のタグ類はついてないことのほうが多い。
(∴バイナリを ident でみてもあんまし面白くない。)
というのも、CVS では枝の管理を ./CVS/
でやってくれるので、
個々のファイルの版番はあまり重要でなくなるため。
実際には版番より枝タグの名前のほうが重要だったりするので、 CVSで埋めるキーワードは $Name$ のほうが使いでがあります。 ただしタグを明示しての checkout/export の時くらいしか 書き替わらないので過信は禁物。
CVS は入ってるが RCS がないなんつー環境はかなり稀だとは思いますが
標準テクニックですが、ssh経由でリポジトリ機につなぐには
% env CVS_RSH=ssh \ cvs -d :ext:user@hostname:/path/to/CVSroot update
CVS_RSH
:ext:
を指定したときに使う
リモートシェル。 Red Hat系はデフォルトが rsh だったりするので
明示的に定義する必要あり。 .cshrc とかに入れておいても良い。
/path/to/CVSroot を含む -d 全体が作業領域の ./CVS/Root に記録される ので、もし作業領域でパスを隠蔽したい場合は ssh ではなく pserver を使わなければならない。 (設定が必要)
22番ポート以外でsshdを動かしている等、変則設定でsshを使う場合は、
CVS_RSH="ssh -p 10022"
とかしてもダメです。ので、
# デフォルト Host * # "cvsserver" の時だけ Host cvsserver Hostname my.host.example.jp Port 10022
% cvs -d :ext:user@cvsserver:/path/to/CVSroot
で my.host.example.jp:10022 へ接続できます。
$HOME/bin/ssh10022 といったスクリプトを作り、中身をこんな感じにして
#!/usr/bin/ssh -F Hostname my.host.example.jp Port 10022CVS_RSH=$HOME/bin/ssh10022 cvs -d :ext:user@server:/path/to/CVSroot で接続できます。sshの設定ファイルで接続先(Hostname)を書き換えるので、 "server" の部分は何でも良く、隠蔽できる。 (が、/path/to/CVSroot は隠せない)
この手法は作業用より、配布用として余計なものは隠蔽したい cvs export 使用時に有用かもしれません。
CVS での管理を中止したり、初回の cvs import
を失敗したので
やり直したい、という場合。
./CVS/
を消します。
% rm -fr ./CVS
% rm -fr $CVSROOT/projectY
$CVSROOT/
以下に CVSROOT/
以外のディレクトリが無いなら、
$CVSROOT/
もごっそり削除してかまいません。
当然のことながら、リポジトリを消去すると 今までの履歴は全部消えます。 2回目以降の import を失敗したときは、 下の手順を試してください。
2回目以降にimportした場合、importをやり直すには、 リポジトリを消すわけにはいかないので、 危険ツールとされる cvs admin を使ってベンダ枝の版を削除します。
具体例:
cvs -d ~/cvsroot import openssh OPENSSH_P OPENSSH-7-6-P1
でimportしたが、*.old が無視されてしまっているのでやり直したい。
手順:
cvs -d $CVSROOT checkout -rOPENSSH-7-6-P1 openssh -d openssh-7.6p1 cd openssl-7.6p1
cvs admin -oOPENSSH-7-6-P1-o は RCS のオプションで、特定の版を消す、という操作です。 ヘルプは cvs admin とだけ打てば出ます。
この操作をしても作業領域は一切いじられないのですが、 上のように作業領域と同じ版を消した場合は、 作業領域にあるファイルはどの版にも属していない中途半端な状態として認識されます。
% cvs status INSTALL cvs status: INSTALL is no longer in the repository =================================================================== File: INSTALL Status: Entry Invalid Working revision: 1.1.1.4 Thu Oct 5 10:53:22 2017 Repository revision: No revision control file Sticky Tag: OPENSSH-7-6-P1 - MISSING from RCS file! Sticky Date: (none) Sticky Options: -ko