svk によるレポジトリ分割の作業記録

前回svnadmin dump を利用してレポジトリの分割を行いましたが,今回は svk をレポジトリ操作ツールとして使用し,レポジトリの分割をおこなってみました。

svndumpfilter に比べるとやや柔軟性が落ちますが,ことレポジトリの分割ということに焦点をおくのであれば,svk を利用したほうが簡便かつ安定しているようです。以下の手順は私があまり svk に慣れていないので煩雑にみえますが,svk に慣れている方からすると直感的でしょうし DEPOTs の切り替えを行うことなくやってのけることもできるはずです。


他の文献には svk repository をそのまま新レポジトリとする手法が載ってたりしたんですが,今回は SVN レポジトリがリモートにあったため愚直な方法を使いました。もっといい方法があるよ,という方はご教示ください。

前説

svk がなんなのかは省略します。

svkSVN レポジトリをあやつるものであることはいうまでもないですが,バックエンド(というかローカルレポジトリ)として SVN repository を利用しています。以下この svk が利用するローカル SVN repository のことを便宜上 svk repository(ないし DEPOTs)と呼ぶことにします。

svk は各プロジェクトで使用するディレクトリを DEPOTPATH と呼んでいます。たとえば,

  • //mirror/MyProject

のような形式のものが DEPOTPATH です。これは実際に svk repository に mirror/MyProject として存在します。

また,svk はどの DEPOTPATH がリモートの mirror であるのか,や,どの DEPOTPATH をどこに checkout したのか,などの mapping data を保持しています。このような mapping 属性等には,

  • depotpath mapping
  • mirror mapping
  • checkout mapping
  • DEPOTPATH copy chain

などがあります。

たとえば mirror mapping に登録されている DEPOTPATH に対して svk が(内部的に)commit を行った場合,mirror 元と sync をとります。また,checkout mapping に登録されている local path で svk が commit を行った場合,その宛先は checkout mapping で対応する DEPOTPATH になります((だから svk の working directory には .svn などの管理用ディレクトリが必要ないんですね))。

以上の内容についてはドキュメントやソースを読んだわけではなくて,今回の手順から推察したものなので,間違いなどあるかもしれません。また,理解できなくても以下の手順は行えますので。

状況

server というリモートマシン上に SVN レポジトリが存在します。この,

  • Sandbox レポジトリ下の foobar というディレクト
    • (Sandbox レポジトリでは trunk 等のディレクトリは切っていない)

  • 新規 foobar レポジトリを作成し,trunk ディレクトリ下につっこむ

ことが目的です。


リモートマシン(レポジトリサーバ)には svk はインストールされていません。ローカルには svk がインストールされています。

前準備(新規レポジトリの作成)

まずはサーバ側で foobar プロジェクト用のレポジトリを作成します。

[dayflower@server] % svnadmin create foobar

サーバ側でやる必要があるのは,この新規レポジトリの作成のみです。

次に,定番ディレクトリの trunk, tags, branches を作っておきます。

% svn mkdir http://server/repos/foobar/trunk    \
            http://server/repos/foobar/tags     \
            http://server/repos/foobar/branches

リビジョン 1 をコミットしました。

作業用 DEPOTs の作成

現在の DEPOTPATH のマッピングを見てみます。

% svk depotmap --list

Depot                   Path
============================================================
//                      /home/dayflower/.svk/local

ふつーに作業をしていた方なら,このように ~/.svk/local に ROOT(//)の DEPOTPATH への mapping があるかと思います。

svk repository と DEPOTPATH への mapping は複数指定できます。ですが,作業ミスによって普段使いの svk の環境が壊れてしまうと困るので,念のために新しい svk repository を作り,そこを ROOT DEPOTPATH にします。

まずローカルで svkSVN repository を作成します。

% svnadmin create ~/tmp/workplace

次に depotmaprelocate 機能を使って,ROOT DEPOTPATH として指定します。

% svk depotmap --relocate // ~/tmp/workplace

Depot '' relocated to '/home/dayflower/tmp/workplace'.

ここで与えるパスは,必ず絶対パスを指定してください。相対パス(ここの例だと,たとえば tmp/workplace)で指定してしまうと,作業中のディレクトリに対する相対パスとみなされ,DEPOTPATH が不在になったりして怒られるので。~ などのシェル展開を使うのは構いません。

depotmap がどのようになったか確認します。

% svk depotmap --list

Depot                   Path
============================================================
//                      /home/dayflower/tmp/workplace

無事新しい作業用 repository が ROOT DEPOTPATH になりました。

現状の mirror mapping を確認してみましょう。

% svk mirror --list

このように何も表示されません。mirror mapping は,svk repository に記録されているので,現状で mirror mapping はないんですね。

では,checkout mapping についてはどのようになっているでしょうか。

% svk co --list

  Depot Path                            Path
========================================================================
  //local/MyProject                     /home/dayflower/dev/MyProject

このように,私の環境では既存の DEPOTPATH と checkout path のマッピングが表示されました。これは,checkout mapping については ~/.svk/config に記録されているからです。

つまり,現在の状態で ~/dev/MyProjectsvk ci などの作業を行うと,おかしなことになっていまいます。と,警告しておきます。

Sandbox レポジトリの mirror-ing

リモートレポジトリ(Sandbox 側)の mirror 設定を行います。

% svk mirror //Sandbox/foobar http://server/repos/Sandbox/foobar

Mirror initialized.  Run svk sync //Sandbox/foobar to start mirroring.

svk をお使いの方ならお馴染みのメッセージが表示されました。あくまで mirror mapping に登録しただけなのでまだ mirroring されていません。svk sync して,同期しておきます。

% svk sync //Sandbox/foobar

Syncing http://server/repos/Sandbox/foobar
Retrieving log information from 1 to 80
Committed revision 2 from revision 70.
Committed revision 3 from revision 71.

...... snip ......

Committed revision 12 from revision 80.

Sandbox レポジトリ全体では 80 のコミットログがあったのですが,foobar に関わるものは 12 で済んだようです。

NG: mirror mapping の relocate

mirror mapping にも depotpath コマンドと同様,--relocate オプションがついています。これを使えば,簡単に同期できるんじゃ……?

% svk mirror --relocate //foobar/trunk http://server/repos/foobar/trunk  

Mirror source UUIDs differ.

ミラー元の UUID が違う,といって怒られてしまいました。

SVK book をひもとくと……

SVK won't let you relocate a mirror unless the new mirrored repository has the same UUID as the original mirror had. This prevents you from accidentally using relocate to point a mirror at something that isn't really the same repository after all.

Version Control with SVK

間違えて全然違うレポジトリに relocate してレポジトリを壊してしまう可能性を避けるために,このようなチェック機構が働いています。

実際,ここで relocate できたとしても,relocate 先のレポジトリがどのような状況にあるのか svk が知る術はないのでうまくいかないことでしょう。

svk mirror --relocate コマンドは,

  • 同一サーバ上でレポジトリを mv した場合
  • 他のサーバにレポジトリ全体を移動した場合
  • レポジトリのアクセス方法を svn+ssh から http に変えた場合

など,レポジトリ全体の同一性が保たれている場合にのみ行うようにしましょう。

foobar レポジトリの mirror-ing

新規に作成した foobar レポジトリの trunk ディレクトリについて mirror-ing 設定を行います。

% svk mirror //foobar/trunk http://server/repos/foobar/trunk

Mirror initialized.  Run svk sync //foobar/trunk to start mirroring.

さっきも当然のように指定しましたが,レポジトリの下位のみの mirror-ing もできるので素敵ですね(もちろん svn 自体にもその機能があるわけですが)。

% svk sync //foobar/trunk

Syncing http://server/repos/foobar/trunk
Retrieving log information from 1 to 1
Committed revision 14 from revision 1.

まだ新規レポジトリでは何も行っていないので,これだけのコミットログで済みました。実は,今回の状況では destinationsync は行う必要がないのですが(どうせ空なので),念のためにやっておきましょう。


ここまでで設定された mirror mapping を確認してみます。

% svk mirror --list

Path                Source
========================================================
//Sandbox/foobar   http://server/repos/Sandbox/foobar
//foobar/trunk     http://server/repos/foobar/trunk

旧レポジトリと新レポジトリの両者が,DEPOTPATH として mapping 指定されていることがわかります。

旧レポジトリから新レポジトリへの smerge

svk には,レポジトリの変更をパッチ集として他のパスによしなに適用してくれる smerge という素敵コマンドが存在します。普段は svk push 等のコマンドのバックエンドとして自動的に作動するので手で触る必要はないのですが*1,今回のような場合に重宝します。

と,いうことで個別コミット+簡略版コミットログ,にするためのオプション --incremental((-I のような省略形もあります。大文字の<アイ>ですからお間違えなく!)) と --verbatim オプションをつけて smerge を実行してみましょう。

% svk smerge --incremental --verbatim //Sandbox/foobar //foobar/trunk 

Can't find merge base for /Sandbox/foobar and /foobar/trunk

merge する base がわからんよ,と怒られてしまいました。

困ったときの SVK book

The --baseless switch makes the smerge assume there is no common ancestor (which there isn't yet).

Version Control with SVK

smerge コマンドは本来,異なる branch などで進行したコミットログを別の branch 等に merge する際に用いるコマンドです。ですから,普通はどのリビジョンからの差分か,という情報が必要になります。ですが,今回の場合は,新旧レポジトリともに,作成時点から最新時点までのコミットを扱うわけです。なので,共通する祖先はいませんよ,ということを明示する必要があります。

--baseless((-B という短縮形アリ)) というオプションを指定してやり直します。

% svk smerge --baseless --incremental --verbatim //Sandbox/foobar //foobar/trunk

Auto-merging (0, 12) /Sandbox/foobar to /foobar/trunk (base /:0).
===> Auto-merging (0, 1) /Sandbox/foobar to /foobar/trunk (base /:0).
Merging back to mirror source http://server/repos/foobar/trunk.
Empty merge.
===> Auto-merging (1, 2) /Sandbox/foobar to /foobar/trunk (base /:0).
Merging back to mirror source http://server/repos/foobar/trunk.
Empty merge.
===> Auto-merging (2, 3) /Sandbox/foobar to /foobar/trunk (base /:0).
Merging back to mirror source http://server/repos/foobar/trunk.
A   foobar.c
A   Makefile
 U  .
New merge ticket: 41f9f42d-c407-4326-8799-8341091d697e:/foobar:71
Merge back committed as revision 2.
Syncing http://server/repos/foobar/trunk
Retrieving log information from 2 to 2
Committed revision 15 from revision 2.

...... snip ......

===> Auto-merging (11, 12) /Sandbox/foobar to /foobar/trunk (base /Sandbox/foobar:11).
Merging back to mirror source http://server/repos/foobar/trunk.
U   foobar.c
New merge ticket: 41f9f42d-c407-4326-8799-8341091d697e:/foobar:80
Merge back committed as revision 11.
Syncing http://server/repos/foobar/trunk
Retrieving log information from 11 to 11
Committed revision 24 from revision 11.

無事 merge されました。新レポジトリに旧レポジトリの変更ログがコミットされたことになります。

ここで注目してほしいのは,各 merge ログ。

Merging back to mirror source http://server/repos/foobar/trunk.

mirror 元のソース(http://server/repos/foobar)にまで merge が及んでいます。これは mirror mapping に登録されている DEPOTPATH に対する操作なので,自動的に mirror 元に波及するんですね。

つうわけで,以上の手順により,新レポジトリに旧レポジトリのコミットログがコピーされました。

後始末

あとは,作業用に使っていた svk repository の depot mapping を解除して,削除すればおしまいです。

% svk depotmap --relocate // ~/.svk/local
Depot '' relocated to '/home/dayflower/.svk/local'.

% svk depotmap --list

Depot                   Path
============================================================
//                      /home/dayflower/.svk/local

今回は新規に作業用の svk repository を作成したので,単にディレクトリごと削除してしまって構いません((既存の DEPOTs を利用していた場合,svk delete を使用する必要があるでしょう))。

% rm -rf ~/tmp/workplace

さて,もともと使っていた DEPOTs の情報は残っているでしょうか。

% svk mirror --list
Path                        Source
=================================================================
//mirror/MyProject          http://server/repos/MyProject

無事,普段使いの svk 環境に復帰しました。

*1:Route 477 が詳しいです