[ Topページへ戻る ]

Subversionでバージョン管理

2003.6.23よりアクセス


概要

Subversionは、CVS(Concurrent Versions System)を改良したバージョン管理ツールです。使い勝手はCVSに非常によく似ています。

主な改良点

コマンド

    svn, svnlook, svnadmin, mod_dav_svn, svnserve, svnversion

リポジトリへのアクセス方法

作業コピーを持ってくるには

    $ svn checkout http://svn.example.com/repos/hello
    A hello
    A hello/build.xml
    A hello/Hello.java
    $ ls -a hello
    build.xml  Hello.java  .svn/
    $
プロジェクトディレクトリの下にtrunk/tags/branchesを設けている場合
$ svn checkout http://svn.example.com/repos/hello/trunk hello
:

trunkというディレクトリが生成されるのはいまいちなので、リポジトリ上のtrunk下にあるファイル・ディレクトリを作業ディレクトリのhelloディレクトリの下にチェックアウトします

変更内容をリポジトリに登録するには

    $ svn commit Hello.java

作業コピーを最新に更新するには

    $ svn update
    U Hello.java

記号の意味は以下の表のとおりです。

記号 内容
U ファイルがリポジトリの最新版に更新された
A ファイルが新規追加された
D ファイルが削除された
R ファイルが置き換えられた(同じ名前だが履歴上は別物)
G ローカルの修正とリポジトリの更新がマージされた
C ローカルの修正とリポジトリの更新が競合している

競合が発生すると、いくつかのファイルが生成されます。aFileの場合、競合が発生すると、以下のファイルが生成されます。

aFile
コンフリクトが発生した個所にその状況が挿入されたファイル
aFile.mine
作業コピーをupdateする前のファイルは、これにリネームされる
aFile.rXXXX (XXXXはupdate前の作業ファイルのチェックアウトされた時の内容)
update前の作業コピーが紐付けされているリポジトリの内容
aFile.rXXXX (XXXXはupdate後のリポジトリの内容)
updateによって最新となるリポジトリの内容

作業コピーのファイルやディレクトリを新たにリポジトリに登録するには

    $ svn add Greeting.java
    A    Greeting.java
    $ svn commit Greeting.java

commit前に取り消しする場合は、svn revertを使用します。

作業コピーのファイルやディレクトリの情報を見るには

    $ svn info Greeting.java

作業コピーのファイルやディレクトリの変更状況を確認するには

    $ svn status
    MM    Greeting.java

リポジトリの新規作成

バークレイDB

Subversion 1.1までのデフォルトの形式でしたが、最近ではファイルシステム(FSFS)に置き換わり、あまり見ることはありません。

    $ svnadmin create --fs-type bdb /home/svn_rep
    $ ls /home/svn_rep
    README.txt conf/ dav/  db/  format hooks/  locks/

リポジトリは、Berkeley DBを使用した場合、CVSのようにディレクトリ/ファイルが生で見れることはありません。hooks/には、例えばコミットした直後に動作するスクリプト、といったものが置かれます。confの下には、リモートからアクセスする際のアクセス権やパスワード設定に関する情報svnserve.confがあります。リモートから接続する場合、svnserve.confを適切に記述しておきます。

Berkeley DBを使用する場合、リポジトリの場所をNFSやSMBなどのネットワーク共有にすることはできません。Berkley DBはデータをメモリにマッピングするため、ファイルを複数CPUから共有すると不整合が発生します。

ファイルシステム

Subversion 1.2からデフォルトとなっている形式です。

    $ svnadmin --fs-type fsfs create /home/svn_rep
    $ ls /home/svn_rep
    README.txt conf/ dav/  db/  format hooks/  locks/

リポジトリのバックアップ

フルバックアップ
$ svnadmin dump /home/svn_rep > svn_rep_backup_`date +%Y%m%d`
* Dumped revision 0.
* Dumped revision 1.
* Dumped revision 2.
    :
$
リストア

バックアップしたリポジトリを復元します。

$ svnadmin create /home/svn/svn_rep
$ svnadmin load /home/svn/svn_rep < svn_rep_backup_20050401

Berkeley DBで作ったリポジトリをフルバックアップし、そのファイルをファイルシステムのリポジトリにリストア(load)してもOKでした。

バックアップには、conf/subversion.confファイルは含まれないので、設定ファイルは手動でバックアップを取る必要があります。

リポジトリへのimport

以下のディレクトリをSubversion管理下に置きたい場合
    hello
       +--- build.xml
       +--- src
             +--- Hello.java

リポジトリ上、javaproj/hello に置く場合は次のコマンドを実行

    $ svn import hello file:///home/svn_rep/javaproj

このとき、環境変数SVN_EDITORに設定したエディタが起動されます。ログを記述してエディタを終了すると、import処理が実行されます。

リポジトリ上のディレクトリを見るには

    $ svn list file:///home/svn_rep

リポジトリ上に新たなディレクトリを作成するには

    $ svn mkdir file:///home/svn_rep/cxxproj

リポジトリ上からファイル・ディレクトリを削除するには

    $ svn delete file:///home/svn_rep/cxxproj

リポジトリ上のファイルを直接別な場所へ移動するには

    $ svn move -m "Log a message" file:///home/svn_rep/cxxproj/a.txt file:///home/svn_rep/cxxproj/sub/a.txt
または、作業コピー上で移動してからコミットするには
    $ svn move a.txt sub/a.txt
    A sub/a.txt
    D a.txt
    $ svn commit

リポジトリ上のディレクトリを直接別な場所へ移動するには

$ svn move 

キーワード置換をするには

以下の5つのキーワードが置換可能です。ただしデフォルトでは置換が無効となっています。

キーワード 略語
LastChangedDate Date
LastChangedRevision Rev
LastChangedBy Author
HeadURL URL
Id
例えば、LastChangedRevisionを有効にするには
    $ svn propset svn:keywords "LastChangedRevision" Greeting.java
ファイル個別に毎回指定しなくても、あるパターンのファイルにキーワード置換属性を指定する

Windows Vistaならユーザーディレクトリ下の\AppData\Roaming\Subversionディレクトリ内にあるconfigファイルを設定します。

enable-auto-props = yes
*.java = svn:keywords=Id
*.sdoc = svn:keywords=Id

拡張子.java、.sdocのファイルをSVNで追加すると、キーワードIdの置換が有効になります。

ネットワークからアクセスするには

HTTP(HTTPS)の場合

 WebDAV/deltaVプロトコルで通信する(HTTP1.1の拡張)
 必要なもの:Apache httpd 2.0、mod_DAV、Subversion、mod_dav_svn

SVNサーバの場合

 svnserveを実行する。デフォルトポートは3690。inetdに設定するか-dオプション付きでデーモンプロセス起動。

 オプションなしでsvnserveを実行
   svn://myhost/usr/local/repos/project へのアクセスは、myhost上の/usr/local/repos/proj にあるリポジトリを参照。
 -rオプション付きでsvnserveを実行(例:svnserve -r /usr/local)
   snv://myhost/repos/proj へのアクセスは、myhost上の/usr/local/repos/proj にあるリポジトリを参照。
 -Rオプション付きは書き込み不可

Solaris 10上でのsubversion設定手順

ネットワークからSVNサーバ経由で利用する場合、各リポジトリのルート直下のconfディレクトリにあるsvnserve.confを適切に記述する必要があります。

複数プロジェクトとリポジトリ

 ・プロジェクト間でリポジトリのデータの共有、移動を行うかどうか?
 ・コミット時の処理、認証、アクセス許可を同じにするかどうか?
 を判断基準に同じリポジトリを使うか別リポジトリにするか判断します

ブランチ

 Subversionでのブランチ作成は、単なるディレクトリのコピーです。
 リポジトリ上に、trunk、branchesというサブディレクトリを設けるTipsが一般的です。2つ方法があり、良いと思う方を使えばよいようです。

各プロジェクト毎にtrunk/branchesを設ける方法
repos
   +--- projA
   |       +--- trunk
   |       +--- branches
   +--- projB
   :       +--- trunk
   :       +--- branches
リポジトリのトップでtrunk/branchesを設ける方法
repos
   +--- trunk
   |       +--- projA
   |       +--- projB
   +--- branches
           +--- projA
           +--- projB
ブランチの作成

現在のtrunkからブランチRB-1.0を作成します

    $ svn copy -m "Creating release branch for 1.0" \
          svn://myhost/repos/trunk svn::/myhost/repos/branches/RB-1.0
作業コピーをtrunkからブランチに切り替える

手っ取り早いのは、新たにブランチを指定してチェックアウトする方法です。

現在trunkをチェックアウトして作業コピーを作っており、これをブランチRB-1.0に切り替える場合

フック(自動処理)

誰かがリポジトリにコミットした際に自動である処理を実行したい、といった目的でフック機構が用意されています。

コミットされたら、公開用ディレクトリを更新する

リポジトリの内容をあるディレクトリに展開しておき、リポジトリに新しいコミットが行われたらそのディレクトリを更新する処理をフック機構で自動で実行します。

UNIXでの仕込み

リポジトリ file:///home/svn_rep/sample および、公開ディレクトリを /share/public/sample とします。

/home/svn_rep/sample/hooks ディレクトリに、実行権付きでpost-commit の名前で実行可能なファイルを作成します。

#!/bin/sh
REPOS="$1"
REV="$2"
PUBLICDIR="/share/public/sample"
LOGFILE="/tmp/svn_rep_sample_post_commit.log"
export LANG=ja_JP.UTF-8
cd $PUBLICDIR
svn update >> $LOGFILE 2>&1

/share/public/sample にあらかじめリポジトリ内容をチェックアウトし、しかるべきアクセス権を付けておきます。

~$ cd /share/public/sample
sample$ svn co file:///home/svn_rep/sample
sample$ chown -R svn:svn .
sample$

svnserveでリポジトリを更新する場合、svnserve実行ユーザ権限でアクセス可能にしておきます。

ログファイルを/var/logの下に作成するとき、あらかじめアクセス権を付けておかないとエラーになるので、rootでファイルをtouchで作成し、ファイルのアクセス権を変更しておきます。

日本語のファイルがコミットされたとき、環境変数LANGで設定がないと、ファイル名をエンコードできずエラーとなります。

コミットされたら、メールで通知する

リポジトリにコミットがあったらメールで通知する処理をフック機構で自動実行します。

リポジトリが大きいときは、すべてのコミットの通知を受けたら大変なので、特定のディレクトリへのコミットを通知することまで実現を狙います。

UNIX(CentOS 5)での仕込み

(CollabNet Subversion serverで)リポジトリを作成すると、リポジトリディレクトリのhooksの下に、post-commit.tmplというファイルが出来ています。これを、post-commitの名前に変更し、実行権を付けます。

#!/bin/sh

# POST-COMMIT HOOK
#
# The post-commit hook is invoked after a commit.  Subversion runs
# this hook by invoking a program (script, executable, binary, etc.)
# named 'post-commit' (for which this file is a template) with the
# following ordered arguments:
#
#   [1] REPOS-PATH   (the path to this repository)
#   [2] REV          (the number of the revision just committed)
  :(中略)
REPOS="$1"
REV="$2"

mailer.py commit "$REPOS" "$REV" /path/to/mailer.conf

デフォルトでは、mailer.py スクリプトを呼び出していますが、これはSubversionのソースアーカイブに含まれているので、CollabNet Subversionバイナリを展開した中にはありません。

で、探してみたところ、日本語対策も入ったcommit-email-jp-CentOS5.pl なるスクリプトが以下サイトで公開されていました。

なお、このスクリプトは"-m <正規表現>"オプションで、メール通知する対象のディレクトリを正規表現で指定できるのですが、バグってます。Subversion Users MLに紹介されていた回避策として以下の修正をします。ただし、この修正をすると、必ず-mオプションの指定が必要になります。(絞り込みしない場合は、-m "." を指定)

-my @project_settings_list = (&new_project);
+my @project_settings_list = ();

また、スクリプト中にメールサーバー名、svnlookコマンドのパスがハードコードされているので、使用環境に合わせて修正します。

-$smtp_server = "AAA.BBB.CCC.DDD";
+$smtp_server = "localhost";
-my $svnlook = "/usr/bin/svnlook";
+my $svnlook = "/opt/CollabNet_Subversion/bin/svnlook";

/home/svn_rep/sample がリポジトリ・ルートのディレクトリとして、上記サイトより入手した commit-email-jp-CentOS5.plをこのディレクトリ下のhooks下に保存します。

~$ pwd
/home/svn_rep
~$ cd sample/hooks
hooks$ cp /tmp/commit-email-jp-CentOS5.pl .
hooks$ 

次に、hooksの中にpost-commitを作ります。まだ作っていなければ、post-commit.tmplを名前を変えて実行権を付けます。

hooks$ cp post-commit.tmpl post-commit
hooks$ chmod +x post-commit
hooks$ 

post-commitを編集します。commit-email-jp-CentOS5.plへの呼び出しを追記します。

/usr/bin/perl /home/svn_rep/sample/hooks/commit-email-jp-CentOS5.pl 
 "$REPOS" "$REV" -m "." --from svn@examples.org -s '[Commit:sample]'
 scm@examples.org
注記:改行なしに記述します。
-s オプションは通知メールの題名に接頭辞として付く文字列です。通知先メールアドレスは複数列挙可能です。

備考
 日本語のディレクトリ名は-mオプションの正規表現ではマッチしないようです。

Tips

Cygwin関連

Cygwinの場合、環境変数TERM にcygwinがセットされていますが、この場合viがうまく起動されず、以下のようなエラーとなります。TERM=vt100のように適切な値をセットします。

vi: cygwin: unknown terminal type
svn: system('vi svn-commit.tmp') returned 1

bashで作業コピーを操作する際、ディレクトリ補完をするときに、.svnが各ディレクトリにできるため、ちょっと面倒です。シェル変数FIGNOREに.svnを登録していると、bashでの補完に.svnが対象外となるため操作が楽になります。

リポジトリの移動

別ホストにリポジトリを移動した場合でワークスペースはそのまま継続使用したい

本来は新しいサーバから再チェックアウトが必要ですが、作業中のワークスペースをどうしても維持したい場合、各ディレクトリに生成される.svnディレクトリの中にあるentriesファイルの情報を修正することでごまかすことが出来そうです。

.svn/entries
<?xml version="1.0" encoding="utf-8"?>
<wc-entries xmlns="svn:">
<entry committed-rev="88"
  name=""
  committed-date="2006-01-23T12:34:56.789012Z"
  url="svn://oldhost/myrep/trunk"
  last-author="torutk"
  kind="dir"
  uuid="xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx"
  revision="88"/>
<entry name="alpha" kind="dir"/>
<entry name="beta" kind="dir"/>
</ec-entries>

urlの部分を変更すれば、新しいホストに対応できそうです。

#!/bin/bash -f

for e in $( find . -name "entries" ); do
        echo $e;
        cp $e $e.oldhost;
        sed -e 's/oldhost/newhost/g' $e.oldhost > $e;
done

対象外のファイル・ディレクトリ

特定のファイルやディレクトリを、バージョン管理対象外としたい場合の設定手段は、意外と限られています。

プロパティ svn:ignore

未だバージョン管理下に置かれていないファイルおよびディレクトリを、svn status, svn add, svn importコマンドの対象から除外します。

逆に言えば、既にバージョン管理下に入ってしまったファイルやディレクトリを無視する設定はできません。また、addはできてしまいます。

差分の確認

バージョン管理ツールの真骨頂は差分情報の取得です。せっかく差分情報が保管されていても、差分情報を取り出し有効活用しなければ単なるファイル共有です。

更新されたファイル一覧をリストアップする

コマンドライン

作業コピーの上で

$ svn diff | awk '/^Index/ {print $NF}' 
include/Sample.h src/Sample.cpp src/main.cpp $

とすると、ベースリビジョンと作業コピーとの比較で変更のあったファイルをリストアップします。

特定のレビジョン(例:123)と作業コピーとの比較は、svn diff -r 123 と、特定の2つのレビジョン間(例:100と123)の比較は、svn diff -r 100:123 と-rオプションで指定します。

作業コピーがない状態で、リポジトリの特定の2つのレビジョン間の比較(例:100と123)の比較は、レポジトリURLに@とレビジョン番号を指定して行います。

$ svn diff http://remote.example.com/repo/trunk/Hoge@100 http://remote.example.com/repo/trunk/Hoge@123
   :

または、レポジトリが同一であれば

$ svn diff -r 100:123 http://remote.example.com/repo/trunk/Hoge
   :

空白の数、改行のみの変更は差分表示しない

svnのサブコマンドdiffのオプション-xに続けて指定するオプションで、空白や改行コードの違いを無視する・しないの制御が可能です。

空白文字の数が違っても差分として表示しない場合、

$ svn diff -x -b Hello.cpp

空白文字を差分比較で無視する場合

$ svn diff -x -w Hello.cpp

改行コードの違いを差分比較で無視する場合

$ svn diff -x --ignore-eol-stype Hello.cpp

上記複数を指定する場合、-xは1つだけ後にパラメータを指定できるので、

$ svn diff -x "-b --ignore-eol-stype" Hello.cpp

と-xのあとに""で囲んで複数オプションを指定します。

異なる場所で作業した結果を統合

 複数の会社で共同開発するソフトウェアの場合で、それぞれの会社の開発場所からSubversionサーバを共有できればいいのですが、往々にして共有できないことがあります。各会社でSubversionサーバを立て、担当するサブシステムをそこで開発し、ある程度の段階で全システムを統合する開発場所に持ってくるというシナリオになります。

やってはいけない作業手順

 このような場合、やってはいけない作業は次の流れです。

 この場合、バージョン管理の禁じ手である、ファイルコピーが発生しています。考えられる問題は、

などが挙げられます。これではバージョン管理以前に逆戻りです。集中リポジトリ方式であるSubversionを、地理的に分散しているために無理やり分散リポジトリのような使い方をしているために生じています。

やるべき作業手順の案

問題を回避するやり方のひとつに、ブランチの活用が考えられます。

なお、最後のマージ作業は、統合リポジトリの構成管理担当者がするべきです。

情報源

書籍・雑誌記事

Web

メーリングリスト

インストールメモ

Windows

どのSubversionを入れようか(2010.12.12改訂)

Windows用のSubversionソフトを探すと、現時点では、以下のソフトが見つかります。

コマンドライン版
GUI版(クライアント機能のみ使えればよいなら)

Solaris

Solaris 10 Subversion設定

CentOS

CentOS 5へのインストール

CentOS 5では、Subversion 1.4.2が標準搭載されています。

Subversion 1.5をCentOS 5へインストールするメモはこちらです。

CollabNet Subversion 1.6をCentOS 5へインストールするメモはこちらです。

CentOS 4.4へのインストール

CentOS 4.4にインストールした際のメモです。

CentOSでは、パッケージ管理にyumを使用します。

# yum install subversion
  :
#

しかし、2007年1月8日現在Subversionは1.4.2が最新版で、1.2を含めてそれより古い版はサポート外となっている状況にもかかわらず、yumでインストールされるSubversionは1.1.4となっています。そこで、RPM形式のバイナリパッケージをダウンロードしてインストールします。

Subversionの本家サイトから"Downloads"のリンクを辿ってBinary Packagesの"Red Hat 8.0, 9, Red Hat Enterprise Linux 3, 4"の項にある"UK Mirror"または"summersoft.fay.ar.us"のリンクを辿ります。rhel-4/i386のディレクトリ下にrpmパッケージがあります。

以下のrpmパッケージファイルをダウンロードします。

CentOS 4.4には、aprおよびapr-utilパッケージがインストールされていますが、バージョンがそれぞれ0.9.4-24.5.c4.2および0.9.4-21となっています。subversion-1.4.0-1が要求するaprおよびapr-utilのバージョンは0.9.7および0.9.7なので条件に合いません。そこで、先にaprとapr-utilの両パッケージをアップグレードします。
rpmコマンドのアップグレードは-Uオプションを指定します。

# rpm -Uvh apr-0.9.12-1.i386.rpm apr-util-0.9.12-1.i386.rpm
  :
#

インストールを確認するには、rpmコマンドで-qオプションを指定します。

# rpm -q apr
apr-0.9.12-1
# rpm -q apr-util
apr-util-0.9.12-1
#

次に、subversionをrpmコマンドを使ってインストールします。インストール時は-iオプションを指定します。-vhオプションを追加すると、詳細情報の表示と進捗表示をrpmコマンドが行います。

# rpm -ivh subversion-1.4.0-1.i386.rpm
  :
#

これでSubversionの基本機能が利用できるようになります。

inet経由でsvnserveを起動

svnserveをinet経由で起動する設定を行います。

/etc/services にsvnserveのサービス名・ポート番号登録をしますが、既に記述済みでした。

svn       3690/tcp            # Subversion
svn       3690/udp            # Subversion

/etc/xinetd.d/svn ファイルを作成します。

service svn
{
    disable = no
    socket_type = stream
    protocol = tcp
    wait = no
    user = apache
    server = /usr/bin/svnserve
    server_args = -i -r /home/svn/repos
}
user=で指定したユーザ権限でsvnserveがファイルを読み書きするので、リポジトリに指定したディレクトリ以下は指定したユーザ権限でファイルの読み書きができるようにパーミッションを設定しておく必要があります。
# cd /home/svn
# chown -R apache repos

svnを登録

# /sbin/chkconfig --add svn
# /sbin/chkconfig --list | grep svn
    svn:  on
#

xinetdを再起動

# /etc/rc.d/init.d/xinetd restart
xinetd を停止中:                        [  OK  ]
xinetd を起動中:                        [  OK  ]
#

GUI版

Linuxで使えるGUIのSubversionツール

外部diff/mergeツールには、

外部diff/mergeツールの設定(meld)

svn diffなどのコマンドは通常CUI上にdiffツールの出力結果をテキスト表示します。しかし、Windows環境でのGUIによる視覚的なdiffツール(WinMergeなど)に慣れてしまうと、やっぱり不便を感じます。そこで、svnコマンドから比較結果を外部ツールに出すように設定を行います。

ユーザ毎に設定する場合、~/.subversion/configファイルに外部diffツールの設定を記述します。ただし、ツールによってコマンドライン引数の指定が異なるので、通常はsvnコマンドからシェルスクリプトを呼び出し、シェルスクリプト内にコマンドライン引数を使用する外部ツールに合わせて置き換える記述を行います。

~/.subversion/configファイルを編集

[helpers]
diff-cmd = /home/torutk/bin/svn-diff-meld
diff3-cmd = /home/torutk/bin/svn-diff3-meld
diff3-has-program-arg = false

~/bin/svn-diff-meldファイルを作成

#!/bin/sh
# SVN diff wrapper for meld
left="$6"
right="$7"
meld "$left" "$right"

~/bin/svn-diff3-meldファイルを作成

#!/bin/sh
# SVN diff3 wrapper for meld
mine="$9"
older="$10"
yours="$11"
meld "$mine" "$older" "$yours"