SVNリポジトリの一部だけを別リポジトリに移行させたときの失敗話

現在、私が会社で携わっている開発では、ソースコードやドキュメントの管理をSVNで行っています。

派生プロジェクトも同じリポジトリで管理した結果…。

やっぱりリポジトリは分けたい!

SVNからのバックアップは、毎日、差分のダンプ、週一で全体のダンプを取得し、別のHDDに保管しています。

が、全体のダンプを取るのに恐ろしく時間がかかっています。リビジョンが15,000近くになり、先日、ついにダンプファイルが80GBを超えました。ダンプ出力に要する時間は、優に2時間を超えています。

で、この秋から新たな派生プロジェクトが開始されたのですが、これまでのルール通り、そのリポジトリにディレクトリを追加。さっそくファイルを百数十MBほど追加したんですが、「いや、待てよ」と。

同じリポジトリにある他のプロジェクトは、既に完了したものや、間もなく完了するものがほとんどで、この先「動かない」ディレクトリがたくさんある状況です。それらを含めての全体のダンプ出力は、なんか、無駄が多いよね、と。

ならば、と、前例には反するんですが、新たにリポジトリを作成しました。

でも、旧リポジトリにコミットしたファイル群は、設計やなんやで既に数十のコミットが発生していて、それらの履歴を無視したくはない、残したまま移行したい、という状況が発生したわけです。

旧リポジトリからダンプを取る

ダンプの取得は簡単。svnadmin dump コマンドを使用します。

さすがに業務の内容をさらすわけにはいかないので、仮称のリポジトリで説明します。

A-Project
    /branches
    /tag
    /trunk
        /A1-subProj
        /A2-dubProj
        /B1-subProj
            /Doc
 B1-Project
    /branches
    /tag
    /trunk

B1-Project は、今回のプロジェクト用に新たに作成したリポジトリです。この /trunk の下に、 A-Project にある /trunk/B1-subProj/Doc を移行して、B1-Projectの /trunk/Doc にしたい、という狙いです。

※以下、実際に実施した誤った手順の説明がありますので、参考にする方は最後までご確認ください。

簡単簡単、とばかりに、リポジトリのある階層で

# svnadmin dump A-Project | svndumpfilter include /trunk/B1-subProj > B1.dump

として、ダンプファイルを取得しました。

これで、B1プロジェクト以外は「歯抜け」になったダンプが出力されます。全体のダンプだと80GB超になりますが、出来たダンプファイルは百数十MBなので、これでOK、と(思ってました)。

新リポジトリに読み込ませる

読み込ませるのは、svnadmin load を使います。

# svnadmin load B1-Project < B1.dump

B1.dump と入力してEnterキーを押下!

順調にマージが始まったように見えたんですが、コミットが完了する前に、B1-ProjectをチェックアウトしていたWindows PC のTortoiseSVN で、ルートからのコミットログを見てみると、

「単に番号が飛ばないようにするための空のリビジョンです。」

というログが大量にコミットされた状態になっていました。

これはマズイ、と、svnadmin load を停止。20ほどのコミットが済んだ状態になっています。恐らく、放っておくと、14,000以上の、この空のコミットが続いたはずです。

ローカルPCでのマージでは無いので、これが正式リポジトリの状態になってしまったので、こそっとバックアップから修復(笑)

昨日の最終コミットまで戻りました。

再度、旧リポジトリからダンプを取る(正)

この「空のリビジョン」作成を防ぐためには、ダンプを取る時点で一手間が必要だったようです。

それは、svndumpfilter にオプションを追加して、不要なリビジョンを抑止する、というもの。

# svnadmin dump A-Project | svndumpfilter include --drop-empty-revs --renumber-revs /trunk/B1-subProj > B1.dump

–drop-empty-revs は、空のリビジョンを除外するためのオプション。

–renumber-revs は、上のオプションを付けたときに、リビジョン番号を詰めるためのオプションです。

出来たダンプファイルは、当初のものより、わずかにサイズが小さくなっています。これは、不要なリビジョンが飛ばされた結果ですね。

再度、新リポジトリに読み込ませる

念のため、新リポジトリからチェックアウト済みの環境を一旦削除し、新たにチェックアウトしました。

前述の通り、昨日の夕方にコミットしたところまで、復活です(若干、冷や汗ものでしたが)。

# svnadmin load B1-Project < B1.dump

コミットの数は、旧リポジトリ A-Project の /trunk/B1-subProj/Doc にコミットした数だけになりました。

最新のコミットログをチェックすると、旧リポジトリに最後にコミットした時間・コミット者・コメントで表示されていますので、完了です。

と言いたいところだったんですが。。

なぜか「単に番号が飛ばないようにするための空のリビジョンです」のログ

数十あるログをさかのぼって確認していくと、突如、「単に番号が飛ばないようにするための空のリビジョンです」のログになります。

最新のコミットログは全く問題なく、最初(リポジトリ作成)から昨日までのログも問題ありません。

その次のログから、十数コミット分ほどが空リビジョンになっています。しかも、その日付は、旧リポジトリの作成日。旧リポジトリの先頭から十数コミット分の日付で「単に番号が飛ばないようにするための空のリビジョンです」が出ているわけです。

ここで、もう一度、手順を確認。

読み込ませたダンプファイルは、空のリビジョンを除外するオプションを付けて生成したもの。実際、そのダンプファイルをテキストエディタで読み込むと、最初のリビジョンが、B1-subProjでコミットされたものであることが、ファイル名やログ内容から読み取れます。

また、そのダンプファイルを読み込ませた環境は、昨夕まで巻き戻したものに、違いありません。別環境でチェックアウトすると、昨夕までのログが表示されます。

なので、空のリビジョンが入り込む余地はないはずです。

頭をひねりながら、念のためにもう一度、新リポジトリを昨夕分にバックアップから戻し、ダンプファイルをロードします。

が、結果は同じ。「単に番号が飛ばないようにするための空のリビジョンです」となる範囲も同じです。

念のため、チェックアウトしたルートに作られた .svn フォルダの中身を確認すると、空のリビジョンと表示されたリビジョンにも、正しく、旧リポジトリでのコミット内容が含まれているようです。

適当な空(と表示されている)リビジョンで、直前のリビジョンと比較をしてみると、期待する差分が表示されます。

要は、ログの表示が壊れているだけのようです。が、「だけ」では済まされないですよね。。

これで解決

さんざん悩んで、思い至ったのが、TortoiseSVNの「キャッシュ」です。

この十数個の「空のリビジョンです」は、最初の手順で空のリビジョンを作ってしまった時の名残じゃないか、と。

TortoiseSVNの「ログキャッシュ」で、「キャッシュされているリポジトリ」から、対象となる新リポジトリを選択して消去。

別PCでのキャプチャのため、中身は空です

で、もう一度、履歴を取得しなおすと、期待通りの正しいログが表示されました。

接続状況が悪い時でも、ログが見れるのはありがたいですので、このキャッシュは非常に有用なのですが、こういう影響も出るんですね。(リポジトリ側の過去ログ情報が変わることは、少ないでしょうし)

というわけで、ログがおかしいな、と思ったら、まずキャッシュを消してみる、という教訓を得た出来事でした。

そうそう、実は「作業完了」ではないですね。

ロード後の状態は、B1-Project の下に、/trunk/B1-subProj/Doc という構成でコミットされています。期待する構成は、 /trunk/Doc ですから、/B1-subProj 下にある /Doc を、/trunk の下に「移動」させましょう。

Doc フォルダを右ドラッグで移動させて「SVNバージョン管理下の項目をここに移動」とするのが簡単ですね。その後、空になった /B1-subProj を削除して、コミットしておきます。

これで、移動が完了。旧リポジトリでの作業時の履歴も残りますし、不自由なく新リポジトリで作業を続けられそうです。