Kernel hackerのためのcvs入門 (初心者向け)

Copyright(c) 1997 and 1998 by Jun-ichiro itojun Itoh <itojun@itojun.org>. All rights reserved.
Freely redistributable. Absolutely no warranty.
$Id: tut-kernel.html,v 1.32 2001/10/29 09:23:37 itojun Exp $
注意: このtutorialでは非常に非一般的な branchの切り方をしているので、参考にしないでください。 そのうち書き直したいんですが時間がないの。(2001/10 itojun)

概要

とりあえず、 cvsは便利だー、というはなし、である。 注意: このtutorialで使っているimportのしかたはとっても非標準的で、 普通の開発プロジェクトでは使いにくい可能性が高いです。 cvsのinfo fileとか見た方がいいんではないかと思います。

まえがき

hackとか研究とかしている場合、FreeBSD (註: 必要に応じて適当にBSD/OSとかに読み変えてください)の カーネルに手をつっこんだり、あるいは/sbin/ifconfigを改造したり、 なーんてことをよくやる。
で、たまにあるのがこんな悲しいストーリー: せっかくがんばってるのに、こんなのは悲しすぎます。 というわけで、よい道具を使ってちょっとでも楽になろう。

そこで、cvsを使いましょう。 cvsが威力を発揮するのは、

です。 実は、ひとりでhackしている場合でも、ひとのソースの改造をしている 場合には、FreeBSDチーム側で勝手にバージョンアップがあるわけです。 ってことは、結局複数名で開発してるのとほとんど等しいわけですね (バージョンの枝わかれのしかたがちょっと違いますが、まあそれは忘れよう)。

以下の説明には、簡単のために省略していることや、わたしが誤解していることが あると思います。 まあ、とりあえず入門書ってくらいなので、多めにみてください。 ほんとのことはman pageとかinfo fileとかに書いてあります。 「これはひどい」ってのがあったら 教えてください。直します。


cvs入門

ちょっと時間がないので割愛。

とりあえず、cvsは(beta versionですが)1.9.10かそれ以降を使ってください。 1.8だとdiffの生成にちょっと虫さんがいるように思います。 1.9だとファイルの追加やsticky tagの処理に虫さんがいるような気がします。


その1: カーネルの改造作業をする

「独自なカーネルの改造をしつつ、FreeBSD本家のバージョンをおっかけ続ける」、 作業をしたいと思いねえ。

ステップ0: イメージトレーニング

だいたい、バージョンの枝わかれはこんなふうになる。
FreeBSD 221-RELEASE === FreeBSD 222-RELEASE ===>
  \			  \
   \			   \
    改造版221 ----->        改造版222 ----->
もとになるOS、仮にここではFreeBSD 2.2.1-RELEASEになっているけど、 それが最初の親玉バージョンになる。 で、そこから枝別れして自分の改造版(改造版221とか改造版222とか)を つくっていくわけだ。 FreeBSDが2.2.2-RELEASEになったら、2.2.2-RELEASEを本流にとりこんで (cvsではこれをimportという)、 FreeBSD 2.2.1-RELEASEと改造版221の差分をあてて改造版222をつくり、 さらにつき進む。
ここで、===>はvendor branchって呼ばれている。

恐がることはなにもない。 失敗したら最初からやりなおせばいいだけの話。

ステップ1: リポジトリの準備

まず、リポジトリ(cvsの使うソースツリー格納場所)の準備をする。 適当なディレクトリを掘り、その場所へのフルパスを環境変数CVSROOTに 設定する(この環境変数はこの先常に設定しつづけておいてほしい)。 さらに、リポジトリの初期化をする。
% cd ~
% mkdir practice
% mkdir practice/cvsroot
% setenv CVSROOT ~/practice/cvsroot	# 環境変数設定
% cvs init				# 初期化

ステップ2: 最初のバージョンのimport

最初のバージョン、FreeBSD 2.2.1-RELEASEをimportしよう。 importの際に気をつけることは以下のふたつ:
% cd /usr/src/sys
% cvs import -ko sys FREEBSD r221
ここでsysはモジュール名である。 今後、さっき設定したcvsリポジトリでは、「モジュールsys」には FreeBSDのカーネルソースがそのままつっこまれている状態になる。 FREEBSDはvendor tagというもんで、vendor branchの名前である。 r221はrelease tagというもんである。 まあ、めんどくさいことは覚えなくていい。 「r221って言うと、いまimportしたFreeBSD 2.2.1-RELEASEのソースがでてくる」 とだけ覚えていればいい。
FreeBSD 221-RELEASE === FreeBSD 222-RELEASE ===>
  \			  \
   \			   \
    改造版221 ----->        改造版222 ----->

試しに、FreeBSD 2.2.1-RELEASEのソースをとりだしてみよう。

% cd /tmp
% mkdir x
% cd x
% cvs co -r r221 sys
ほーら、取り出せたでしょう。

ここで注意しないといけないのは、 r221みたいな、基本の枝には変更を加えちゃいけない ということである。 これをやってしまうと、あとでdiffとれなくなるのだよ。 次に枝わかれのさせかたとかをやるので、変更するならr221以外の枝について 作業をしましょう。

ステップ3: 枝わかれさせよう

さて、これからが本番なり。 r221から、itojun221という枝(branch)をつくろう。 この作業はどこのディレクトリでやっても構わない。
% cvs rtag -b -r r221 itojun221 sys
この作業で、図の赤いところができたことになる。
FreeBSD 221-RELEASE === FreeBSD 222-RELEASE ===>
  \			  \
   \			   \
    改造版221 ----->        改造版222 ----->
試しに、いま作ったitojun221をとりだしてみよう。
% cd /tmp
% mkdir y
% cd y
% cvs co -r itojun221 sys
FreeBSD 2.2.1-RELEASEとおなじものが取り出せたはずだ。

ステップ4: 改造しよう

いまつくったitojun221に改造を加える。 まずはitojun221のツリーを取り出す。 もうやっているかもしれないが、こうやると取り出せる:
% cd /tmp
% mkdir y
% cd y
% cvs co -r itojun221 sys
そしたら、取り出したツリーの上でおもむろに作業をしよう。 変更にはいろいろな種類があるが、だいたい以下のようなもんである。 rcsとちがって、編集前のロックは必要ない。 基本的には「変更してからcvs commit」である。
ファイルを変更する
とりあえずいきなり編集してしまってよい。
編集が終ったら、cvs commitとすると、itojun221の枝の 最新版としてリポジトリにとりこまれる。 cvs commitはディレクトリ木全体に働くので、 ある木の下を全部commitしたければ、てっぺんでcommitしたらいい。
ちなみに、どの枝に対して変更が加わるかは、 「どの枝をcvs coしたか」で決まる。 各ディレクトリにCVSていうディレクトリがあるはずだけど、 ここに覚えておいてもらえるのだ。 たいていはこれで問題ないはずだ。
「リポジトリに」ディレクトリを追加する
ディレクトリのあるところに行って、cvs addする。
% ls
foo/	baa/	baz/
% cvs add foo
追加をリポジトリに反映させるためには、cvs commitすること。
別にcvsに管理してもらいたくないディレクトリをほるのは、普段どおりで 構わない。
やまもりディレクトリ/ファイルを追加するときはcvs importを 使いましょう。
「リポジトリに」ファイルを追加する
リポジトリ上で管理されるファイルを増やしたいときは、 ファイルのあるところに行って、cvs addする。 再帰的にaddしたり、"/"を使ってパスを書いたりはできないので、 addしたいファイルがやまほどあるときは各ディレクトリを訪問すること。 それから、親ディレクトリは全部先にcvs addしておかないとダメ。
追加をリポジトリに反映させるためには、cvs commitすること。
てもとでファイルを置いておきたいだけなら、そのまま置いておいていい。
やまもりディレクトリ/ファイルを追加するときはcvs importを 使いましょう。
「リポジトリから」ファイルを削除する
リポジトリ上で管理されてたファイルの管理をやめさせたいときは、 もとになるバージョン(FreeBSD 2.2.1-RELEASE)にはあるんだけど、 改造版には要らないファイルを削除するには、cvs remove -fを 使う。
% ls
foo	baa	baz
% cvs remove -f foo
削除したことをリポジトリに反映させるためには、 cvs commitすること。
てもとでファイルを削除したいだけなら、いきなり削除していい。
ファイルをリネーム
これはちょっとめんどくさいので、cvs FAQをよんでください。 「リネーム」の場合に、バージョン情報をどう扱うのかは結構難しい 問題なので、利用者のセンスがちょっと問われる。 簡単に言えば、単純にremoveしてaddすると、バージョン 管理情報が引き継がれないのです。 引き継がれないと困ることもあるし、引き継がれると困ることもありますよね?
差分をとる
リポジトリに入ってるitojun221の状態と、 てもとのディレクトリの差分をとるには、こうする。 全部commitしてあれば、なにも表示されないはずだ。
% cvs diff
context diffがいいな、とかいう場合には-cをつける。
% cvs diff -c
特定の枝の最新版と作業ディレクトリの差分をとる (例: 2.2.1-RELEASEとの差分) ときには、-r 枝の名前とすればいい。
% cvs diff -c -r r221		# 2.2.1-RELEASEと作業ディレクトリの差分
特定の枝の最新版どうし(例: 2.2.1-RELEASEと、itojun221)の 差分をとりたくなったら、-rをふたつつける。 現在いるディレクトリについての差分が見れるはずだ。
% cvs diff -c -r r221 -r itojun221	# 2.2.1-RELEASEとitojun221の差分
注意: GNU patch 2.5は、cvs diffで作ったdiff fileを 処理できない。 Index:行を優先して見るpatchコマンドでないとダメ。 今後のcvsのバージョンアップで状況は改善されるかも、しれない。
変更状況を見る
commit前にファイルの変更状況を見たいときは、cvs -n update とする。 本来は他のひとがリポジトリを書き換えたときに、その状況を てもとのディレクトリに反映するコマンドなんだが、どうせいまは ひとりで作業していることになってるので、問題なし。
行頭の文字の意味はこんなかんじ。
A: addされていてまだcommitされてない
M: 変更されていてまだcommitされてない
R: 削除されていてcommitされてない
?: リポジトリに入ってないファイル。入れのこしがないよう 気をつけよう(*.oとか、.cvsignoreに羅列された ファイルは無視される)。
リポジトリに入れたくないファイルはどうしよう?
cvsは*.oみたいな、よくある「無視してほしいなってファイル」を 無視してくれるようになっている。 が、場合によってはyacc使ったりするときのように、*.cなのに 無視させたいファイルがでることがある。
そういうのを指定しておくには、各ディレクトリに.cvsignoreって ファイルを置こう。 .cvsignore自体もcvsで管理するのを忘れずに。
% echo 'foo.c' > .cvsignore
% cvs add .cvsignore
他には?
思い付いたら追加します。 知りたいことがあったら言ってね

ステップ5: コンパイルはどうするの?

cvsは、明示的にcvs addされたファイルだけを管理対象にするので、 いま作業している作業ディレクトリで試しにコンパイルしても全く差し支えない。 というわけで、安心してコンパイルしましょうね。

sys/compileの下が邪魔なら、.cvsignoreで制御してあげればよいと 思います。

ステップ6: FreeBSDの配布をおっかけよう

さて、もとになっているカーネルソース、FreeBSDの次のバージョン (2.2.2-RELEASEってことにしておこう)がでたら、 ってのをしないといけませんね。

まず、2.2.2-RELEASEのソースをリポジトリに取り込みます。

% cd /usr/src/sys		# 2.2.2-RELEASEのソースツリー
% cvs import -ko sys FREEBSD r222
2.2.1-RELEASEのときとはrelease tagが違うのに注意。 これでここまでできました。
FreeBSD 221-RELEASE === FreeBSD 222-RELEASE ===>
  \			  \
   \			   \
    改造版221 ----->        改造版222 ----->
次に、2.2.2-RELEASEから枝わかれした、itojun222という枝をつくります。
% cvs rtag -b -r r222 itojun222 sys
これでここまでできました。
FreeBSD 221-RELEASE === FreeBSD 222-RELEASE ===>
  \			  \
   \			   \
    改造版221 ----->        改造版222 ----->
さて、ではr221とitojun221の差分をitojun222へマージしましょう。 必ずしもすんなりいくとは限りませんが、まあとりあえず自動でいってみます。
% cd /tmp
% mkdir z
% cd z
% cvs co -r itojun222 sys
% cd sys
% cvs update -j r221 -j itojun221
これで、すんなりいく部分についてはすんなりいくはずです(意味不明)。 すんなりいかなかったファイルについては、
<<<<<<< foo
nantara kantara
=======
kantara hontara
>>>>>>> r1.1.1.1.2.1
のように、両方のバージョンがファイルに取り込まれていますので、 手でがんばってマージします。

その2: もっとらくちんになろう

バージョン間の差分をほいほいクリック猿でとりたくはありませんか? たとえば、ここ みたいに。
このためには、 cvsweb配布元からcgiスクリプトをとってしかけます。 環境変数CVSROOTの内容をスクリプトのなかに設定しておく必要があります。 もとのツリー(2.2.2-RELEASE)にないファイルで増えたファイルは、 レポジトリ上はAtticというサブディレクトリに入ってるので注意ね。

他にもtcl/tkなフロントエンドなんかもあるらしいが、使ったことないので割愛。


その3: 配布キットの準備をする

誰かにこの素敵なkernel patchを配布したくなったら、こうすればok。
% cvs rdiff -r r221 -r itojun221 sys >sys.diff
rdiffdiffの違いは、 ってなかんじである。 増えたファイルはちゃんと/dev/nullとのdiffをとってくれるので、 安心である。

とりあえず、diffの作成が楽になっただけでもcvsを導入した甲斐はある、 というものだ。


その4: 複数のノートパソコンで開発する!

これこれ、これが書きたかったのだ。

複数台のノートパソコンを使い分けている場合、ソースがそこらじゅうに ちらばったりしませんか? その場合、

とすると、ノートパソコンからデスクトップ上のリポジトリをいじれます。

やりかたは簡単で、

% setenv CVS_RSH ssh		# rsh使うなら不要
% setenv CVSROOT desktop.itojun.org:/usr/home/itojun/practice/cvsroot
とするだけ。 ほんとにこれだけ。 あとは前と全く変わることなく作業ができます。

たとえば、2台のマシンのカーネルを揃ってバージョンアップして実験したいとき とか、cvs coで最新版カーネルを持って来てコンパイルなんてことが できます。 マシンAで変更した内容をcvs commitしたら、マシンBで cvs update -dとすると、 変更が反映されますのでソースの同期も自由自在です。

「うまくいくんかい」というあなた、FreeBSDとかNetBSDとかがちゃんと 開発できてるのをみれば一目瞭然でしょう。 開発者は全世界に散っていて、ひとつのリポジトリに対して remote cvsしてうまくいってるわけです。 ましてやわれわれの場合、開発者はひとりなのだから、 問題が起きたら自分で反省すればいいだけの話(笑)。 面倒な議論や相談は要りません (もちろん、研究室内とかwide内とかで複数人開発してもいいすけど、 その場合いつバージョンをfreezeするのかい、とかそういう問題が 出るので、別のむずかしい問題に挑戦するハメになるのことね)。


その5: 複数人で開発する!

上記でうっすら述べた複数人開発のはなしだ。

まずはリポジトリが開発メンバ全員からgroup readable/writableであるか 確認してください。 必要ならchgrpする。

各個人のやる作業はほとんど変わりませんが、commitだけが違います。

複数ひとがいるときは、各個人がてもとに作業用のソースコードツリーを cvs coしていることになります。 また、「ぼくがcheckoutした」時点よりあとに、誰かがcommit作業を してるかもしれません。ので、てもとのツリーは「リポジトリの最新の 状態に対する変更」であるとは限りません。

ので、cvs commitするまえに、以下の手順で てもとの木を、「リポジトリの最新の状態に対する変更」にしてください。

  1. commitの作業をはじめるまえに、リポジトリが更新されていないか 調べるため、cvs updateを実行する。 これで「リポジトリの最新状態」と「自分の手もとでやった変更」が マージされます。
    註: 他のひとがファイル/ディレクトリを増やしている場合、 cvs updateだけでは十分に更新されない場合があります。 cvs update -d -r itojun221とかやると、新しく掘られた ディレクトリについてもきちんと更新されます。
  2. cvsがうまくマージできなかったところを手で直す。
  3. 動作確認をする。
  4. もっぺんcvs updateする。 また更新されていたらマージと動作確認をくりかえす。
また、makeくらいは通してからcommitしましょう。 中途半端な状態でcommitしない。 どうしても作業が長期にわたりそうで中間状態をcommitしないといけない場合、 自分の作業用の枝をつくって作業しましょう。

上記の手順をよめば、ふたりで並行にcommit作業をすると永遠に終らない(笑) ことがわかるでしょう。 ので、cvsだけに頼らないで、mailとか音声で共同作業者に 「いまからcommitするんでよろしく」とか叫びましょう。 叫ぶのと叫ばないのとではだいぶん苦労が違います。 ちゃんと叫べば、「中途半端な状態でcommitしない」制約も緩くなります。

複数人開発の場合、「いつリリースするか」の制御はかなり難しくなります。 わたしもまだ甘ちゃんなのでこれについてはふれません。 厳しくやるなら、リリース用の枝をほらないといけないかも。


cvsの向き不向き

バイナリファイルは多分扱いにくいでしょう(そんなもん扱うことあるんかい?)。

自分でスクラッチから書いていて、バージョンの枝わかれもしそうにない ソースの場合は、rcsの方が楽な場合もあるかもしれない。 まあ、rcsでいいやと思う間はrcsでもいいでしょうね。

rcsとの違いは、こんなもんかな。


もっと高級テクニック

branchとtagの違いについて

cvsでは、「branch」つまり枝と、「tag」という概念が存在する。 上記では故意に「branch」と「tag」の区別をあいまいにしたまま 記述していた(というか、tagのことはなるべく避けて記述した)。 実は、branchとtagは違うものである。

あるbranchから枝わかれしてできたバージョンの並びを「branch」、 branch上のあるバージョンについた名札を「tag」という。 ここでややこしいのが、 「あるbranch xの先頭のバージョンには、 "x"という名前のtagがついている」ということ。 例えば、itojun221 branchの先頭のバージョンには、 「itojun221」というtagがついている。

r221 ================== r222 ======>			vendor(FREEBSD) branch
  \			  \
   * -- * -- itojun221	   \				itojun221 branch
			    \
			     * -- itojun222		itojun222 branch
アスタリスクはなんか適当なバージョンである。 図ではitojun221 branchを作ってから3回、itojun222 branchを作ってから 2回commit作業をしている。 新しいバージョンがcommitされると、itojun221とかitojun222とかの 「branch名とおなじ名前のtag」は先頭のバージョンに移動する。 例えば、itojun222 branchに新しいバージョンがcommitされると、 「itojun222」というtagは新バージョンをさすようになる。
r221 ================== r222 ======>			vendor(FREEBSD) branch
  \			  \
   * -- * -- itojun221	   \				itojun221 branch
			    \
			     * -- * -- itojun222	itojun222 branch
実はcvs co -r itojun222 sysなどのオペレーションで指定していた 「itojun222」は、「itojun222」というtagだったのだ。 このおかげで、-r itojun222と言えば必ずitojun222 branchの先頭の バージョンがもらえるしかけになっている。

ある時点のツリーにtagをつける

(to be written)

sticky tag

(to be written)

commitがあったらmailing listに通知する

(to be written)

cvsのバージョンによる違い

いままで感じていることを書いておく。 とにかく、1.9.22またはそれ以降の方が調子いいような気がする。 「1.9.22」のように3つの数字で構成されているのはbeta releaseなのだが、 official releaseの1.9よりもbeta releaseの方がよい。 現時点(1998/6/24)で一番のおすすめは1.9.26である。

beta releaseはhttp://www.cyclic.com/から たぐってゲットすること。


To be done


役立つページたち


[itojun] [はっかーずぱらだいす]
感想とかを投げてみる