まずは修正履歴を見て、リビジョン番号を確認する。
そして、そのリビジョン番号のファイルの内容を表示する(標準出力に出力される)。
$ cvs log ファイル $ cvs update -p -r リビジョン ファイル
updateの-rオプションでリビジョンを指定(-Dオプションで日付を指定してもいい)。
updateの-pオプションで標準出力に出力する。
Eclipseの場合、ヒストリーを使用。[2004-05-16]
一時的に古いファイルの内容を復活させ、必要なくなったら元(最新版)に戻すことが出来る。
$ cvs log ファイル ←リビジョン番号の確認 $ cvs update -r リビジョン ファイル
こうすると、ファイルがそのリビジョンの内容に置き換わる。
そして、そのファイルにはstickyというオプションだかフラグだかラベルだかが貼り付けられる。これが「このファイルは古いバージョンだよ」という情報になる。
stickyが付いている間は、そのファイルを修正してもコミットできなくなる。
元に戻すには、以下のコマンドを実行する。(これにより、stickyが外れる)
$ cvs update -A ファイル
U ファイル
なお、stickyが付いているかどうかはstatusで確認できる。
一時的にではなく 古いファイルを復活させるには、一旦古いファイルの内容を取り出し、新しい修正としてコミットするしかない。
(人間から見て古いバージョンであっても、CVSから見れば「一度確定(コミット)されている以上、内容が古くなったとしても新しいバージョンである」という思想)
$ cvs log ファイル ←リビジョン番号の確認 $ cvs update -r リビジョン ファイル $ mv ファイル ファイル~ ←別名で退避させると同時に、ファイルを消す $ cvs update -A ファイル U ファイル ←ファイルが復活する $ mv ファイル~ ファイル ←古い内容で上書きする $ cvs update ←あとは普通に更新・コミット M ファイル $ cvs commit
途中で「cvs update -A」とするのがポイント。単なる「cvs update」だと、(statusで見れば分かる通り)古いソースがそのまま取得されるだけでstickyが解除されないので、コミットすることが出来ない。
でもこのやり方だとコマンドが多くて面倒くさい。
CVSのマニュアルのHPに書いてある方法が一番簡単だと思う。HPには「一番簡単なやり方ではない」と書いてあるけど。
要するに古いファイルの内容を見るやり方と同じで、出力先を現在のファイルにしてしまう。
$ cvs log ファイル ←リビジョン番号の確認 $ cvs update -p -r リビジョン ファイル > ファイル $ cvs update ←あとは普通に更新・コミット M ファイル $ cvs commit
コミット前にファイルの修正(updateで「M」の状態)をやめて元の内容に戻すには、次のような方法がある。
$ rm ファイル
$ cvs update ファイル
U ファイル ←ファイルが復活する
でもCVSとしては、次のようなやり方が望ましいんだろうな〜。
$ cvs update -C ファイル
いずれの方法でも、stickyが付いている場合には そのstickyを付けた時の状態に戻るので注意。
Eclipseの場合、置換を使用。[2004-05-16]
コミット前にファイルの削除(updateで「R」の状態)をやめて元の内容に戻すには、addを使う。
$ cvs remove -f Sub.java ←削除してみた cvs remove: scheduling `Sub.java' for removal cvs remove: use 'cvs commit' to remove this file permanently $ cvs update cvs update: Updating . R Sub.java ←削除の確定待ちになっている $ ls CVS ←作業ディレクトリ上からは消えている $ cvs add Sub.java U Sub.java ←復活! cvs add: Sub.java, version 1.2, resurrected
コミット前にファイルの追加(updateで「A」の状態)をやめるには、removeを使う。
$ cvs remove -f ファイル
この場合、コミットは不要。
ファイルを削除してコミットする前の状態(updateで「R」の状態)で作業ディレクトリにファイルを追加すると、削除を途中でやめる方法そのままではうまくいかなくなる。
$ cvs update cvs update: Updating . cvs update: Sub.java should be removed and is still there ←削除されるのにまだ存在するってさ R Sub.java
コミットすると削除が確定してしまう。
コミット後にaddすると再追加という形になる。
$ cvs add Sub.java cvs add: re-adding file Sub.java (in place of dead revision 1.2)
コミットする前なら、一時的にどけておく方法が使える。
$ mv Sub.java Sub.java~ $ cvs add Sub.java U Sub.java cvs add: Sub.java, version 1.3, resurrected ←復活! $ mv Sub.java~ Sub.java
add・removeで、リポジトリにディレクトリを追加したり削除したりできる。
しかし、他人の作業ディレクトリで追加・削除された場合(ブランチでの追加・削除も同様)や、自分の作業ディレクトリでもremoveで削除しただけの場合では、自分の作業ディレクトリには反映されない。
これは、通常のupdateでは、現在の自分の作業ディレクトリの範囲でしかファイルの変更をチェックしない為である。
こういった時の為に、updateには「-d」「-P」というオプションがある。
$ cvs update -d -P $ cvs update -dP
こうすれば、ディレクトリの追加・削除が作業ディレクトリにも反映される。
Eclipseの場合、設定しておけば自動的に上記のようになる。[2004-05-16]
タグだけを確認するコマンドは無い。[2006-06-06]
一番最初から存在しているファイルには全てのタグが付いているはずなので、そのファイルのログやステータスを確認することで代用する。
importの際に付けたベンダータグやリリースタグも、同じ手段で確認できる。
% cvs import -m'test' test testv testr ↑コメント ↑ ↑リリースタグ ベンダータグ
% cvs log test.txt RCS file: /opt/cvs/cvsroot/test/test.txt,v Working file: test.txt head: 1.1 branch: 1.1.1 locks: strict access list: symbolic names: testr: 1.1.1.1 ←リリースタグ testv: 1.1.1 ←ベンダータグ keyword substitution: kv total revisions: 2; selected revisions: 2 description: ---------------------------- revision 1.1 date: 2006/06/06 13:58:51; author: cvs_user; state: Exp; branches: 1.1.1; Initial revision ---------------------------- revision 1.1.1.1 date: 2006/06/06 13:58:51; author: cvs_user; state: Exp; lines: +0 -0 test ←import時のコミットコメント =============================================================================
% cvs status -v test.txt =================================================================== File: test.txt Status: Up-to-date Working revision: 1.1.1.1 Mon Jun 6 13:58:51 2006 Repository revision: 1.1.1.1 /opt/cvs/cvsroot/test/test.txt,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: (none) Existing Tags: testr (revision: 1.1.1.1) ←リリースタグ testv (branch: 1.1.1) ←ベンダータグ
ブランチ1.1.1がベンダータグで、リビジョン1.1.1.1がリリースタグらしい。
tagやrtagを使って間違って付けたタグ、あるいは不要になったタグを削除するには、-dオプションを使う。[2006-06-03]
$ cvs tag -d 消したいタグ
$ cvs rtag -d 消したいタグ 対象モジュール
※ここでいう「対象モジュール」とは、importしたときに付けた名称のこと。ファイル1つ1つではない。
※tagで付けたタグもrtagで付けたタグも区別せずrtag -dで消せる。[2006-06-06]
他人が同じソースの同じ箇所を修正した場合、update時にコンフリクト(衝突)が発生して、そのままではコミットできなくなる。
この解消は、ファイルの内容を修正するしかない。
$ vi ファイル ←ソースを修正する(エディタは何でもいいけどー) $ cvs update ファイル M ファイル ←「M」に変わって、コミットできる状態になった
$ ls .#ファイル* ←コンフリクト発生時のファイルがリビジョン付きの名前で保存されるので、ファイル名を確認 $ cp .#ファイル.リビジョン ファイル ←上書き $ cvs update ファイル M ファイル ←「M」に変わって、コミットできる状態になった
$ cvs update -C ファイル ←リポジトリの内容になったので、コミット不要
$ cvs update cvs update: Updating . C ファイル $ cvs commit -f -m'force'
もう1つ、誤って作業ディレクトリ内の「CVS」ディレクトリごとファイルを削除し、その後に(ファイルを元に戻すつもりで)ディレクトリ上に同名のファイルを追加すると、update時にコンフリクトが発生し 上述の方法では解消できないことがある。
こんな特殊なコンフリクトの場合は、ファイルを一旦削除し、後から上書きするしかない。(恒久的に古いファイルを復活させる方法とほぼ同じ)
$ mv ファイル ファイル~ ←別名で退避させると同時に、ファイルを消す $ cvs update ファイル U ファイル ←ファイルが復活する $ mv ファイル~ ファイル ←新しい内容で上書きする $ cvs update ←あとは普通に更新・コミット M ファイル $ cvs commit
「kオプション」は、CVSキーワードを展開するかどうかを指定するオプション。
importする時やファイルを追加するときに指定する。変更するにはadminを使用する。
例えばブランチからトランクにマージする際、ソースの中の「$Id$」といったCVSキーワードがCVSによって展開されていると、その内容がブランチとトランクの間で異なる為、コンフリクトが発生する。
このような場合だと、CVSキーワードを展開しないようにしておけば、そこでコンフリクトが発生することは無くなる。「$Log$」の場合は怪しいが…。
$ find . -name "*.java" | xargs cvs update -kk
※ちなみに、ファイルが修正中の時はkオプションの変更は出来ないようなので注意。
-kk | キーワード展開しない |
-kkv | キーワード展開する(デフォルト) |
-kb | バイナリ(キーワード展開しない) |
なお、変なkオプションを指定すれば、kオプションの一覧が見られる。
$ cvs update -khelp
cvs update: invalid RCS keyword expansion mode
Valid expansion modes include:
-kkv Generate keywords using the default form.
-kkvl Like -kkv, except locker's name inserted.
-kk Generate only keyword names in keyword strings.
-kv Generate only keyword values in keyword strings.
-ko Generate the old keyword string (no changes from checked in file).
-kb Generate binary file unmodified (merges not allowed) (RCS 5.7).
(Specify the --help global option for a list of other help options)
作業ディレクトリにあるファイルの現在のkオプションが知りたければ、statusで見ることが出来る。
UNIXでは、ファイルやディレクトリにパーミッション(権限)がある。
最初にインポートしたときや新規ファイルをコミットしたときに、それぞれのファイルやディレクトリに付いていた権限を元にした新しい権限でリポジトリに反映されるようだ。
そしてチェックアウトや更新によってファイルを取り込
むと、「リポジトリ内のファイルの権限」と「そのときのumask」を元にした権限で作業ディレクトリのファイルが作られるようだ。
状況 | コマンド | 説明 | 例 |
---|---|---|---|
新規登録時 | add + commit |
ファイルの所有者(u)の読込/実行権限が、 リポジトリのファイルの所有者・グループ・他人(ugo)全ての読込/実行権限になる。 (書込権限は無い状態になる)(umaskは無関係) |
test.sh :rwx/-w-/--- …作業ディレクトリ↓ test.sh,v:r-x/r-x/r-x …リポジトリ |
test.c :rw-/---/--- …作業ディレクトリ↓ test.c,v:r--/r--/r-- …リポジトリ |
|||
リポジトリからファイルを取り込む時 | update (commit) |
リポジトリのファイルの各権限(ugoのrx)に、umaskの各書込権限の有無(ugoのw)を反映させた権限になる。 (ugoそれぞれに対し、umaskで書込が許可されていれば+w、無ければ-wになる) |
test.sh,v:r-x/r-x/r-x …リポジトリ↓( umask:007(o-rwx) )test.sh :rwx/rwx/r-x …作業ディレクトリ |
test.c,v:r----x--- …リポジトリ↓( umask:025(g-w,o-rx) )test.c :rw---x-w- …作業ディレクトリ |
自分の作業ディレクトリのファイルの権限を変更しただけでは、updateを実行してみても更新扱いにならない。
権限変更と共にファイルの内容を変更してコミットしてみても、リポジトリの権限は変更されない。
作業ディレクトリ側は、リポジトリからファイルを取り込む時には上記のような新権限でファイルが作られる。
「ファイルを取り込む時」とは、以下のような場合のこと。
つまり、普通の方法(CVSのコマンドで提供された方法)ではパーミッションを変更することは出来ない。
ここでは作業ディレクトリとリポジトリが同じマシン上にあるUNIX環境で試したが、pserverを使ったリモート操作のUNIX環境やクライアントにWindowsを使った環境のことを考えれば、権限をCVSがそのまま扱うのは無理があるって事かなぁ?
そこで荒業だが、リポジトリ内のパーミッションを直接変えてやると(そんな事をしても大丈夫な保証は無いと思うが)、その後ファイルを新しく作業ディレクトリに取り込む時に新しいパーミッションでファイルが作られるので、ある程度はパーミッションを制御できる。
$ ls -l ワークディレクトリ/test* -rw------- 1 hishidama hishidama 10 May 19 23:45 test.sh $ cd $CVSROOT/sample $ ls -l test* -r--r--r-- 1 hishidama hishidama 89 May 19 23:46 test.sh,v $ chmod u+x,go-rwx test.sh,v ←所有者に実行権限を付け、グループと他人は一切アクセス不可に変更 $ ls -l test* -r-x------ 1 hishidama hishidama 89 May 19 23:46 test.sh,v $ cd ワークディレクトリ $ rm test.sh $ umask 007 ←o-rwx:他人は一切アクセス不可 $ cvs update cvs update: Updating . cvs update: warning: test.sh was lost ←ファイルを復活させると U test.sh $ ls -l test* ↓所有者にはちゃんと実行権限が付いているが、グループに書込権限が付いてしまう… -rwx-w---- 1 hishidama hishidama 10 May 19 23:47 test.sh
CVSでチェックアウトするのではなしに、一時的なバックアップとして手でディレクトリごとコピーすることもあるかもしれない。
$ cd 作業用ディレクトリ $ cp -rp サブディレクトリ バックアップディレクトリ
この状態だと、管理用の「CVS」ディレクトリもコピーされてしまっているので、実は作業ディレクトリが増えたような形になる。
CVSディレクトリの中のファイルを見てみると、リポジトリ内の階層が書いてある。つまり作業ディレクトリとしては別でも、リポジトリでは同じ場所を指す。
この状態で、updateを実行すると、バックアップディレクトリは「?」が表示される。
仮にaddを実行しようとすると、次のようにエラーとなるので追加できない。
$ cp -rp sub1 sub2 $ cvs add sub2 cvs add: sub2/CVS already exists
sub2の下の「CVS」ディレクトリを削除しちゃったりすれば追加できちゃったりするが、それをやるとすごくまずい。
sub2の削除に「cvs remove」を使うことになるが、(バックアップディレクトリの下にあるディレクトリはリポジトリのsub1を指したままなので)悲惨なことになる(泣)
$ cvs remove -f バックアップディレクトリ
$ cvs commit
$ cvs update -P
こうすると、きちんと(!)リポジトリからサブディレクトリの中が削除される!(バックアップディレクトリはサブディレクトリと同じ場所を指しているから、それを消したことになる…)
手動でバックアップしたディレクトリを消す際は、CVSコマンドを使わずに消そう(嘆)
$ rm -rf バックアップディレクトリ
てーか、そもそも手でバックアップを取るなっちゅーんだよね…。
それに、うっかり単純に元に戻すと…
$ cp -rp バックアップディレクトリ サブディレクトリ
CVSの管理ディレクトリまで元に戻ってしまう。サブディレクトリ側で何か修正していたら、それがCVSにはどう判断されることやら…(汗)
つまり、そもそも手でバックアップをとるなっちゅーんだよね…。
CVSのremoveを使わずにUNIXコマンドのrm等でファイルを消した場合、updateした時に「Lost」とか出てきて、リポジトリから復活するので注意。
これを利用したのが古いファイルを復活させたり特殊なコンフリクトを回避したりする方法なわけだけど。
他所で多数のソースを修正して、まとめてCVSの作業ディレクトリに持ってくるなんてこともあるかもしれない。
その際、どのファイルが削除された把握するのが面倒だからって、作業ディレクトリを「rm
-r」でさっぱり消して上書きしたりすると…
はい、その通り。管理用の「CVS」ディレクトリが無くなって、update時に特殊なコンフリクトが発生するはめに陥ります。
ついでに、消したいはずのファイルも復活するし。
そんなことをするに越したことはないけれど、
remove -fでディレクトリを削除して、そこにファイル群をコピーした上でaddすればいいのかなぁ…?
と思ったけど、それだけではやっぱりうまくいかない。
ブランチの作業ディレクトリのソースをupdate -rで別バージョンにした後、update -Aを実行するとstickyが外れる(statusで見ると、「Sticky Tag」が「none」になる)。すなわちトランクのソースに変わってしまう。
この状態でupdateを実行すると、トランクのソースと比較・更新されてしまう。
元に戻すには、他のソースをstatusで見て「Sticky Tag」のブランチタグを確認し、update -C -rを実行する。
$ cvs status Test.java =================================================================== File: Test.java Status: Up-to-date Working revision: 1.1.2.8 Wed Oct 1 06:15:29 2003 Repository revision: 1.1.2.8 /usr/local/cvs/test/Test.java,v Sticky Tag: rel_1 (branch: 1.1.2) Sticky Date: (none) Sticky Options: -kk $ cvs update -C -r rel_1 元に戻すファイル