SVN の基本用語とコマンド
SVN (Subversion) は、中央リポジトリを基準にソースコードと変更履歴を管理するバージョン管理システムである。 Git のように各開発者がリポジトリ全体の履歴をローカルに複製する方式ではなく、中央リポジトリにあるプロジェクトを作業コピーとして取得し、修正して再度コミットする流れで使用する。
この文書では、SVN を初めて使う開発者がよく目にする用語と基本コマンドを、実務の流れに合わせて整理する。
基本用語
Repository
Repository は、プロジェクトのファイルと変更履歴を保存する中央保存場所である。
ソースコードだけでなく、ファイルの追加、修正、削除、ディレクトリ構造の変更といった履歴も一緒に保存される。
SVN リポジトリは通常サーバー上に置き、複数の開発者がネットワーク経由でアクセスする。
アクセス方式は環境に応じて http://、https://、svn://、svn+ssh://、file:// などを使用できる。
https://svn.example.com/repos/sample
svn+ssh://svn.example.com/svn/sample
file:///var/svn/sample
運用環境では、プロジェクト単位でリポジトリを分けるか、1 つのリポジトリ内に複数のプロジェクトディレクトリを置くかを選択する。 権限管理、バックアップ範囲、デプロイ単位が異なる場合は、リポジトリを分ける方が管理しやすい。
Revision
Revision はリポジトリの変更番号である。
SVN ではファイルごとにリビジョン番号が個別に増えるのではなく、リポジトリにコミットが 1 回発生するたびにリポジトリ全体のリビジョン番号が増える。
たとえば sample.c という 1 つのファイルだけを修正してコミットしても、リポジトリのリビジョンは r10 から r11 に増える。
このリビジョン番号を使うと、特定時点のソースを再取得したり、変更履歴を比較したりできる。
svn log -r 10:20
svn diff -r 10:11 sample.c
svn checkout -r 10 https://svn.example.com/repos/sample/trunk sample-r10
trunk
trunk はプロジェクトの中心となる開発ラインである。
一般的に日常的な開発作業は trunk で行う。
sample/
trunk/
src/
pom.xml
チームの規則によって異なるが、trunk は常にビルド可能な状態を維持するのが望ましい。
大きな機能開発や長期間の修正は branches に分け、安定したら trunk へマージする。
branches
branches は trunk から分岐した開発ラインである。
リリース保守、大規模な機能開発、実験的な作業のように、trunk と分離して作業する必要がある場合に使用する。
sample/
branches/
feature-payment/
release-1.0/
ブランチはディレクトリのコピーのように見えるが、SVN 内部では変更履歴を保持したコピーとして管理される。
ブランチを作成するときは通常のファイルコピーではなく svn copy を使用する。
svn copy https://svn.example.com/repos/sample/trunk \
https://svn.example.com/repos/sample/branches/feature-payment \
-m "Create feature-payment branch"
tags
tags はリリース時点のソースを保存するディレクトリである。
たとえば 0.1、0.2、1.0 のようなリリースバージョンをタグとして残す。
sample/
tags/
0.1/
0.2/
1.0/
SVN のタグも技術的にはリポジトリ内のコピーである。 そのため、権限やチーム規則によってタグディレクトリを変更できないようにしておくのがよい。 タグはデプロイ時点のスナップショットとして扱い、タグ配下で直接開発しない。
svn copy https://svn.example.com/repos/sample/trunk \
https://svn.example.com/repos/sample/tags/1.0 \
-m "Tag release 1.0"
基本作業フロー
SVN の基本作業は、リポジトリからソースを取得し、ローカルで修正した後、変更内容を確認し、中央リポジトリへコミットする順序で進める。
svn checkout svn+ssh://svn.example.com/svn/sample/trunk sample
cd sample
svn update
svn status
svn diff
svn add new-file.c
svn commit -m "Add new file"
コミット前には必ず svn update で最新の変更を取り込み、svn status と svn diff で自分が送信する変更を確認する習慣を付けるとよい。
主なコマンド
import
svn import は、まだ SVN で管理されていないローカルディレクトリをリポジトリに最初に登録するときに使用する。
プロジェクトを初めてリポジトリへアップロードするときに主に使用し、すでにチェックアウトされた作業コピーで日常的に使うコマンドではない。
svn import sampledir svn+ssh://svn.example.com/svn/sample/trunk \
-m "Initial import"
import はローカルディレクトリをリポジトリへアップロードするが、そのローカルディレクトリを自動的に作業コピーへ変換するわけではない。
初回登録後の実際の開発は、改めて checkout して進める方が安全である。
svn checkout svn+ssh://svn.example.com/svn/sample/trunk sample
checkout
svn checkout は、リポジトリのファイルをローカル作業コピーとして取得するコマンドである。
作業コピーには実際のソースファイルと、SVN が変更状態を追跡するためのメタデータが含まれる。
svn checkout svn+ssh://svn.example.com/svn/sample/trunk sample
短く co という別名も使用できる。
svn co svn+ssh://svn.example.com/svn/sample/trunk sample
チェックアウトしたディレクトリ内では、通常のファイルと同じようにコードを修正できる。 ただし、ファイルの追加、削除、移動、名前変更は、できるだけ SVN コマンドで処理することでリポジトリ履歴を正確に残せる。
export
svn export は、バージョン管理メタデータなしで純粋なファイルだけを取得するときに使用する。
デプロイ用のソース一式や、一時的な確認用ディレクトリが必要な場合に便利である。
svn export svn+ssh://svn.example.com/svn/sample/trunk sample-release
特定リビジョンのファイルだけを取得することもできる。
svn export -r 120 svn+ssh://svn.example.com/svn/sample/trunk sample-r120
export で取得したディレクトリは作業コピーではないため、svn update、svn commit、svn status などの操作はできない。
継続して開発するディレクトリが必要な場合は、export ではなく checkout を使用する。
update
svn update は、リポジトリの最新変更を現在の作業コピーに反映する。
複数人が同じリポジトリを使う環境では、修正前とコミット前に実行するとよい。
svn update
短く up という別名を使用できる。
svn up
特定リビジョンに作業コピーを合わせるときは -r オプションを使用する。
svn update -r 120
更新中に複数人が同じファイルの同じ部分を修正していた場合、競合が発生することがある。 競合が発生したらファイル内容を確認して手動で整理し、解決済みであることを SVN に伝えてからコミットする。
svn status
svn resolve --accept=working sample.c
svn commit -m "Resolve conflict in sample.c"
status
svn status は、作業コピーで変更されたファイル状態を確認する。
コミット前に必ず確認すべきコマンドである。
svn status
よく見る状態文字は次のとおりである。
| 状態 | 意味 |
|---|---|
M |
修正済み |
A |
追加予定 |
D |
削除予定 |
? |
SVN が追跡していないファイル |
! |
SVN が追跡しているがローカルから消えたファイル |
C |
競合発生 |
例は次のとおりである。
$ svn status
M src/main.c
A src/new-file.c
? build.log
? で表示されるファイルはコミットされない。
新しいファイルをリポジトリへアップロードするには、先に svn add を実行する。
add
svn add は、新しいファイルやディレクトリをバージョン管理対象として登録する。
登録しただけではリポジトリへすぐにアップロードされず、その後 commit する必要がある。
svn add sample.c
svn commit sample.c -m "Add sample.c"
ディレクトリを追加すると内部ファイルも追加対象になることがあるため、コミット前に svn status で不要なファイルが含まれていないか確認する。
ビルド成果物、ログ、IDE 設定ファイルのようにリポジトリへ登録しないファイルは、svn:ignore プロパティやクライアントの ignore 設定で除外する。
commit
svn commit は、作業コピーの変更内容をリポジトリへ反映する。
コミットが成功するとリポジトリのリビジョンが増える。
svn commit -m "Fix login validation"
短く ci という別名を使用できる。
svn ci -m "Fix login validation"
特定のファイルだけをコミットすることもできる。
svn commit src/login.c -m "Fix login validation"
コミットメッセージは変更理由が分かるように書く。
fix、update、test のように意味が不足したメッセージだけを残すと、後で svn log で履歴を追跡しにくくなる。
コミット前の確認フローは次のとおりである。
svn update
svn status
svn diff
svn commit -m "Fix login validation"
log
svn log はリポジトリの変更履歴を確認する。
svn log
変更ファイル一覧まで見たい場合は -v オプションを使用する。
svn log -v
特定のリビジョン範囲やファイルに関するログだけを見ることもできる。
svn log -r 30:100 sample.c
リモートリポジトリ URL を直接指定すると、作業コピーがない環境でも履歴を確認できる。
svn log https://svn.example.com/repos/sample/trunk
diff
svn diff は作業コピーの変更内容を比較する。
デフォルト実行時は、ローカル変更と基準リビジョンとの差分を表示する。
svn diff
特定ファイルだけを比較できる。
svn diff sample.c
特定リビジョンと比較するときは -r または --revision オプションを使用する。
svn diff -r 4 sample.c
svn diff -r 10:20 sample.c
コミット前には svn status で変更ファイル一覧を確認し、svn diff で実際のコード変更をレビューする。
この過程でデバッグコード、一時ログ、不要なフォーマット変更が混ざっていないか確認できる。
blame
svn blame は、ファイルの各行がどのリビジョンで誰によって変更されたかを表示する。
問題が発生したコードの変更背景を調べるときに便利である。
svn blame sample.c
一部の SVN クライアントでは praise、annotate、ann という別名も使用できる。
ただし、blame の結果だけを見て作成者を責めるのではなく、当時のログや関連変更ファイルも一緒に確認する必要がある。
svn log -r 120 -v
svn diff -c 120 sample.c
lock と unlock
svn lock は、特定ファイルをロックして他のユーザーが修正できないようにするときに使用する。
主にマージが難しいバイナリファイル、デザインファイル、文書ファイルに使用する。
svn lock sample.docx -m "Edit release note"
ロックを解除するときは svn unlock を使用する。
svn unlock sample.docx
テキストソースコードは、一般的にロックよりもマージを前提に作業する方が効率的である。 すべてのファイルを習慣的にロックすると協業速度が落ちる可能性があるため、ロックが必要なファイル種別をチーム規則として決めておくとよい。
delete, move, copy
SVN でファイルの削除、移動、コピーを行う場合、通常のファイルシステムコマンドより SVN コマンドを使用する方が安全である。 そうすることでリポジトリ履歴を正しく残せる。
svn delete old-file.c
svn move old-name.c new-name.c
svn copy trunk branches/feature-a
削除や移動もすぐにリポジトリへ反映されるわけではない。
作業コピーに変更予定状態として残り、commit して最終的に反映される。
svn status
svn commit -m "Rename old-name.c to new-name.c"
ファイルのバックアップと復旧
リポジトリ全体をバックアップしたり別サーバーへ移行したりするときは、svnadmin dump と svnadmin load を使用する。
これらは通常の作業コピーで実行する svn コマンドではなく、サーバーのリポジトリディレクトリを直接扱う管理者コマンドである。
svnadmin dump /var/svn/sample > sample.dump
ダンプファイルを新しいリポジトリへ復元するときは、まずリポジトリを作成してから load を実行する。
svnadmin create /var/svn/sample-new
svnadmin load /var/svn/sample-new < sample.dump
運用リポジトリをバックアップするときは、次の点も一緒に確認する。
- リポジトリパスとプロジェクト URL を混同しない。
- バックアップファイルを別サーバーやストレージに保管する。
- 復旧テストを定期的に実施する。
- リポジトリ hook、アクセス権限設定、Apache または svnserve の設定も一緒にバックアップする。
よく使う作業例
新しいファイルを作成した場合
新しいファイルは SVN が自動的に追跡しない。 ファイルを追加してからコミットすることで、リポジトリへアップロードされる。
touch sample.c
svn status
svn add sample.c
svn commit sample.c -m "Add sample.c"
既存ファイルを修正した場合
すでに SVN が追跡しているファイルは、修正後すぐに状態と差分を確認してコミットできる。
svn update
vi sample.c
svn status
svn diff sample.c
svn commit sample.c -m "Fix sample calculation"
リポジトリの最新内容を反映する場合
他の開発者の変更を取得するときは svn update を使用する。
svn update
競合が発生したら、競合ファイルを整理した後、解決済み状態として表示し、コミットする。
svn status
vi sample.c
svn resolve --accept=working sample.c
svn commit sample.c -m "Resolve merge conflict"
実務上の注意点
コミット前には update、status、diff を確認する
SVN は中央リポジトリを基準に協業するため、コミット前に最新変更を反映する習慣が重要である。
svn update なしで古い作業コピーからコミットすると、競合を遅れて発見したり、他の変更の文脈を見落としたりする可能性がある。
svn update
svn status
svn diff
ファイル操作は SVN コマンドで処理する
ファイル追加は svn add、削除は svn delete、移動と名前変更は svn move を使用する。
OS の rm、mv、ファイルエクスプローラーだけを使うと、SVN が意図した変更を正確に追跡できないことがある。
trunk、branches、tags の規則をチームで決める
SVN はディレクトリ構造を強制しない。
trunk、branches、tags は広く使われる慣例だが、実際の運用方法はチーム規則として決める必要がある。
一般的な規則は次のとおりである。
trunkは常にビルド可能な状態に保つ。- 長期的な機能開発は
branchesで進める。 - リリース済みソースは
tagsに残し、修正しない。 - タグ作成とリリースブランチ作成の権限を制限する。
バイナリファイルはロックポリシーを決める
画像、文書、デザインファイルのように自動マージが難しいファイルは、svn lock を使う方が安全な場合がある。
逆にテキストソースコードにロックを多用すると、並列開発が難しくなる。
ファイル種別ごとにロック使用可否を決め、ロックメッセージを残す規則を設ける。