subversion不徹底入門 CVSが広く使われるようになってきてますが、CVSにもいろいろ嫌なところが わかってきています。commitの単位がファイル単位であるとか、ファイルの 移動ができないなどが有名なところでしょう。 そこで、それらを改善したリビジョンコントロールシステムがほしくなって くるわけです。ずいぶん前から subversion というのがその目標としている ところからみて有望視されてきてましたが、もうすでにある程度は実用に なってきているので、それを紹介しておきましょう。 FSIJあたりでも subversionをつかってみようという話があがってきているので そのうち fsij.org あたりで subversion serviceを提供するかもしれません(?) では、まずインストールからです。sidならばそのままapt-getで インストールできます。 ここではいきなりいれるのはコワいという人のために woodyなマシンに chroot環境にsubversionをいれる方法を紹介します。 まず chroot環境をいれるディレクトリをつくり、そこで debootstrap を使って初期の環境を作成します。 # mkdir sid-root # debootstrap sid sid-root E: Couldn't download libpcap0 あら、だめです。どうも woodyリリース時の debootstrapに はいっているsidの情報と今のsidの状態がくいちがっているようです。 もう一度全部消して、sidのdebootstrapをダウンロードしてきてその中の /usr/lib/debootstrap/scriptからsidというdebootstrapのスクリプトを とってきます。それを debootstrap/sid というファイルにおいておいて debootstrapすると # debootstrap sid sid-root http://hp.debian.or.jp/debian debootstrap/sid I: Base system installed successfully. これで sid-root以下に sidのchroot環境ができました。chrootコマンドを 使って # chroot ./sid-chroot これで sid-chrootの環境にはいることができます。 /etc/apt/sources.listを編集してapt-getでsubversionをインストールします。 # apt-get install subversion Reading Package Lists... Building Dependency Tree... The following extra packages will be installed: libapr0 libdb4.0 libneon23 libssl0.9.6 libsvn0 libxml2 libxmltok1 patch zlib1g The following NEW packages will be installed: libapr0 libdb4.0 libneon23 libssl0.9.6 libsvn0 libxml2 libxmltok1 patch subversion zlib1g 0 packages upgraded, 10 newly installed, 0 to remove and 0 not upgraded. Need to get 3138kB of archives. After unpacking 8505kB will be used. Do you want to continue? [Y/n] y さらに subversion-server, subversion-toolsもインストールしておきます。 # apt-get install subversion-server subversion-tools Reading Package Lists... Building Dependency Tree... The following extra packages will be installed: apache2-common libapache2-dav-svn libswig1.3 mime-support python2.2 python2.2-subversion rcs The following NEW packages will be installed: apache2-common libapache2-dav-svn libswig1.3 mime-support python2.2 python2.2-subversion rcs subversion-server subversion-tools 0 packages upgraded, 9 newly installed, 0 to remove and 0 not upgraded. Need to get 4541kB of archives. After unpacking 18.2MB will be used. Do you want to continue? [Y/n] y これでsubversionのコマンドがインストールできました。 リポジトリの作成 まずリポジトリの作成をしてみましょう。これは CVS でいうところの cvs -d /cvsroot init に相当する作業です。 subversionのリポジトリを /svn/repos にするとすると次のコマンドで リポジトリが作成できます。 # svnadmin create /svn/repos cvsではそのままリポジトリディレクトリを使って作業ができましたが、subversion では WebDAV を使ってリポジトリにアクセスします。したがってWebDAVで アクセスできるように apache2 をインストールして、subversionむけの 設定をする必要があります。 これは CVSでいうところの pserverの設定 に相当すると考えていいでしょう。 CVSでは inetd.conf に cvspserver stream tcp nowait.40 root /usr/sbin/tcpd /usr/sbin/cvs-pserver という設定をするのに相当するわけです。 まず apache2 をインストールします。 # apt-get install apache2 Reading Package Lists... Building Dependency Tree... Package apache2 is a virtual package provided by: apache2-mpm-worker 2.0.43-1 apache2-mpm-threadpool 2.0.43-1 apache2-mpm-prefork 2.0.43-1 apache2-mpm-perchild 2.0.43-1 You should explicitly select one to install. E: Package apache2 has no installation candidate Descriptionをみくらべてみると -prefork がstableっぽいので これをinstallしておくといいでしょう。 # apt-get install apache2-mpm-prefork Reading Package Lists... Building Dependency Tree... The following NEW packages will be installed: apache2-mpm-prefork 0 packages upgraded, 1 newly installed, 0 to remove and 0 not upgraded. Need to get 200kB of archives. After unpacking 578kB will be used. Get:1 http://hp.debian.or.jp sid/main apache2-mpm-prefork 2.0.43-1 [200kB] Fetched 200kB in 0s (663kB/s) Selecting previously deselected package apache2-mpm-prefork. (Reading database ... 8741 files and directories currently installed.) Unpacking apache2-mpm-prefork (from .../apache2-mpm-prefork_2.0.43-1_i386.deb) ... Setting up apache2-mpm-prefork (2.0.43-1) ... Starting web server: Apache2(98)Address already in use: make_sock: could not bind to address 0.0.0.0:80 no listening sockets available, shutting down Unable to open logs invoke-rc.d: initscript apache2, action "start" failed. dpkg: error processing apache2-mpm-prefork (--configure): subprocess post-installation script returned error exit status 1 Errors were encountered while processing: apache2-mpm-prefork E: Sub-process /usr/bin/dpkg returned an error code (1) # woody環境で apache などを動かしていれば、そこで port 80 で apacheが 動いているはずなので、sid chroot環境で動いているapache2の port を かえておきます。port 番号は /etc/apache2/ports.conf で設定できます。 /etc/apache2/ports.conf: Listen 80 となっているのを例えば Listen 2481 とかにします。(2401(pserver)+80(http)) これで dpkg --configure -a をすると unpack 状態のapache2の 設定が最後までおこなわれて installed 状態にすることができます。 # dpkg --configure -a Setting up apache2-mpm-prefork (2.0.43-1) ... Starting web server: Apache2. # dpkg -C 次に subversionむけの設定です。これは /etc/apache2/mods-available/dav_svn.conf というファイルで設定します。 例えば以下のようにします。 DAV svn SVNPath /svn/repos このファイルは実は /etc/apache2/mods-enable/ からsymlinkされていて /etc/apache2/apache2.conf で /etc/apache2/mods-enabled/*.{load,conf}を Include しています。 なお、apache2 processのユーザ (/etc/apache2/apache2.conf の User で 指定されているユーザ。defaultは www-data)の権限でアクセスするので、 リポジトリのownerをそのユーザにしておく必要があります。 # chown -R www-data:www-data /svn/repos このように設定をかえたら apache2をリスタートしておきます # /etc/init.d/apache2 restart ここまでで、このapache2にアクセスしてみましょう。 GET するとこうなります。 # telnet localhost 2481 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /svn/repos/ HTTP/1.0 HTTP/1.1 200 OK Date: Wed, 20 Nov 2002 15:40:26 GMT Server: Apache/2.0.43 (Debian GNU/Linux) DAV/2 SVN/0.14.5 (r3566) ETag: "0//" Accept-Ranges: bytes Content-Length: 218 Connection: close Content-Type: text/html; charset=UTF-8 Revision 0: /

Revision 0: /


Powered by Subversion version 0.14.5 (r3566). Connection closed by foreign host. これで、anonymousユーザで/svn/reposにsubversionコマンドで アクセスできるようになります。本当はちゃんとユーザ認証すべきですが それについては後述します。 まずsubversion管理下にいれてみましょう。CVSではimportに相当する 作業です。 subversionのコマンドは/usr/bin/svnというコマンドです。 # cd /tmp # mkdir test # cd test # vi test.txt This is test. # cd .. # svn import http://localhost:2481/svn/repos/ test test こうすると testディレクトリ以下を リポジトリのtest以下に importすることになります。CVSの時と同じようにimport時の メッセージを指定していないと $EDITORがたちあがってメッセージを 入力するようにうながされます。 ---------------------------------------------------------------- --This line, and those below, will be ignored-- A test ~ ~ ---------------------------------------------------------------- で、適当にメッセージをいれてエディタを終了するとimportされます。 ---------------------------------------------------------------- This is test module. --This line, and those below, will be ignored-- A test ~ ~ ---------------------------------------------------------------- Adding test/test.txt Transmitting file data . Committed revision 3. これで test/ 以下が http://localhost:2481/svn/repos の下の test/ 以下に importされます。 CVSではリポジトリにどういうのがあるのかといったリストをとるのは 基本的には用意されていませんでしたが、subversionではlistというコマンドで どういうのがあるかを調べることができます。 # svn list http://localhost:2481/svn/repos test/ このように test というのがあるのがわかります。 ここでimport前の test を test-old にmvしておきましょう。 # mv test test-old checkoutをする 次にリポジトリから最新の状態をとりだしてきましょう。CVSでいうところの cvs checkoutに相当する作業です。subversionでもsvn checkoutでおこないます。 # svn checkout http://localhost:2481/svn/repos/test A test/test.txt Checked out revision 5. # cd test # ls -la total 16 drwxr-xr-x 3 root root 4096 Nov 20 16:04 . drwxrwxrwt 4 root root 4096 Nov 20 16:04 .. drwxr-xr-x 8 root root 4096 Nov 20 16:04 .svn -rw-r--r-- 1 root root 14 Nov 20 16:04 test.txt CVSではCVS/というCVSが管理するためのディレクトリが作られていましたが、 subversionでは.svnというディレクトリがつくられていることがわかります。 logをみる commitした時のログをみるには svn log コマンドを使います。 CVSでいうところの cvs log に相当する作業です。 # svn log ------------------------------------------------------------------------ rev 5: anonymous | 2002-11-20 16:02:15 +0000 (Wed, 20 Nov 2002) | 2 lines This is test module. ------------------------------------------------------------------------ また現在の手元(Working copy)の状態をみるための svn info というコマンドが あります。 # svn info Path: Url: http://localhost:2481/svn/repos/test Revision: 5 Node Kind: directory Schedule: normal Last Changed Author: anonymous Last Changed Rev: 5 Last Changed Date: 2002-11-20 16:02:15 +0000 (Wed, 20 Nov 2002) # svn info test.txt Path: test.txt Name: test.txt Url: http://localhost:2481/svn/repos/test/test.txt Revision: 5 Node Kind: file Schedule: normal Last Changed Author: anonymous Last Changed Rev: 5 Last Changed Date: 2002-11-20 16:02:15 +0000 (Wed, 20 Nov 2002) Text Last Updated: 2002-11-20 16:04:48 +0000 (Wed, 20 Nov 2002) Properties Last Updated: 2002-11-20 16:04:48 +0000 (Wed, 20 Nov 2002) Checksum: MgrstsrYkQjmx+T9GLUTMQ== 編集して commitしてみよう まず適当にファイルを編集してみましょう。 # vi test.txt This is test 2. で、diffをとってみます。これはCVSでいうところの cvs diffに相当する 作業です。subversionでも svn diffコマンドでdiffがとれます。 # svn diff Index: test.txt =================================================================== --- test.txt (revision 5) +++ test.txt (working copy) @@ -1 +1 @@ -This is test. +This is test 2. diff(1)でつかえるオプションをわたすには -x を使います。 # svn diff -x --show-c-function CVSでのcvs commitに相当する作業は subversionではsvn commitです。 # svn commit CVSとおなじようにcommit messageを指定していないと$EDITORがたちあがって commit messageを入力する必要があります。 ---------------------------------------------------------------- --This line, and those below, will be ignored-- M /tmp/test/test.txt ---------------------------------------------------------------- commit messageを入力してエディタを終了するとcommitされます。 ---------------------------------------------------------------- test -> test 2 --This line, and those below, will be ignored-- M /tmp/test/test.txt ---------------------------------------------------------------- Sending test.txt Transmitting file data . Committed revision 6. 変更点をチェキしてみましょう。svn logを使うと commit logをみることが できます。 # svn log test.txt ------------------------------------------------------------------------ rev 6: anonymous | 2002-11-20 16:12:00 +0000 (Wed, 20 Nov 2002) | 2 lines test -> test 2 ------------------------------------------------------------------------ rev 5: anonymous | 2002-11-20 16:02:15 +0000 (Wed, 20 Nov 2002) | 2 lines This is test module. ------------------------------------------------------------------------ svn diff を使うとあるrevisionからの差分をみることもできます。 # svn diff -r 5 Index: test.txt =================================================================== --- test.txt (revision 5) +++ test.txt (working copy) @@ -1 +1 @@ -This is test. +This is test 2. CVSになかったファイルの移動など cvsではファイルの移動は mv しておいて cvs remove、cvs add と するしかありませんでした。subversionではこの点が改善されていて svn moveコマンドでファイルの移動ができます。またsvn copyコマンド を使うとファイルのコピーもできます。 ディレクトリ subversionではディレクトリも svn mkdirコマンドを使うことで つくっていくことができます。ディレクトリを削除した時は svn deleteでリポジトリに反映させることができます。 $Id$とかをいれてみる。 # vi test.txt This is test 2. $Id$ # svn commit # svn log ------------------------------------------------------------------------ rev 7: anonymous | 2002-11-20 16:17:21 +0000 (Wed, 20 Nov 2002) | 2 lines add $Id$ ------------------------------------------------------------------------ rev 6: anonymous | 2002-11-20 16:12:00 +0000 (Wed, 20 Nov 2002) | 2 lines test -> test 2 ------------------------------------------------------------------------ rev 5: anonymous | 2002-11-20 16:02:15 +0000 (Wed, 20 Nov 2002) | 2 lines This is test module. ------------------------------------------------------------------------ # cat test.txt This is test 2. $Id$ あれれ、かわっていませんね。subversionでは、このようなkeyword おきかえはpropertyを設定しないとおこなわれないことになっています。 propoertyを設定するには、次のように svn:keywords propertyに おきかえするkeyword(たとえばIdとか)を設定します。 # svn propset svn:keywords "Id" test.txt property `svn:keywords' set on 'test.txt' 現在設定されているpropertyはsvn proplist, svn propgetでわかります。 # svn proplist test.txt svn:keywords # svn propget svn:keywords test.txt Id ただ propertyを設定しただけではworking copyでの置きかえはおこなわれません。 # cat test.txt This is test 2. $Id$ # vi test.txt This is test. $Id$ # svn commit -m 'remove 2' Sending test.txt Transmitting file data . Committed revision 8. # cat test.txt This is test. $Id: test.txt 8 2002-11-20 16:20:53Z anonymous $ なお、一気に propset するには例えばこうします。 % svn propset svn:keywords Id `find . \( -name .svn -prune \) -o -type f -print | xargs grep -l '\$Id:'` % svn commit anonymousじゃあれなので認証を さて、いままでではユーザ認証をまったくしていなかったので 誰が編集してもanonymousが作業したことになってしまいます。 そこでユーザ認証するようにしてみましょう。 詳しくは /etc/apache2/mods-available/dav_svn.conf に かかれている通りです。 まず passwdファイルを作ります。これはapache2のhtpasswd2 コマンドを使って次のようにつくります。 # htpasswd2 -c /etc/subversion/passwd ukai New password: Re-type new password: Adding password for user ukai で次のように設定します。 # vi /etc/apache2/mods-available/dav_svn.conf DAV svn SVNPath /svn/repos AuthType Basic AuthName "Subversion Repository" AuthUserFile /etc/subversion/passwd Require valid-user これで /svn/reposに対して GET PROPFIND OPTIONS REPORT 以外の アクセスをする時に/etc/subversion/passwdに登録されているユーザ でしかアクセスできないようになります。 設定ファイルをかきかえたらapache2をリスタートします。 # /etc/init.d/apache2 restart 一旦削除してもう一度 checkoutしましょう。 # rm -rf test # svn checkout http://localhost:2481/svn/repos/test A test/test.txt Checked out revision 8. # cd test # echo by anonymous >> test.txt ここで svn commit すると、今度はユーザのパスワードをきいてくるようになります。 # svn commit -m 'add by anonymous' root's password: ここで単にリターンを入力すると、usernameとpasswordをきいてきます。 username: password: どちらもちゃんと入力しないと以下のように認証エラーでcommitできません。 svn: Authorization failed svn: Commit failed (details follow): svn: MKACTIVITY of /svn/repos/!svn/act/aa6787e9-e1af-0310-a675-959a2ab7c54d: authorization failed 次にちゃんと user, passwordを入力してみましょう。 # svn commit -m 'add by anonymous' root's password: username: ukai ukai's password: Sending test.txt Transmitting file data . Committed revision 9. # もしくは checkoutの時などに --username, --password で usernameとパスワードを 指定しておくと、localに認証情報をcacheしておくことができます。 (実際は .svn/auth/ に記録されます) # cd .. # rm -rf test # svn checkout --username ukai --password 'xxxxxx' http://localhost:2481/svn/repos/test A test/test.txt Checked out revision 9. # cd test # vi test.txt This is test. $Id: test.txt 9 2002-11-20 16:29:37Z ukai $ by ukai # svn commit -m 'by ukai' Sending test.txt Transmitting file data . Committed revision 10. conflictの解消 CVSの時と同じように複数の人が同時に編集していると作業がconflictすることが あります。 # svn commit -m 'conflict test' Sending test.txt svn: Merge conflict during commit svn: Commit failed (details follow): svn: Your file 'test.txt' is probably out-of-date. svn: The version resource does not correspond to the resource within the transaction. Either the requested version resource is out of date (needs to be updated), or the requested version resource is newer than the transaction root (restart the commit). # svn update A memo.txt CU test.txt Updated to revision 12. このように conflictしているものは C がついて表示されます。 # cat test.txt This is test. $Id: test.txt 12 2002-11-20 17:41:59Z ukai $ $HeadURL: http://localhost:2481/svn/repos/test/test.txt $ by ukai <<<<<<< .mine conflict test? ======= >>>>>>> .r12 # svn status C test.txt ? test.txt.54040.00001.mine ? test.txt.54044.00001.r12 ? test.txt.54048.00001.r10 # svn info test.txt Path: test.txt Name: test.txt Url: http://localhost:2481/svn/repos/test/test.txt Revision: 12 Node Kind: file Schedule: normal Last Changed Author: ukai Last Changed Rev: 12 Last Changed Date: 2002-11-20 17:41:59 +0000 (Wed, 20 Nov 2002) Text Last Updated: 2002-11-20 16:32:17 +0000 (Wed, 20 Nov 2002) Properties Last Updated: 2002-11-20 17:45:20 +0000 (Wed, 20 Nov 2002) Checksum: WqeO3OAGcvPBoDoBFf5DOA== Conflict Previous Base File: test.txt.54048.00001.r10 Conflict Previous Working File: test.txt.54040.00001.mine Conflict Current Base File: test.txt.54044.00001.r12 ここで次のように conflictを解消することができます。 # cat test.txt This is test. $Id: test.txt 12 2002-11-20 17:41:59Z ukai $ $HeadURL: http://localhost:2481/svn/repos/test/test.txt $ by ukai conflict test ok # svn resolve test.txt Resolved conflicted state of test.txt # svn commit -m 'fix conflict' Sending test.txt Transmitting file data . Committed revision 13. hostでproxy woody側のapacheで次のように proxy_moduleを有効にして NameVirtualHost で virtual hostを設定しておくと、 port番号を気にしないで使えるようにできます。 ここで設定するのはchroot側ではなく、元のwoody側の方です。 LoadModule proxy_module /usr/lib/apache/1.3/libproxy.so NameVirtualHost 192.47.44.103 ServerName svn.fsij.org ServerAdmin webmaster@fsij.org ProxyRequests On ProxyPass / http://localhost:2481/ ProxyPassReverse / http://localhost:2481/ # /etc/init.d/apache stop # /etc/inti.d/apache start branch & tags CVSではtagコマンドを使ってbranchやtagをつくっていましたが subversionではsvn copyというコマンドを使うことでbranchや tagをつくります。これはbranchやtagなどといったものは現状の コピーをもとに編集していったもの、および現状のコピーと とらえることができると考えるといいでしょう。 まず、svn copyする前に上位のディレクトリをsvn mkdirコマンドで 作っておく必要があります。 % svn mkdir http://svn.fsij.org/svn/repos/branch もしつくってないとsvn copyなどをしようとしても svn: RA layer request failed svn: PROPFIND of /svn/repos/!svn/bc/393/test: 404 Not Found svn: Your commit message was left in a temporary file: svn-commit.62368.00002.tmp のようなエラーがでます。 例えば trunk/test にあるもののbranchを作るにはたとえば 以下のようにします。 % svn copy http://svn.fsij.org/svn/repos/trunk/test \ http://svn.fsij.org/svn/repos/branch/test これで現状revisionの trunk/test が branch/test にコピーされます。 このコピーしたものがbranchになり、修正していくことができます。 またコピーした時点を tag をうったものとして考えることもできます。 また、svn switchコマンドをつかって % svn switch http://svn.fsij.org/svn/repos/branch/test とするとworking copyは branchでの作業をするようになります。 ちなみに % svn info をすると Url: がswtichしたほうにかわっているのがわかります。 mergeする時は svn mergeコマンドを使います。 # svn merge url1[@m] url2[@n] hooks CVSでは CVSROOT/loginfo などをつかって、コミット時にlog message などを関係者にメールしたりしていましたが、subversionでは hooks を使います。 # cd /svn/repos/hooks # ls -la total 40 drwxr-xr-x 2 www-data www-data 4096 Nov 28 18:49 . drwxr-xr-x 7 www-data www-data 4096 Nov 18 16:29 .. -rw-r--r-- 1 www-data www-data 1287 Nov 18 16:29 post-commit.tmpl -rw-r--r-- 1 www-data www-data 1375 Nov 18 16:29 post-revprop-change.tmpl -rw-r--r-- 1 www-data www-data 1640 Nov 18 16:29 pre-commit.tmpl -rw-r--r-- 1 www-data www-data 1870 Nov 18 16:29 pre-revprop-change.tmpl -rw-r--r-- 1 www-data www-data 98 Nov 18 16:29 read-sentinels.tmpl -rw-r--r-- 1 www-data www-data 1377 Nov 18 16:29 start-commit.tmpl -rw-r--r-- 1 www-data www-data 100 Nov 18 16:29 write-sentinels.tmpl .tmplというのはサンプルでこの.tmplをのぞいたファイル名にすると実際に 使われるようになります。一番よく使う例としてはcommit logをメールすることで しょう。これは 例えば 次のような hooks/post-commit で実現できます。 #!/usr/bin/ruby REPOS=ARGV[0] REV=ARGV[1].to_i fromaddr='svn@example.com' toaddr='svn-committers@example.com' svnauthor=%x{svnlook #{REPOS} rev #{REV} author}.chomp! svndate=%x{svnlook #{REPOS} rev #{REV} date}.chomp! svnchanged=%x{svnlook #{REPOS} rev #{REV} changed}.chomp! svnlog=%x{svnlook #{REPOS} rev #{REV} log}.chomp! svndiff=%x{svnlook #{REPOS} rev #{REV} diff}.chomp! #commit-email.pl "$REPOS" "$REV" toaddr require 'net/smtp' Net::SMTP.start( 'localhost', 25 ) {|smtp| smtp.send_mail < /svn/dump/`date +%Y-%m-%d` 2>/dev/null CVSからの以降 subversionにはcvs2svnというコマンドが用意されています。 これを使うとcvsのrepositoryからsubversionのrepositoryに 変換することができます。 cvs2svn -s /svn/respo /var/lib/cvs/moge これで HEAD(trunk)が trunk/moge に変換されます。 他に branchesやtagsなどがブランチ、タグ用に作成されます。 annotate? CVSにはcvs annotateコマンドで各行がどのrevisionで最後に 変更されたのかを簡単に調べることができますが、これが subversion にはまだありません。svn 1.0以降に実装される予定です。 最後に /usr/share/doc/subversion/book/book.html に詳しく書かれているので それを見ればokでしょう。