レポジトリ分割の作業記録
とりあえず新規レポジトリを作るまでもないけど作業履歴をとっておきたい,という場合,わたしは Sandbox というレポジトリに全部つっこんでいます。
- /Sandbox
- /project1
- /project2
- /foobar
各サブプロジェクトごとに trunk 等を切ることもありますし,そうしないこともあります。こんな感じで Sandbox で作業してて熟してくると,単独プロジェクトとして分割管理したくなります。
- /foobar
- /trunk
- /tags
- /branches
できれば,いままでの作業履歴も移行したい!
「svnadmin dump
」や「svndumpfilter
」というキーワードでググると色々でてきますが,今回は下記のサイトを参考にしました。
- Subversionの続き履歴を残したままリポジトリ間でファイルを移動 - StepupEngineeringはてな支店
- cvsからsubversionに移行するときにやったこと その1
- svndumpfilter&mkdir: 気の向くままに・・・
では,実際の作業記録です。
まず svnadmin dump
の用法をみてみます。
% svnadmin dump --help dump: usage: svnadmin dump REPOS_PATH [-r LOWER[:UPPER]] [--incremental] Dump the contents of filesystem to stdout in a 'dumpfile' portable format, sending feedback to stderr. Dump revisions LOWER rev through UPPER rev. If no revisions are given, dump all revision trees. If only LOWER is given, dump that one revision tree. If --incremental is passed, then the first revision dumped will be a diff against the previous revision, instead of the usual fulltext. Valid options: -r [--revision] arg : specify revision number ARG (or X:Y range) --incremental : dump incrementally --deltas : use deltas in dump output -q [--quiet] : no progress (only errors) to stderr
--incremental
とか --deltas
とか -r
とか魅惑のオプションがいろいろあるのですが,普通にすべて dump するのなら特に指定する必要はなさそうです。
ではリモートから dump
してみます。
% svnadmin dump http://server/repos/Sandbox > sandbox.dump svnadmin: 'http://server/repos/Sandbox' is an URL when it should be a path
(ローカル)パスじゃないとだめだよ,と怒られてしまいました。
しかたがないので,レポジトリのおいてあるサーバにログオンして dump することにします。
$ svnadmin dump /repos/Sandbox > sandbox.dump * Dumped revision 0. * Dumped revision 1. * Dumped revision 2. ...... snip ...... * Dumped revision 66. * Dumped revision 67. * Dumped revision 68.
無事 dump できました。
このままだと Sandbox レポジトリすべての dump になってしまっているので,今回扱いたい foobar プロジェクトだけ抜き出します。
まずは svndumpfilter
の用法を確認。
$ svndumpfilter help general usage: svndumpfilter SUBCOMMAND [ARGS & OPTIONS ...] Type 'svndumpfilter help <subcommand>' for help on a specific subcommand. Type 'svndumpfilter --version' to see the program version number. Available subcommands: exclude include help (?, h) $ svndumpfilter include --help include: Filter out nodes without given prefixes from dumpstream. usage: svndumpfilter include PATH_PREFIX... Valid options: --drop-empty-revs : Remove revisions emptied by filtering. --renumber-revs : Renumber revisions left after filtering. --preserve-revprops : Don't filter revision properties. --quiet : Do not display filtering statistics.
include
や exclude
というコマンドで抽出対象パスを限定できるようです。今回の場合,レポジトリの新規移行がメインなので --drop-empty-revs
が有用ぽい。
ということで,foobar に限定した dump を抽出します。
$ cat sandbox.dump | \ svndumpfilter --drop-empty-revs --renumber-revs include /foobar > foobar1.dump Including (and dropping empty revisions for) prefixes: '/foobar' Revision 0 committed as 0. Revision 1 skipped. Revision 2 skipped. ...... snip ...... Revision 55 skipped. Revision 56 skipped. Revision 57 committed as 1. Revision 58 committed as 2. ...... snip ...... Revision 67 committed as 11. Revision 68 committed as 12. Dropped 56 revision(s). Revisions renumbered as follows: 68 => 12 67 => 11 ...... snip ...... 58 => 2 57 => 1 56 => (dropped) 55 => (dropped) ...... snip ...... 2 => (dropped) 1 => (dropped) 0 => 0 Dropped 121 node(s): '/hogehoge' '/hogehoge/fugafuga' ...... snip ......
Sandbox 自体は 68 回コミットしたことになっていますが,foobar についてはわずか 12 回分のリビジョンで済みました。
このままの dump file だと新レポジトリの中に foobar というディレクトリを掘って,その下にファイルが置かれてしまいます。レポジトリの各操作対象パスは Node-path: ***
という書式で書かれているのでそれを置換することにします。上記 yoshiki@Clouder さんの作業を参考にして perl のワンライナーで置換しました。
$ perl -pe 's{^Node-path: foobar}{Node-path: trunk}' foobar1.dump > foobar2.dump
以上で foobar 用の dump ファイルが出来上がったので,実際に foobar レポジトリを作成します。また,あらかじめ trunk, tags, branches ディレクトリを作成しておきます。
$ svnadmin create /repos/foobar % svn mkdir http://server/repos/foobar/trunk \ http://server/repos/foobar/tags \ http://server/repos/foobar/branches リビジョン 1 をコミットしました。
いよいよ dump ファイルのインポートです。svnadmin load
コマンドを使います。
$ svnadmin load --help load: usage: svnadmin load REPOS_PATH Read a 'dumpfile'-formatted stream from stdin, committing new revisions into the repository's filesystem. If the repository was previously empty, its UUID will, by default, be changed to the one specified in the stream. Progress feedback is sent to stdout. Valid options: -q [--quiet] : no progress (only errors) to stderr --ignore-uuid : ignore any repos UUID found in the stream --force-uuid : set repos UUID to that found in stream, if any --use-pre-commit-hook : call pre-commit hook before committing revisions --use-post-commit-hook : call post-commit hook after committing revisions --parent-dir arg : load at specified directory in repository
色々オプションがあるようですが,今回の場合何も指定しなくてよさそうです。
いざインポート!
$ svnadmin load foobar < foobar2.dump <<< Started new transaction, based on original revision 1 svnadmin: File already exists: filesystem '/www/repos/foobar/db', transaction '1-1', path 'trunk' * adding path : trunk ...
すでに trunk というパスがレポジトリにあるよ,と怒られてしまいました。dump ファイルの中には trunk フォルダを生成するというログも入っているので当たり前です。
ここでレポジトリの作成からやり直す手もあるのですが,今回は dump ファイルを手直しして,trunk ディレクトリを作成している部分を削除することにしました。
$ less foobar2.dump ...... snip ...... PROPS-END Revision-number: 1 Prop-content-length: 121 Content-length: 121 K 7 svn:log V 17 foobar の作成 K 10 svn:author V 9 dayflower K 8 svn:date V 27 2007-11-21T01:08:46.472700Z PROPS-END Node-path: trunk Node-action: add Node-kind: dir Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 ...... snip ...... $ vi foobar2.dump
Revision-number: 1
では trunk というディレクトリを追加しているだけです。ですので vi で「Revision-number: 1
」のエリアをごっそり削除してみました。
さぁ,これで今度こそインポートできるでしょうか。
$ svnadmin load /repos/foobar < foobar2.dump <<< Started new transaction, based on original revision 2 * editing path : trunk ... done. * adding path : trunk/Makefile ... done. * adding path : trunk/foobar.c ... done. ------- Committed revision 2 >>> <<< Started new transaction, based on original revision 3 * editing path : trunk/foobar.c ... done. ...... snip ...... ------- Committed revision 12 >>>
無事できました*1。
今回の場合,svndumpfilter
の出力をほぼそのまま使うことができましたが,レポジトリの環境によっては苦労も多いみたいです。
dump ファイルをあれこれ操作したおすなら,SVN::Dump という cpan モジュールを使うといいのかもしれません。自称 alpha release とのこともあり,詳しく調べてはいません。
また,svk を使うともっと簡単なステップで分割することもできるようです(⇒koshigoewiki:開発環境:svk [KoshigoeWiki]の「リポジトリ分割」)。今回の環境では svk がインストールされていなかったので svn を使いました。
*1:ただ,実はこの手順だと revision 1 のほうが 2〜12 より日付が新しくなってしまうんですよね。ちょこっと不安。