minimize

事業拡大のため、新しい仲間を募集しています。
→詳しくはこちら

Subversion とは、CVSに代わる次世代のバージョン管理システムです。
CVSに似たモデルを採用しつつ、CVSにあった欠点のほとんどが解消されています。
以下、SubversionをSVNと略します。

CVSとの違い

まずSVNを使ってみてCVSと違うところと言えば、
リビジョン番号の扱い方でしょう。
CVSではファイル毎にリビジョン番号が付いていますが
SVNでは「ツリー全体」に対してリビジョン番号が付けられています。

例えば、あるプロジェクトツリーでA,B,Cという3つのファイルがあったとします。

A 1.1
B 1.3
C 1.5

ファイルの右側に付いているのがリビジョン番号です。
ファイルそれぞれに付いています。
これらのファイルを全て編集してコミットすると、リビジョン番号は以下のようになります。

A 1.2
B 1.4
C 1.6

ツリー全体に付くリビジョン番号

これに対して、SVNではどうなるでしょう。

A 12
B 12
C 12

SVNではツリー全体にリビジョン番号が付けられるので
全てのリビジョン番号は同一です。
では、同じようにファイルを編集してA,B,Cの順にコミットしてみます。

A 13
B 14
C 15

リビジョン番号が一つずつ上がっています。
ここで「あれ?」と思った人はなかなか鋭いです。
SVNではツリー全体にリビジョン番号が付けられると説明したのに
ファイル毎にリビジョン番号が異なっていますね。

この状態でSVNをUpdate(更新)してみましょう。
すると、以下のようになります。

A 15
B 15
C 15

このように、リビジョン番号が同一になりました。
う〜ん、不思議ですね。少し説明しましょう。

Update(更新)しないとリビジョン番号は変化しない

SVNでは、コミットする度に「ツリー全体の」リビジョン番号が一つ上がります。
しかし、そのファイルに対してUpdate(更新)をしない限りリビジョン番号は変化しないのです。
この挙動は、CVSでも同じです。

先程の例で言うと、Aをコミットしたときに
「A,B,C全てのリビジョン番号が13になる」のです。
しかし、BとCはUpdateしていないので
ローカル上に表示されるリビジョン番号は12のままなのです。

この段階で、Bはまだコミットされていません。
つまり、リビジョン番号12と13のBは「同一」です。
次にBをコミットすると、A,B,C全てのリビジョン番号が14になります。
このとき、リビジョン番号13と14のAは「同一」ですが
リビジョン番号13と14のBは「異なり」ます。
最後にCをコミットすると、A,B,C全てのリビジョン番号が15になります。

このように、SVNではリビジョン番号がツリー全体に付けられているので
初めのうちは戸惑うことがあります。
ここでもう一度。SVNのリビジョン番号は
「ファイル単位」ではなく「ツリー全体」に付けられます。

チェンジセット

SVNでは、チェンジセットという概念を持っています。

CVSでは、A,B,Cの3ファイルを一つずつコミットしても
同時にコミットしても動作は同じでした。

SVNではこれが異なります。
先程の状態(リビジョン番号15)で、A,B,Cのファイルを今度は「同時」にコミットしてみます。
すると、リビジョン番号は以下のようになります。

A 16
B 16
C 16

このように複数のファイルを同時にコミットすると
リビジョン番号は「一つだけ」上がります。
SVNに対してコミットするときは、この点に注意しておくと
後々の管理がしやすくなります。
まとまった修正などは必ず一括でコミットするようにしましょう。

ディレクトリの扱い

CVSでは、ディレクトリをバージョン管理することは出来ませんでした。
ディレクトリはあくまでファイルの格納先に過ぎないため
空ディレクトリをコミットする事は出来ない等の制限がありました。

SVNではディレクトリもファイルと同じように扱われるので
問題なく使うことが出来ます。

ファイル名の変更、ディレクトリ移動

CVS最大の欠点とも言えるのがこれです。
ファイル名を変更すると、以前までの履歴は全て消え
変更後のファイルはリビジョン番号1で新規作成されてしまいます。
ディレクトリを移動する場合も同じです。
これによって、CVSではファイル名を変更したりディレクトリを移動する事は
履歴を消してしまうという意味で推奨されない行為になってしまったのです。

SVNでは、ファイル名を変更してもディレクトリを移動させても
その履歴は失われずに保持されます。
Javaのプロジェクトでは、リファクタリング操作により
頻繁に(クラス分割などにより)クラス名が変わったり
(パッケージ変更などにより)ディレクトリの移動が行われます。
SVNを使っていれば、これらの操作を何の躊躇いもなく行うことが出来ます。

しかし、IDE対応は正直言ってまだまだです。
SVNのEclipseプラグインもありますが、
リファクタリングでファイル名を変更したときに上記の対応がされません。
CVSのときと同じように「削除して作成する」という行為になってしまうのです。
ここら辺は、今後に期待しましょう。

ローカルキャッシュ

現在編集中のファイルを、CVSリポジトリの最新のものと比較するとき
CVSは当然のようにCVSサーバと通信を行いその結果を返します。

しかし、これは多くの場合無駄な処理なのです。
なぜなら、そのファイルを誰か別の人がコミットしていない限り
CVSリポジトリの内容は「前回ローカルにUpdateした状態」と
変わっていないからです。

SVNでは、Updateした段階でそのファイル内容を
.svnフォルダ内にコピー(キャッシュ)します。
以後、編集中のファイルを比較するときは
このキャッシュファイルと比較すればいいので、外部と通信に行かなくていいのです。

また、SVNはコミットするときにもキャッシュファイルを利用します。
キャッシュファイルと比較して、「異なる部分」だけをSVNサーバに送信するので
ネットワーク使用量が抑えられます。
通常、修正箇所というのはファイルの一部分に過ぎないので
こうした方が効率が良いのです。

CVSもそうでしたが、SVNの処理は決して早いとは言えません。
それを補う意味でもキャッシュの仕組みは有効なのです。

Branch(枝)について

CVSにもSVNにもBranch(枝)という考え方は存在します。
しかし、両者には大きな違いがあります。

CVSのBranchは、使ったことがある人ならわかると思いますが
非常にややこしい作りになっています。
「Branchは使うな」という不文律があるくらいです。

これに対して、SVNのBranchはとってもシンプルです。
SVNは内部的にBranchという概念を持っていません。
単純に、あるリビジョンの「コピー」を作ることでBranchを実現しています。

.
`-- trunk
    |-- A 4
    |-- B 4
    `-- C 4

上のようなリポジトリがあったとします。
trunkとは、最新の枝という意味で付けています。
ここから、現在の状態をBranchとして分けてみましょう。

.
|-- trunk
|   |-- A 5
|   |-- B 5
|   `-- C 5
`-- branch
    |-- A 5
    |-- B 5
    `-- C 5

branch には、trunk と全く同じ状態のA,B,Cがコピーされました。
次に、branch/A に修正を加えてコミットします。

.
|-- trunk
|   |-- A 6
|   |-- B 6
|   `-- C 6
`-- branch
    |-- A 6
    |-- B 6
    `-- C 6

branch/A には修正が加えられた後の内容が格納され、
trunk/A には修正を加える前の内容がそのまま格納されています。
branch/A は trunk/A からコピーされたので祖先は同じですが
その後 branch/A は trunk/A とは全く別のものとして扱われるのです。

ここが、CVSとは大きく異なる点です。
CVSではBranchに分けた後のファイルも最新枝のファイルも
同じファイルとして扱われるので、その後の扱いが非常に面倒になるのです。

そして、Branchを作成するためのコピー作業は
SVNのマニュアルによれば「プロジェクトの大きさに関わらず、一定の時間」
しか掛からないそうです。
SVN内部では実際にファイルをコピーしている訳ではないからです。

バイナリファイルの扱い方

CVSでは、ファイルをバイナリ形式とテキスト形式に分けていました。
さらにテキスト形式では何種類ものキーワード置換パターンがあり
これが原因でトラブルが発生することも多かったようです。

SVNでは、バイナリやテキストなどの違いはありません。
コミットされたファイルは「一切の変更が行われずに」SVNに記録されます。
文字コードの扱いなども気にする必要はありません。
そういった処理は、クライアント側で責任を持って下さいというスタンスです。
もちろん、オプションによって日付の置換などをする事も可能です。

CVSでは、バイナリファイルの格納は常に
差分ではなくファイル全体を格納していたので非常に効率が悪かったのですが
SVNでは全てのファイルをバイナリ形式の差分で
圧縮してリポジトリに格納するので
バイナリファイルを積極的に扱うことが出来ます。

その証拠に、SVNは自身をソース管理の枠にとらわれずに
あらゆるリソースのバージョン管理に使うことを
推奨しています。

実際の使われ方

CVSからの改良点は、実際の開発時に大きな効果を発揮します。

例えば、リビジョン番号がツリー全体に付けられていることを利用すると
以下のようなメリットがあります。

スナップショット

「○月○日の状態を取り出したい」といった場合、CVSならば
スナップショットを取る手法が一般的でした。
しかし、スナップショットを取るのは
プロジェクトが大きくなればなるほど時間も掛かり、何より面倒です。
うっかりスナップショットを取り忘れたことを悔やんでも
もう後の祭りです。

SVNでは、リビジョン番号がツリー全体に付けられています。
つまりこれは、各リビジョンがそれぞれスナップショットであるという事です。
「○月○日の状態を取り出したい」という要望は
「リビジョン番号○○の状態を取り出したい」という要望と同じです。
リビジョン番号はコミットをする度に付けられるので
取り忘れるという心配はしなくていいのです。

チェンジセット

プロジェクトが保守段階に入ると、ファイルの修正はバグや仕様修正などの
単位になることが多くなります。

「バグ○○に関する修正内容を確認したい」
こんな要望はよくあります。
しかし、CVSではこの要望に答える完璧な回答がありません。
あえてやるとするならば、修正前の状態をスナップショットとして撮っておいて
修正後に再びスナップショットを撮り
その差分を比べるといったやり方でしょうか。
こんな面倒な事をするくらいなら、修正内容を別紙にでも書き出した方が早いでしょう。

SVNはチェンジセットという概念を持っています。
複数のファイルを一括でコミットすることによって
ツリー全体のリビジョン番号は一つだけ進みます。
「バグ○○に関する修正内容を確認」するためには
つまり、「リビジョン○○で修正された内容」を取得するだけでいいのです。

Private Branch

前述した通り、CVSでは推奨されなかったBranchの作成も
SVNでは積極的に行うことが出来ます。

プロジェクトを進行中、比較的修正範囲の大きい仕様変更があったとします。
正にこういった時のためにBranchがあるのです。
まず、現段階での最新ツリーをBranchとしてコピーします。

そして、このPrivate Branch上のファイルに変更を次々とコミットしていきます。
Branchが分かれているので、他の開発者はこれらの変更を
全く気にすること無く開発を進めることが出来ます。

修正が完了したら、その修正をtrunk(最新の枝)にマージします。
マージが完了したら、Private Branchは削除してしまいましょう。

Branchの考え方

CVSでコミットをするタイミングで
SVNは随時スナップショットを撮っています。

そして、CVSでスナップショットを取る(タグ付け)タイミングで
SVNではブランチを作成すればいいのです。
通常のプロジェクトなら、リリースのタイミングがそれに当たるでしょう。
これは、先程のPrivate BranchではなくPublic Branchと呼ぶことが出来ます。

CVSではタグとBranchの概念が分かれていましたが
SVNではこれらの違いは特にありません。
さらに言えば、通常のディレクトリとの違いさえ無いのです。

ディレクトリを作成して「これはBranch」と自ら宣言すれば
それがBranchです。
そのディレクトリの中身を変更しないという決まりにすれば
それがスナップショットです。
シンプルでありながら柔軟な使い方が出来るのが
SVNの便利なところです。

WebDAVとの連携

SVNリポジトリはいくつかの方法で外部から参照することが出来ます。
一つは、cvspserverと同じように専用サーバを起動する方法です。
SVNでは svnserve というプログラムが用意されているので
これを利用します。

もう一つは、Apache2と連携させる方法です。
WebDAVというのをご存知でしょうか。
これは、HTTP経由でファイルシステムを扱うプロトコルで
Windowsならば「マイ ネットワーク」から「ネットワーク プレースの追加」で
通常のフォルダと全く同じようにHTTP上のディレクトリを扱うことが出来ます。

SVNをApache2と連携させると、さらに高度な事が出来るようになります。
それが「自動リビジョン管理」です。

まず、SVNリポジトリをWebDAVとして公開します。
先程説明したように、このフォルダは通常のフォルダと同等の扱いになります。
そしてここのファイルを修正して保存してみましょう。
なんと、それだけで「SVNへコミット」したことになるのです。

つまり、バージョン管理のことを全く知らない人でも
SVNが使えるのです。
共有のドキュメントフォルダなどにこの仕組みを採用すれば
自動的に履歴が残されるので非常に便利です。

ソース管理とソースの関係

CVSでソース管理をしているにも関わらず、ソースに
以前の履歴や修正内容が残されているのを見たことはありませんか?

これらの記述は、大昔にソース管理ツールなどというものが
存在しなかった時代に用いられていた手法です。
こういった記述は、修正を繰り返す度にソースを汚していきます。

これを改善するためにソース管理システムがあるのです。
修正内容は、SVN(CVS)のコミットコメントとして残すので
ソース上に記述する必要はありません。
コメントアウトしている箇所は、もうそれが必要無いと判断した時点で
遠慮なく削除しましょう。

リポジトリ内容

CVSでは、リポジトリの内容はファイル単位に分かれています。
それに比べて、SVNはリポジトリの内容をデータベースに格納しています。
つまり、管理者が直接リポジトリの内容を見ることは出来ません。

これ自体は、別に気にするべき問題ではないでしょう。
僕も以前、CVSリポジトリ内のファイルを
直接削除したりした事がありますが
それがSVNで出来ないからといって何も困りません。

なぜなら、そんな(リポジトリの内容を直接いじるような)場面に陥る必要がないように
SVNは作られているからです。

SVNの作者はこんな事を言っています。
「MySQLのデータが(バイナリ)ファイルに格納されている事で誰か混乱を起こしますか?」
その通りです。これと同じように、リポジトリの内容が
データベースに格納されているからといって何も心配する事は無いのです。

SVNのススメ

以上を踏まえて、2006年3月現在
SVNはCVSから乗り換えられるだけの実績と安定性を
持っているようです。

僕も、半年くらい前から新規作成したプロジェクトのバージョン管理には
SVNを使うようになりました。
今のところ何の問題も起きていません。

以前はEclipseから使うときに文字化けの問題もあったのですが
現在ではそれも解消されています。
CVSから置き換わる日も近いでしょう。