[Subversion]巨大なリポジトリから多数の不要ファイルを恒久的に削除する方法

はじめに

肥大化したSVNレポジトリの扱いはどうすればよいでしょうか?

Subversionのレポジトリは、リビジョンが進むにつれて容量が増える一方であり、何もしなければ容量が減ることは絶対にありません。
svn del などしても、"削除マーク"というメタ情報が追加されるだけであって、データ量は全く減らず、むしろ逆にメタ情報の分だけ増えます。
バージョン管理ツールというのはそういうものだからです。

ただそうはいっても、リビジョンが数万件になってくるとレポジトリの容量は頭が痛い問題です。
何とかしてレポジトリ内の大量の不要データを削除する方法はないものでしょうか?

svndumpfilterを使ってレポジトリをダイエットさせる

唯一の答えはこれです。
試行錯誤の末にこれをうまく成功させるコツがわかったので、紹介します。

やり方

このようなシェルスクリプト1個作って実行するだけOKです。
filter.sh
svnadmin dump /path/to/repo 2>/dev/null |  svndumpfilter exclude  \
 /tools/LocalTools/fuga \
 /branches/hoge/zip \
 /trunk/www/zip \
 /trunk/www/images/user/  \
 /branches/dqneo/123/www/images/user/ \
 /branches/dqneo/456/www/images/user/ \
 ... このように除外パス名をえんえんと列挙 ...
 /document/仕様書/過去資料/ \
 /document/デザインデータ/ \
 |  svnadmin load /path/to/repot_new
(¥マークは実際にはバックスラッシュです)

svndumpfilter exclude の引数に、除外したいパス名をつらつらと書きます。
私のケースでは100行超えるほどになりました。

dump先をファイルではなくパイプでそのまま処理するのがコツです。(後述)
実行する
$ bash filter.sh

# 成功したら新旧レポジトリをリネームして入れ替え

mv /path/to/repo /path/to/repo_old
mv /path/to/repot_new /path/to/repo

成功させるにはコツがいる

コミット数万件あるような巨大レポジトリのケースでは、上記スクリプトが一発で成功することはほぼないでしょう。
以下、遭遇したトラブルと対処法を書きます。

loadが失敗する

ブランチ作成、マージ、svn cp や svn mv などによって複雑な分岐の歴史があるとき、パスの整合性が壊れてエラーが出ます。
<<< オリジナルのリビジョン 11735 に基づき、新しいトランザクションを開始しました
svnadmin: E160013: ファイルが見つかりません: トランザクション 11734-91y, パス 'trunk/www/top/2010-05'

svn log $REPO -v -c 11735
------------------------------------------------------------------------
r11735 | dqneo | 2010-09-06 10:46:33 +0900 (月, 06  9月 2010) | 1 line
Changed paths:
   A /trunk/www/top/2010-04 (from /trunk/www/top/Apr:10118)
   D /trunk/www/top/Fuga
これは、svn mvの整合性が壊れたというエラーです。
svndumpfilter: E200003: コピー元のパス '/branches/dqneo/top/www/top/2011-01' が不正です。
これは、svn cpの整合性が壊れたときのエラーです。

コピー元またはコピー先の片方だけがexclude除外されていると、このようにパスの整合性エラーが起きます。

私のケースではこのエラーが何十回も出ました。
また、エラーが出ずに途中で突然止まる場合もあります。その場合もたいていはパスの不整合が原因です。
整合性エラーの対処法
先ほどのシェルスクリプトの、svndumpfilter exclude の引数に除外パスを追加してあげましょう。

追加したら、途中からダンプ&ロードを再開できるようにオプション指定します。
svnadmin dump /path/to/repo -r 11735:HEAD  2>/dev/null |  svndumpfilter exclude  \
 /tools/LocalTools/fuga \
 /branches/hoge/zip
 (以下略)
 |  svnadmin load /path/to/repot_new
dumpのオプションでリビジョン指定するのがポイントです。
こうすれば、ダンプ&ロードを続きから実行できます。
エラーにが出たら、また上の手順でexclueパスを追加して再開します。
根気よく一個一個依存関係を解決していくしかありません。
ダンプ&ロードで一時ファイルに書き出さない方がよい
ここは盲点です。
ダンプァイルを作ってそれをリストアするという方法だと、エラーで止まったときに途中から再開することができません。
私は当初、svnadmin dumpでダンプファイルを作成して、それに対してsvndumpfilterをかまし、svnadmin loadで戻すという作戦でやっていました。
# ダンプ
svnadmin dump /path/to/repo > repo.dump

# フィルタにかける

svndumpfilter exclude \
 /path/foo \
 /path/bar \
< repo.dump > repo_new.dump

# リストア
svnadmin create /path/to/repo_new
svnadmin load /path/to/repo_new < repo_new.dump
このやり方だと、loadを途中から再開することができないのです。
svnadmin loadにはリビジョン指定するオプションがないからです。
(ダンプファイルをhackしてリビジョンで分割すればできるかもしれないが、むちゃくちゃ難易度が高い)
つまり整合性エラーでこけたときに、また最初からダンプ&ロードをやり直すハメになります。
コミットが数万件あると数時間のロスになるので痛いです。

結局一時ファイルをつくるのをやめて、直接パイプで処理したらうまく行きました。
svnadmin dump /path/to/repo  2>/dev/null | svndumpfilter exclude \
 /path/foo \
 /path/bar \
 | svnadmin load /path/to/repo_new
ダンプ時の標準エラー出力は捨てる
パイプで処理する場合、ダンプの進捗とロードの進捗が両方画面に表示されます。
これは非常に見にくいです。
ダンプに失敗することはまずないので、ダンプ時の標準エラー出力は捨てましょう。( 2>/dev/null )

うまく行かない場合は、ダンプ&リストを何回かにわける

excludeにたくさん指定を書いて一気にやろうとすると、失敗ばかりでなかなか進みません。
1回のダンプ&リストアで全部整理しようとせず、ダンプ&リストアを何回もやって少しずつダイエットさせれば確実にできます。

私は何度も何度も失敗を繰り返し、途中ストレスでお腹を壊したり(笑)しながら丸2日かかって成功にいたりました。
この記事が誰かのお役に立てば幸いです。
カテゴリ:

人気記事