# This text is extracted in 1996. # NOT in sync with the latest presentation. # CVSの使いかた CVS Concurrent Version System の使い方 本文を入力してください 人手で協同作業を管理すると... お約束や習慣の導入 変更がおきたかどうか不明 受動的に、変更を検出する方法 能動的に、変更を他の人に通知する方法 どんな変更がおきたか不明 誰かが変更している途中で、別の誰かが変更してしまうことが起きる 昔の版の再現(版管理)も手作業 原始的にはemacs の backup ファイルの機能など ログ(記録)をとるのが難しい ログをファイルにとっておこうとした時点で、衝突の問題 衝突回避の一つのアイデア RCSのやりかた(CVSは別) ファイルを「ロック」する 同時には一人しかアクセスできない 変更したくとも、当然ロックの解放を待たなければならない ロックしたまま放っておかれてしまう場合も CVSでの衝突回避 リポジトリ、作業用コピー、checkout、commit、update、マージ CVS で管理すると いつでも昔の版が取り出せる 日付指定 「リリース4.21 」のような指定 複数のファイル(ディレクトリ単位)をまとめて扱うのが容易 tc.c の バージョン1.8 に対応するのが tc.h の バージョン1.4 などという対応づけが楽 複数の人間の変更を安全に扱える それでいて複数の人間が同時に変更できる 変更同士が衝突、矛盾して人手の介入が必要にはなる 大事なのは、「何か起こった!」ことが検出できるということ 誰がいつ変更したのか記録される 版ごとにログがつけられる 変更があったことを[受動的に]検出できる 変更が起きたときに[能動的に]自動で通報する仕組みがある CVS 基本概念 CVS基本概念 リポジトリ 共有物にかかわる全ての情報 管理用の情報 見えてさえいれば、共用ディスク、個人のホーム、どこにあってもよい NFS mount (書き込むので hard mount)で複数の機械で共有 遠隔のリポジトリを CVSサーバ/クライアントを通して使える CVSのユーザーは中を直接触ってはいけない 環境変数$CVSROOTは必ずここを指すように注意 $CVSROOT を適当に変化させれば複数のリポジトリを使い分けられる プロジェクトごと お仕事用と個人用 ソースファイルは RCS 管理ファイルの形で保存 変更内容 ログ 変更日時、変更した人 作業用ディレクトリ・作業用コピー  リポジトリの内容がそのまま取り出される どこにあってもいい cvs checkout コマンドを実行したディレクトリに作られる [開発者環境間の分離] ここでの作業はリポジトリに登録(commit)しない限り他人に影響しない 変更削除好き勝手・コピーやコンパイル等でファイルが増えても構わない owner 自分・mode は umask CVS サブディレクトリは管理用 自分でいじらないこと モジュール モジュールの説明が終わったら、複数人で何かcheckout する実験をしてみよう。個人用に独立した作業用コピーができる。 相互に関連したファイルの集まり リポジトリはモジュールの集合・モジュールはリポジトリのサブディレクトリ モジュール名とリポジトリ内の位置との関係を $CVSROOT/CVSROOT/modules ファイルに定義 cvsでの作業(特にリポジトリを対象とした作業)は普通モジュール単位 checkoutしかり・update しかり・commit や release も多分 モジュールは再帰的に構成可 リビジョン・リビジョン番号 リビジョン = 個々のファイルの各バージョン HEADリビジョン 同じモジュール内でもファイルによって、リビジョン番号は違ってくる 変更して"cvs commit" する度に、番号は小数点より右側が増加 実際にどこか変更があったファイルだけ登録し直される ⇒ モジュール内のファイル全部では番号が増加しない ⇒ commit を重ねるうちに、ファイルによって番号がずれてくる "cvs stat" コマンドで番号が確認できる リリース(CVSでの用語) ファイルの集まりで製品が構成されるが、その製品としてのバージョン RCSを使った実装になっている 安全確実 差分だけの記録 ⇒ ディスク容量の節約 複数の版をひとつのファイルにまとめていながら、最新の版を取り出すのは速い ブランチ(枝) ブランチ番号・主トランク(幹) リビジョンが枝わかれしたもの ブランチ番号は、分岐点のリビジョンに偶数をひとつ足した形 リビジョン同様、ファイルごとにリビジョン番号は異なる 何故なら、分岐時点のリビジョン番号がファイルごとに異なっているから BASE リビジョン・HEAD リビジョン cvs コマンド(-r オプション)で、"BASE", "HEAD"という特別の名前で参照できる BASE = 取り出した作業用コピーの元になったリビジョン 人によって(作業用ディレクトリによって)異なる 作業用ディレクトリができれば、updateしない限り変わらない HEAD = リポジトリ内で(普通は主トランクの)最新のリビジョン 同じリポジトリを使っている人の間で共通 時間の経過によって、刻々と変化 CVS 基本コマンド概要 cvs cvsオプション コマンド名 コマンドオプション ... cvs コマンド名 -H = ヘルプ cvs -n コマンド名 = 今ここで "cvs コマンド名" やったら何が起きるか CVSを使った基本の作業1 CVSを使った基本の作業2 CVSを使った基本の作業3 CVSを使った基本の作業4 ファイルの追加のしかた まず、新しく追加したいファイルを作業ディレクトリ上に準備して... 作業ディレクトリに追加したいファイルを置くだけでは駄目 前の例でも、新しくできたtcという実行ファイルはcommitされなかった 「cvs add ファイル名」 実際にリポジトリに置かれるのは、その後 cvs commit したとき ⇒それまでは他の人からは見えない 「cvs add ディレクトリ名」 commit しなくても、リポジトリ内にすぐディレクトリが作られる ディレクトリ内のファイルは、再帰的に追加「されない」 (cvs add dir/file のような指定すらできない) そのディレクトリに降りていって、ひとつひとつ cvs add ファイルの削除のしかた ファイルを作業ディレクトリからrmしておいて... 「cvs remove ファイル名」 実際にリポジトリから取り除かれるのは、その後 cvs commit したとき 本当には消えない(本当に消えたら、古いリビジョンが取り出せなくなる) ⇒ Attic というディレクトリに隠される ディレクトリは削除できない いったん削除したファイルを将来になって add? Attic から引き出してくる手作業 CVSを使った基本の作業ここまでまとめ cvs checkout モジュール名 cvs status [ファイル名] cvs commit [-m ログメッセージ] [ファイル名] ログはぜひ入れよう。時間をかけてメッセージを考える価値がある。漢字も使える。 cvs release [-d] モジュール名 cvs diff [diffoptions] [ファイル名] cvs add ファイル名 cvs remove ファイル名 古いバージョンを取り出すには? cvs checkout -r リビジョン番号 モジュール名orファイル名 cvs checkout -D 時刻指定 取り出した後、変更・commit はできない[スティッキータグ] ⇒ したければ「ブランチ」を作成 cvs checkout -p -r リビジョン番号 モジュール名/ファイル名 でも「タグ」や update コマンドを覚えないと不便 CVSを使い始めるとき 本当の最初はリポジトリの設定 [cvs ver1.8以降] cvs init -d 新リポジトリのディレクトリ [cvs ver1.7以前] $CVSROOT を設定して cvsinit プロジェクトでCVSでの管理を始めるとき 既にファイルがある場合 まだ何も作っていない場合 まずディレクトリ構成を決め、それに従って mkdir なるべく、後でファイルの追加・削除・移動がおきないように 慣れたら、とりあえずトップディレクトリだけ作って、中味は空のままにしておき、後から徐々に add していくのでもかまわない。 そして「cvs import -m "ログ" リポジトリ内の位置 ベンダタグ リリースタグ」 (カレントディレクトリの内容が全部 import の対象になることに注意) ベンダタグ = ディレクトリ構造の供給者名 (ここではあまり気にしなくていい) リリースタグ= 最初のリリースを識別する記号 (ここではあまり気にしなくていい) 例: cvs import -m "Creating directory structure." tc/man WATANABE start 開発が進むにつれて、必要に応じて cvs add 「モジュール」の定義 "cvs checkout リポジトリ内のパス"の指定で、常にあらゆるファイルが取り出せる。⇒ それでは何が不満だというの? modulesファイルでのモジュール定義 $CVSROOT/CVSROOT/modules これ自身 CVS で管理されている ⇒ checkout, 変更,そしてcommit モジュールのリポジトリ内の置き場所を指定 モジュール定義をしなくても、リポジトリ内の位置指定で作業はできるが、なるべく定義する習慣にしよう。 結局モジュール同士の包含関係を指定することになる fancy, funky な function alias(別名) モジュール同士の包含関係の陽な指定 commit 時検査 同時並行開発作業1 同時並行開発作業2 同時並行開発作業3 並行開発のまとめ 先にcommitされたら、その分の変更を取り込まないと commit できない マージ。重なりは手動で解決。 cvs update: 最新版(HEADリビジョン)までの変更を作業用コピーにとりこんでくれる いつやってもいい。 朝きたとき・休憩後・同僚に出し抜かれた気になったとき:-) cvs -n updte : いまどうなってるの?(調べるだけ。作業用コピーに何ら変化をおこさない。) update の出力 作業用ディレクトリ リポジトリ 表示 ------------------------------------------------------------------ (無変更) (無変更) (なし) 変更 M 変更 U 変更 変更 C add A remove R 追加 U 削除 XXX is not (any longer) pertinent 未登録 ? ファイルの状態(cvs status) Up-to-date(最新) Locally modified(自ファイル変更) Needs update(要update) Needs merge(要merge) タイムスタンプ:make との関連 update すると作業用コピーのタイムスタンプはどうなるの? リポジトリ内のタイムスタンプには合わせない タイムスタンプは update された時刻になる update されないファイルはそのまま リビジョン番号のそろえ方 cvs commit -r 6.0 checkout 以降に変更があってもなくても、全てのファイルのリビジョンが 6.0 になる。 [ステッィキータグ(後述)]この作業ディレクトリ上でさらに編集を続けるには、cvs update -A が必要 後から取り出すことを考えて特定の時点に印をつけるには、「タグ」の方がふさわしい キーワード置換(後述)を考えたカッコつけ タグ リビジョン(の組み合わせ)に名前をつける リビジョン番号やブランチ番号がファイルによって違うのは、全然心配いらなかったんだ! tag コマンド 作業用ディレクトリ上でタグづけ BASE リビジョンへのタグづけ ⇒ commit していない変更は、つけるタグと無関係になる 複数のタグ・タグの移動・削除 cvs tag -d Tag で削除: 移動と同様にやらない方がよい。よく考えてからやること! タグに関係したコマンド例 cvs checkout -r Tag [ファイル名] cvs diff -r Tag [ファイル名] 作業用コピーとタグの時点の内容との差分 cvs diff -r Tag1 -r Tag2 [ファイル名] Tag1とTag2の間の差分 diff -r BASE -r HEAD cvs update -r Tag [ファイル名] Tagで示されるリビジョンを取り出し、作業ディレクトリの変更分とマージ 作業ディレクトリを何も変更していない場合、Tagで示されるリビジョンで作業 ディレクトリを置き換えることに対応。 この後、変更・commit はできない[スティッキータグ] ⇒ やりたければ、ブランチを作成する cvs status -v ファイル名 ファイルについているタグを全部列挙 rtag コマンド 作業用コピーを取り出さずにリポジトリ上に直接タグふり tag コマンドでは、BASEのところにタグを設定 ⇔ 横軸が全部見えているrtagの場合、tagをふる場所(時点)をどうやって決めるのか? 時刻指定: cvs rtag -D 日時 Tag モジュール 別のタグ指定: cvs rtag -r TagWhere NewTag モジュール 後述のブランチタグをふる時に便利 リビジョンの指定: cvs rtag -r 3.4 ファイル名 モジュール 恐らくファイル単位でないと意味がない タグによる、モジュール間のリリースの関係づけ 「OS2.0」の呪文で{com 1.4, lib 1.5, doc 2.0, kern 2.1, dev 1.2} が欲しい その中の kern2.1には、Process 1.1, Memory 1.3, ...が含まれていてほしい タグによって関係をつける タグによって再帰的に関係をつける タグは静的にファイルにつくだけ RDB的な柔軟性なし・表の形では見えない・タグの移動はやらない Kernel2.1が使うProcessサブシステムを1.2に変えたい cvs rtag -r PROC-1-2 KERNEL-2-1 確かに "checkout -r KERNEL-2-1" で Process 1.2 を得るようになるが checkout -r OS-2-0 ではどうか? タグの管理 cvs status -v 等の情報は、ほとんど管理に役立たない タグをつけたことを忘れない 何のためにつけたのかを忘れない ブランチを作る(=ブランチタグをつける)ときには、分岐点にタグをつけておく rtag だと分岐点にタグをつけざるを得ない タグ名の選び方は大事 ブランチの分岐点と、ブランチ自体の区別 BP-XXXX とXXXX XXXX と BR-XXXX BP-XXXX と BR-XXXX 公的なものと、自分専用の気軽なものの区別 PRIV-XXXX, WATANABE-XXXX, ... X-XXXX (Xで始まる名詞は少ない...という意見に納得できる人) commitのタイミング 賛否両論 [急進的方針] ちょっと変えてはコンパイラを通りもしないソースを commit ⇒ ... そのモジュールを使っている他の人もコンパイルできなくなる 「他の人」が機械のことも(夜中の自動テスト) [保守的方針] ほとんど commit しない 他の人が、変更分を有効利用できない commit するときに衝突(変更矛盾)、重なりがたくさんおきて大変 まとまった量の変更の予定がある場合 ⇒ ブランチ上で変更して後でマージする手も。 ブランチ(枝)再訪 どんな時にブランチを作るか? マージするのが難しいような、大きな変更がありそうだ プロジェクト全体に影響を与えることなく、一部のメンバだけ(時には個人)で変更を管理・共有したい ブランチを checkout した人だけにその変更が見える 実験的なものなど 一時的で、いずれ主トランクに併合される リリース1.0 を出荷した → これに若干の手直しの要求がきた。 しかし既にリリース2.0の開発が進んでいて、今さら戻れない...... ブランチは相互に独立な開発ライン "checkout -r ブランチ"で、ブランチ上の最新版が手にはいる "update -r ブランチ"で、作業コピーをブランチ上の最新版にする 主トランクの時と同様、ブランチの線上で変更とcommitの繰り返し ブランチ1.2.2を checkout したら、その変更は 1.2.2上に付け加えられていく ブランチの作成(rtag -b) スティッキータグ 作業用コピーに張り付いたまま、以降の作業に影響するタグ checkout, update, commit での -r 指定で「スティッキータグ」が作業用コピーにつく 指定がブランチタグだった場合 = ブランチからコピーを取り出した ⇒ 以降の変更・commitは、(メイントランクではなく)ブランチに対してやる ⇒ 作業用コピーは、checkout 後も、自分がどのブランチのものか覚えておく必要あり 指定が特定のリビジョン(やタグ)だった場合 = その特定のリビジョンに興味がある ⇒ 以降の作業もそのリビジョンに対してやる ⇒ ... 変更してcommitすることはできない。 昔の1.2を取り出して変更した。 ではそれをどの rev にcommit すればよいか?まさか1.2に?? cvs status で、ついているスティッキータグが確認できる スティッキータグをはずすには? cvs update -A スティッキータグがはずれる = 主トランクに戻る スティッキーDate (-D 時刻) スティッキーオプション バイナリファイルの checkout -ko マージ キーワード置換 ファイル中の$Id$, $Date$, $Author$ などの文字列が、update の時に自動的に置換 "$Id$" → "$Id: driver.c,v 1.6 1994/11/30 08:49:15 katsu Exp $" コマンドのオブジェクトの中に、$Id$などが入るように工夫しておく static char version_string[] = "$Id$"; 差分やパッチ(diff)にキーワードの部分を含まないようにするには...... -kk オプション = キーワードのみに置き換えて、値の方を含まない $Log$ は CVS となじまない cvs log コマンドでいつでも log は参照できる ブランチ間のマージで問題が起きる 取り出されたログを編集してしまいがち ident コマンド ファイル(特にバイナリファイル)中からキーワードを探して表示 cvs admin -ko, cvs checkout -ko : キーワード置換をしない指定 特にバイナリファイルに対して有効 history ウルサイ上司には教えたくないコマンド cvs の操作記録は $CVSROOT/history に残る ← cvs history で参照可 cvs history -o 自分が checkout したモジュール cvs history -a -o 誰かが checkout したモジュール cvs history -o -w 作った作業用ディレクトリと取り出したモジュール cvs history -a -o -w 全員について -o -w cvs history -x A パターン モジュールができて以降 add されたファイル cvs history -c パターン 変更されたファイル cvs history -c -D 'last Friday' 先週金曜日以降変更されたファイル cvs history -c -t TAG TAGというタグがつけられた以降の変更 cvs history -c -l -[fp] X [ファイル,リポジトリ]Xを最後に変更した人 cvs history -T タグづけの記録(rtag のみ) cvs history -[fpn] X [ファイル, リポジトリ,モジュール] Xの情報 cvs history -m モジュール 信用はできない rtag の情報はあるが、tag の情報はない 抜け道がある 後から操作できる 事故の時やリポジトリ管理のために参照 統計 よく衝突(変更矛盾)を起こしていて、切り分けを考え直したいファイル せっかく置いたのにだれも使っていないモジュール CVSの光と影 ファイルの追加削除等 remove したファイルと同名のファイルを、再び add できない。 ファイル名の変更は、リポジトリ上で手動での作業 cvs remove して cvs add するのが安全確実だが、ログと history と revision number は不連続になる。 ディレクトリを削除できない 変更のマージでは、ファイルの追加削除に関する作業をやってくれない ハードリンク・シンボリックリンクなどは考慮されていない(cvs のコマンド一般についていえる) 例えばシンボリックリンクを add すると、リンク先のファイルが add される etc. CVSはデータベースではない しかし基本的には安心して使ってよい commit point, atomicity(all or nothing, 原子性) ... の概念なし 複数のファイルあるいはディレクトリに対して cvs xxx やっていたのをCtrl-C で止めると、あるファイルについては xxx が完了して、別なファイルには xxx が行われていないということが有り得る。 障害時の回復の機能なし RCSのレベル(CVSでいえば、個々のファイル単位)では atomicity の概念あり 途中で止めても、差分が中途半端に登録されたり、ファイルが壊れて昔のバージョンが取り出せなくなるようなことは起こらない。 途中で Ctrl-C で CVS を止めたらどうなるか? 個々のファイルの内での一貫性は RCS が保証。 waiting for xxx's lock in ... と出ているところ(他の人がリポジトリをロックして操作している状態)で止めるのは安全 checkout,update: コピーを捨てるか再度実行。 add,remove: commitしなければいい。 tag: 再度実行する必要あり。 戻すのは通常は -d 。ただしタグの「移動」(既存のタグ名の再利用)だった場合は戻すのは困難。 commit: 再度実行すればよい。しなかったことに戻すのは詳しい知識要。 cvs コマンドを複数同時に走らせて大丈夫? 大丈夫 通常はちゃんとロックしている。一人で複数のコマンドを走らせるのは、複数の人が複数のコマンドを走らせることの特殊ケース。 HEAD に対する cvs rtag (-r 引き数なし) commitによって HEAD はいつも揺れ動いている。 rtag はロックが不完全(タグづけ中のディレクトリのみしかロックしない??) 例えば同じモジュールに対して同時に rtag を複数動かすと、 rcs: RCS file ... is in use cvs [rtag aborted]: failed to set tag ... といって終了することがある。(仕方ないから tag を delete してやり直そう。) でも HEAD に対して rtag をする(しなければならない)場面はほとんど考えられない。 $CVSROOT/CVSROOT/commitinfo などで commit を禁止しておけば安全 [cvs 1.3 以前] cvs tag コマンドが複数動く時に軽い問題 同時に同じファイルにタグをつけようとすると、片方でrcsのエラーが起きる エラーが起きたファイルだけタグづけをやり直せば問題なし。 ディスクが安い世の中で本当によかった リポジトリにはすべてのソース 一人に一つづつ作業用コピー 三つもコピーを持って、相互にupdate/マージする狂人もいる 自動的なマージは意味を考えていない 機種ごとの構成の管理へのブランチの応用 あまりよい考えではない あるブランチ上の差分をどう他のブランチに反映するか Sun OSの変更を Solaris 版にも... ファイルの追加削除が大変 ブランチにファイルを add するのは trival でない "cvs add ファイル; cvs commit -r ブランチタグ" マージの時に、ファイルの追加削除を含めるのは手作業 ブランチ上で -D オプションはうまく働かない 結論:CVS はソースの管理だけ。構成管理はブランチ以外の手段を使う。 CVSは何でもやってくれるツールではない 単にソースを蓄えているだけ build システム(make)の代わりではない プロジェクト管理ツールではない 開発者間のコミュニケーションのツールではない 構成管理ツールではない リポジトリ管理者 ・リポジトリには必ずリポジトリ管理者がいなければならない ・難しい作業は少ないが、慎重さや集中力がいることもある 管理者の仕事 dump, backup バックアップ daily 大規模な変更(ブランチのマージ) importの前 リポジトリの一部に書き込み権限がない・ソースの一部に読み込み権限がない・RCSファイルにリビジョン1.1がない(RCSからの移行時)...などで失敗することあり リポジトリに書き込まれないようにするのは案外難しい commitinfo (commit 操作のフィルタ)に /bin/false を設定するだけでは不完全 [cvs 1.5 以前] "taginfo" (tag 操作のフィルタ)のような仕組みがなかった rcs ファイルを直接リポジトリに置くのを禁止できない 病的な technic を使われた場合 リポジトリのあるディスクを umount できれば理想的 テープなどへの保存とリポジトリからの消去 ディスク容量の肥大が気になる方々へ 変更を全部 commit してもらう リポジトリ内のディレクトリを再帰的に保存 対応する modules,commitinfo, editinfo, loginfo, (cvsignore) の設定等の記録 ファイルやディレクトリのモード cvs ではディレクトリは、g+w で作られる [BSD] サブディレクトリの group はデフォルトでは上のディレクトリと同一 リポジトリ直下に最上位のモジュールを作るときにモード変更 リポジトリをリポジトリ管理者(group)だけが書き込み可(単一group) 管理者(group)が、モジュールの設置,mode,groupを管理 結局単一グループなら何もやることがない リポジトリ自体をworld writable(リポジトリをgroup間で共有) モジュールを置いたときに、group を適当に設定してもらう chmod g+t して、モジュールの所有者だけに削除,renameを許す [SysV R4] [SunOS] 系 サブディレクトリの group は、その作成者(cvs を使っていても真) chmod g+s や mount 時に -o grpid しておくと、上のディレクトリの group を継承 勝手な group で import などをしても、group の設定不要 $CVSROOT/CVSROOT CVSROOT 自体 modules ファイルは誰が管理するか? リポジトリを見るだけでも、cvsコマンドを使えば history に書き込む commit すれば、commit log に書き込む ...これらを考えてモード設定 ユーザーが属するgroupの設定も忘れない import したときのmode, owner, group は、checkout しても保存されない 自分専用のコピーなのだから、意味がない 他ツールとの連携 loginfo [変更の能動的な通知]commit したら、特定のプログラムを起動するように設定できる メール、ニュースとの連携: 例えば、変更が起きる度にメーリングリストやニュースグループに自動でログを投稿 自動テスト リポジトリに登録されてるものは、恐らく編集途中でない。 ⇒ コンパイラを通る  cron で深夜や週末のテスト・プロファイル・統計 リポジトリを NFS で共有 ⇒ CVSROOT/loginfo も当然共有されてしまう ⇒ コマンドが共通であるように注意 事故や失敗の後始末 cvsの #* ファイル、rcs の ,* ファイル ロック、一時ファイル #cvs.$rfl.3094, #cvs.tfl.19747, #cvs.lock(ディレクトリ), #cvs,wfl.19747, ",co.c," ... import で間違って登録してしまった *.o, a.out などの削除 間違った本人が cvs remove ⇒ Attic というディレクトリに置かれる ただしリポジトリには残ったまま 格好(と安いディスク空間)を気にせず、放っておく選択もある