今更聞けないSubversionの使い方


# はじめに

ここでは、いまさら聞けないSubversionの使い方について述べる。タグやブランチなどしっかりと使いこなそうとすると、色々と覚えなければいけないSubversionだが、実は、もっとも使うコマンドは、指で数えるくらいしかない。ここでは、その基本的なコマンドを紹介した後、時々必要になるタグ作り、ブランチ作り、エクスポートなどの各種コマンドのリマインダについてまとめる。

なお、ここでは、Unix, Linuxベースのシステムで、subversionをコマンドラインベースに使う方法を紹介する。Windowsで使う方法は紹介しないので、別のページを探してほしい。

# Subversionとは

以下、Wikipediaから抜粋

```
Apache Subversion（アパッチ・サブバージョン SVN）はプログラムのソースコードなどを管理する集中型バージョン管理システムの一つである。元々は、CollabNet, Inc. が開発していたが、2009年11月7日にApache Incubatorプロジェクトのひとつとなり、2010年2月17日よりApacheのトッププロジェクトとなった。ライセンスはApache Licenseに準じたものとなっている。

歴史的には広く使われているバージョン管理システムの一つにCVSがあった。CVSはよくできているが、ディレクトリの移動の管理やネットワーク対応の点、不可分な更新などでやや難があった。これらCVSの問題点を解決すべく開発されたのがSubversionである。 古くからオープンソースソフトウェアの開発においてはCVSが多く使われていたが、近年ではSubversionを使用するオープンソースプロジェクトも多くなりつつある。
```

補足すると、現在、eclipse surveyでは一番利用されているバージョン管理システムでもあるようだが、githubの登場でgitに猛追されている（今年当たり、比率が入れ替わるかも）

* http://www.slideshare.net/IanSkerrett/eclipse-survey-2013-report-final/14

# セットアップ

Redhat系のシステムでは以下のコマンドでインストールできる

```bash
$ sudo yum install subversion
```

Debian系のシステムでは以下

```bash
$ sudo apt-get install subversion
```

Mac OS Xでは、以下

```bash
$ brew install subversion
or
$ sudo port install subversion
```

# ワーキングコピーを作る

SVNでは、ソースの各バージョンを保持している「リポジトリ」から、そのときの最新版（とは限らないが大抵の場合はそう）のコピーを自分のローカルのストレージに持ってきて、ソース管理をしていく。この”そのときの最新版（とは限らないが大抵の場合はそう）のコピー”のことを「ワーキングコピー」という。

![リポジトリとワーキングコピー](http://blog-imgs-38.fc2.com/o/g/6/og6/Subversion2.png)


ワーキングコピーの作り方は、もう既にリポジトリ領域がある場合はその内容を持ってこれば良いが、今自分のローカルストレージにしかソースがなくて、これから管理を始めたいといった場合も備えて、自分で作る方法も紹介していく。

なお、ここではSVNのリポジトリは既に用意されている前提で話を進める。リポジトリの作り方などは今後、追加でまとめていく。

## リポジトリの中身を調べる

さあ、ワーキングコピーを作ろうとしても、リポジトリの中のどこにソースがあるのかすぐには分からないことも多いだろう（というかそういう場合ばっかしだ）。そんな場合に備えて、まず最初にunixのlsコマンドに相当するsvnのコマンドを紹介する。

svnコマンドは、`svn XXX`と第二引数XXXに具体的な命令が指定される。svn lsは、svnリポジトリのロケーションの下にどのようなファイルとディレクトリが入っているのかを確認するコマンドだ。仮に、以下のアドレスにリポジトリがあるとすると

* svn://192.168.1.2/repo

そのトップディレクトリの内容を調べるコマンドは、

```
$ svn ls svn://192.168.1.2/repo
thread/
```

となる。第三引数以降は、svnコマンドによってそれぞれ異なる。それぞれの詳細は、

```
$ svn help
<どんなコマンドがあるかの一覧が見れる>
$ svn help XXX
<XXXというコマンドの詳細（引数も含む）が見れる>
```

というコマンドで確認できるので、困ったら確認してみよう。話がずれた。リストの話に戻る。`svn ls`は実は`svn list`命令の省略形で、同じ命令を

```
$ svn list svn://192.168.1.2/repo
thread/
```

としても実行できる。が、lsの方が短いのでlsだけを覚えておいて問題ない。

さて、コマンドを実行すると、リポジトリの下にthreadというディレクトリが入っていることが分かる。さらに、threadの中身を見ようとすると、以下のコマンドを実行すれば良い。

```
$svn ls svn://192.168.1.2/repo/thread
branches/ tags/ trunk/
```

このように”svn ls”コマンドの使い方は、unixシステムのlsコマンドとほとんど変わらない。”svn ls”コマンドで自分の取りたいソースのロケーションを特定して、具体的にそれを取ってくる作業（チェックアウトするという）に移る。


## もう既にあるリポジトリ領域からチェックアウトする

リポジトリから、自分のローカルストレージにワーキングコピーを作ることを「チェックアウト」するという。仮に、以下のロケーションにソースが用意されているとすると

* svn://192.168.1.2/repo/thread/trunk

このソースをチェックアウトするコマンドは以下のようになる

```
$ svn co svn://192.168.1.2/repo/thread/trunk thread
```

svnコマンドの各引数について簡単に説明する。第二引数の”co”は”checkout”の略であり、もちろん、略さず

```
$ svn checkout svn://192.168.1.2/repo/thread/trunk thread
```

でも同様の操作を行えるが、毎回、`checkout`と打ち込むのも面倒なので、大抵の場合は”co”という省略形に落ち着く。

第三引数の”svn://192.168.1.2/repo/thread/trunk”はリポジトリのアドレス、第四引数の”thread”はチェックアウトするときのディレクトリ名である。

第四引数は省略可能だが、そうすると、リポジトリのアドレスの最後のbasename（この場合は”trunk”）がディレクトリ名となってしまい、見分けがつかない。というのは、trunkというのが、svnのソースが格納されるルートディレクトリの慣習的な名前になっており、大抵のプロジェクトのルートディレクトリはtrunkという名前であるからである。そのため、通常のチェックアウトでは、第四引数でディレクトリ名を指定してチェックアウトすることになる。

お気づきかもしれないが、第四引数で指定されるディレクトリ名は、trunkの一つ前のディレクトリ名が使われることが多い。これは偶然でなく、subversionでは、各プロジェクトの下にtrunk, tags, branchesという3つのディレクトリを作って、ソース管理するのが慣習になっているためである。

コマンドが成功するとthreadというワーキングコピーが、コマンドを実行したディレクトリに作成される。このワーキングコピーを使って、今後、ソースを管理していく。

## 自分で作る

SVNのリポジトリがあるが、まだ自分のソースコード（これからこのソースコードのセットのことをプロジェクトと呼ぶ）がリポジトリで管理されていない場合は、まずそのプロジェクトをリポジトリにアップロードしなければならない。このアップロード作業のことを”インポートする”という。仮に以下のロケーションにリポジトリがあり、

* svn://192.168.1.2/repo

これから、インポートするプロジェクトのトップディレクトリ名がthreadだとすると、リポジトリの中に新しくthreadの管理領域を作る作業は以下のコマンド群で実現できる。

```
$ svn mkdir svn://192.168.1.2/repo/thread -m "create new project, thread"
$ cd /path/to/thread
$ svn import . svn://192.168.1.2/repo/thread/trunk -m "initial import of project, thread"
```

最初のコマンドは、レポジトリの中に新しいプロジェクトのディレクトリを作る作業（svn mkdir）で、第三引数がその新しいディレクトリのロケーションをさす。-mオプションは、続く文字列をコメントとするオプションで、今回は英語で「新しくプロジェクト作るよ」と書いている。

次のコマンドでは、ローカルストレージにあるthreadディレクトリのパスに移動（チェンジディレクトリ)している。

その後、3つ目のコマンドで、そのルートディレクトリのインポート作業(svn import)をしている。この際、第二引数は、インポートする対象（このときはカレントディレクトリ”.”)、第三引数は、新しく作るリポジトリのアドレスである。-mオプションは前述のとおりコメントである。

直接、svn://192.168.1.2/repo/threadにインポート作業をしないのは、svnでは慣習的にプロジェクトディレクトリの下にtrunk, tags, branchesという3つのディレクトリを用意してソースを管理するためで、そのため、svn mkdirで先にプロジェクトのディレクトリを別途用意してあげる必要がある。

tagsディレクトリ、branchesディレクトリはその必要がでてきてから作っても良いが、最初に作っておくなら、以下のコマンドで作れる。

```
$ svn mkdir svn://192.168.1.2/repo/thread/tags -m "create tags directory in thread project"
$ svn mkdir svn://192.168.1.2/repo/thread/branches -m "create branches directory in thread project"
```

さて、ここまでの作業で、リポジトリにソースコードをインポートすることができた。しかし、このままでは、自分のローカルストレージにはまだ、ワーキングコピーが用意されていない（ここがsubversionの面倒なところだ）ので、新しくワーキングコピーをチェックアウトする必要がある。

大抵の場合、このチェックアウトをするときに邪魔になるのが、元々あるソースコードのディレクトリだ。今回の例では、バックアップとして別のディレクトリ名にリネームするが、インポートが問題なく済んでいる場合は、削除しても構わないだろう（ただ、チェックアウトして、問題なくインポートが完了していることを確認するまでは残しておく方が無難である）。

```
$ cd /path/to/thread
$ cd ..
$ mv thread thread_
$ svn co svn://192.168.1.2/repo/thread/trunk thread
$ ls
thread thread_ …
```

以上で、threadのワーキングコピーを自分のローカルストレージに作成することができた。


# 自分で編集した内容をリポジトリに反映する（コミット）

自分で編集したファイルをリポジトリに反映することを「コミット」するという。

## 反映するファイルが、まだsvnに登録されていないファイルの場合

編集したファイルが新しい場合は、svn addでそのファイルを管理ファイルとして登録し、コミットする。仮にファイル名をnewfileとすると

```
$ svn add newfile
$ svn commit newfile -m "add newfile"
```

として、コミットする。-mオプションは、しつこいが後に続く文字列をコメントとして扱うオプションである。

なお、第三引数のnewfileは省略することができ、

```
$ svn commit -m "add newfile"
```

としても良い。ただ、この場合、同じディレクトリに含まれる変更のあるすべてのファイルがコミットされるので、場合によって使い分けよう。

## 既に登録されているファイルをコミットする

既にSVNの管理下にあるファイル（過去にインポートしたり、addしたりしているファイル）はコミットするだけで良い。仮にコミットするファイル名をchanged_fileとすると、

```
$ svn commit changed_file -m "update changed_file"
```

のようにしてコミットする。ちなみに、ファイル毎にコミットする必要はなく、

```
$ svn commit -m "update all"
```

とすると、同じディレクトリに含まれるすべての変更をコミットすることができる。大抵の場合、その日の変更をまとめてコミットする場合が多いため、よく使うのは後者のパターンである。

# 他人が編集した内容をリポジトリに反映する（アップデート）

前の章で、自分が編集したファイルをリポジトリにコミットする方法について述べたが、ここでは、他の人がリポジトリに対して行った変更を反映する方法について述べる。

他の人がリポジトリに対して行った変更を自分のワーキングコピーに反映することを「アップデート」するといい、以下のコマンドで実行できる。

```
$ svn update
```

このコマンドを実行すると、実行したディレクトリに含まれるすべてのファイルの変更が反映される。このとき、自分の編集内容と矛盾がなければ単純に内容が反映され、矛盾があると、その矛盾がソフトウェアから指摘され、矛盾を解決する作業に進むことになる。この矛盾を解決する作業のことを「マージ」するといい、パターンに応じて色々な修正法があるが、後にまとめることにする。

一旦、ワーキングコピーを作成してしまえば、これまで紹介したsvn add, svn commit, svn updateが使うコマンドのほとんどなので、いちいちドキュメントをみなくても運用ができる。ここまでをまずマスターしよう。

# その他、コマンド群

その他、svnを運用する上で必要なコマンドについてまとめる予定である。

## リポジトリを作る

	編集中

## SVNのサーバを立てる

	編集中

## タグ/ブランチを作る

### まずコンセプトの話をしよう

タグとブランチの話をする。システム内部のタグとブランチの扱いは実はバージョン管理システム毎に異なる。しかし、概念的なものはほぼ一緒なので、ここではコンセプトの話をした後にsvnでどのようにタグとブランチを作っていくのかを紹介する。

語弊があるかもしれないが、タグとブランチの概念を一言で表すと以下のようになる

* タグ：ある時点のソースのスナップショット
* ブランチ：あるソースの本流(trunk)とは異なる別の開発版

それぞれ、もう少し詳しく説明する。まずはタグの話をしよう。ここまで全くバージョン管理そのものの話はしてこなかったが、svnはバージョン管理システムで、コミットした各時点のソースを保存しておき、必要に応じて戻すこと（ロールバックという）ができる。その戻すときの手がかりがsvnではリビジョン番号というもので、最初のコミットからインクリメンタルに1,2,3と番号がついていて管理されている。しかし、ぶっちゃけた話、自分が戻したい時点のリビジョン番号を覚えている人なんていない。というより人間の能力を超えている。そこで、IPアドレスに対するホスト名のように、もう少し人間でも覚えておけるような文字列で管理できないだろうかということで、その機能を実現するのが、「タグ」である。

![タグ](http://www.nulab.co.jp/kousei/images/chapter2/tag.png)

タグの概念は上の図が良く表しており、ある時点のソースコード全体に対して、本来のタグのように文字列で目印をつけておく。タグをつけた時点のソースコードが必要になった場合は、そのタグ名を手がかりに、ソースをチェックアウトすれば良い。この機能は、ソースコード開発において、安定版やリリース版を保持するためによく使われる。

ロールバックしたいケースのもう一つにある特定のコードにバグがでてしまい、どうしても解決できないので、動いていた時点まで戻したいといった個別のニーズがある。この場合はタグを使うのではなく、ログを見ながら、リビジョン番号を確認し、ロールバックするという手続きをとることが多い。この手順に関しては、別の小節で紹介する。

次にブランチの話をする。ブランチとは、メインのソースコード開発を川の本流に例えるなら、支流にあたる概念である。ある挑戦的な機能をソースコードに付け加えたいのだけど、成功するかどうかもわからないし、それまでのソースコードは保持し続けなければいけない。メインの開発を続けながらも、挑戦的な機能の開発も並行して行いたいといった時にメイン版とは別にそれをコピーした別のソースコードのリポジトリを作って管理する。しかし、全く別々に管理するのではなく、必要に応じてメインの変更を反映したり、挑戦的な機能の開発が成功したら、本体に合流させたい。そういった用途で作られるのがブランチである。

![ブランチ](https://softech.informatik.uni-kl.de/twiki/pub/Homepage/InfothekSubversion/Subversion.gif)

ブランチの概念を表しているのが上の図で、リビジョン1から2にかけて、ブランチを作り、本体側で施した3の変更を4でブランチに反映し、本体の開発が7まで進行したところで、5で開発した機能を8で本体に追加する。抽象的だが、このような操作を行うのが、ブランチである。

### 長い話はこれでおしまい。どうやって作るの？

コンセプトの話が長くなった。このように全く概念の違うタグとブランチだが、svnではタグとブランチは同一のものとして扱われる。svnでは、スナップショットをとったときにそれ以降、コミットを行わないものをタグと、コミットを引き続き行っていくものをブランチとして扱う。どちらも、

```
$ svn copy <スナップショットをとるソースコードのアドレス> <スナップショットをとる先のアドレス（新規）>
```

というコマンドで作られる。違いはシステムが決めるのではなく、扱うユーザがこのスナップショットは変更しない、これは変更するといったルールを自分たちで決めて管理していく。このような事情から、svnでは慣習的にプロジェクトのトップディレクトリの下に、trunk, tags, branchesという3つのディレクトリを作る。trunkはソースコードの本流を管理する場所、tagsはタグを置く場所、branchesはブランチを置く場所と決めておくことで、両者の混同を避けるのである。

もう少し具体的なコマンド操作について見ていこう。仮に、

* svn://192.168.1.2/repo/thread/trunk

というアドレスのソースコードのタグを取りたい場合、その操作は

```
$ svn copy svn://192.168.1.2/repo/thread/trunk svn://192.168.1.2/repo/thread/tags/yyyyMMdd -m "create new tag, yyyyMMdd"
```

となる。-mはコメントのオプションである。この操作では、元のtrunkのスナップショットを

* svn://192.168.1.2/repo/thread/tags/yyyyMMdd

というtagsディレクトリの下のyyyyMMdd（これは日付のつもり）というディレクトリに作っている。”svn copy”の操作を行うことで、trunkでの反映履歴はすべてこのタグ下でも見ることができる。実はこのディレクトリをチェックアウトし、その内容を変更、コミットすることもできるが、それは「更新しない」というルールを決めて管理していく。

変更を行いたい場合、つまりブランチを作りたい場合は以下の操作を行う。

```
$ svn copy svn://192.168.1.2/repo/thread/trunk svn://192.168.1.2/repo/thread/branches/yyyyMMdd -m "create new branch, yyyyMMdd"
```

見て分かる通り、この違いは、どのディレクトリの下に作るかという違いでしかない。branchesの下に作られたディレクトリはチェックアウトしたあと、「更新しても良い」というルールで管理していく。

作ったタグもブランチも、それぞれsvnのディレクトリなので、そのアドレスを指定することで、チェックアウトすることができる。が、慣習的にタグは更新を行わないので、エクスポート（後述する）し、ブランチは更新を行うので、チェックアウトするのが妥当な操作である。

```
$ svn export svn://192.168.1.2/repo/thread/tags/yyyyMMdd thread-yyyyMMdd
$ svn co svn://192.168.1.2/repo/thread/branches/yyyyMMdd thread-yyyyMMdd
```

## エクスポートする

svnのソースコードをみんながみんなバージョン管理したいわけではない。ソースさえ手に入れば良いという人や、リリースするときにバージョン管理を外したい（こっちの方がメインのニーズ）ということもある。このようにバージョン管理をせず、ソースコードだけをリポジトリから出力することをエクスポートするという。ちょうど、インポートとは逆の概念になる。

svnでのエクスポートは以下のコマンドで実現できる。

```
$ svn export svn://192.168.1.2/repo/thread/trunk thread
```

引数はチェックアウトのそれと全く変わらないが、このコマンドを使って出力したディレクトリは以後、バージョン管理ができない（より詳しく言うと、出力したディレクトリの下に.svnというディレクトリが作成されない）。必要に応じて使い分けてほしい。

なお、あまり知られていないことだが、このエクスポート作業は実はリポジトリからだけでなくワーキングコピーからもできる。

```
$ cd /path/to/workingcopy
$ svn export . ../thread-release
```

使う機会はあまりないかもしれないが、覚えておくとネットワーク環境がないときに自分のパソコンからソースコードを渡すといったときに便利かもしれない。

## ソースの変更をマージする

	編集中