ゴミ箱.net

汚物は消毒

Subversionのリポジトリが壊れて地獄を見た話

ある日Subversionのリポジトリにアクセスしてファイルをチェックアウトしようとしたら
no such revision
という見慣れぬエラーが出た。
特定のリビジョンで変更があったファイルを取得しようとするとエラーになるようだ。過去の履歴についても、そのリビジョンより前のリビジョンは特に問題なく取得できるが、そのリビジョン以降は取得することは出来ない。
svnadminによるダンプもそのリビジョンで停止するので不可能。どうあがいても絶望

リポジトリは悪名高いBerkeley DBではなくFSFS形式で作っている。リポジトリのフォルダの中のdb/props/の下を確認したら、特定のリビジョン番号に対応するファイルだけが消えていた。
その消えたファイルにはリポジトリで変更されたファイルの差分が含まれている。そのため、変更されたファイルのこのリビジョン以降の履歴が一切取得できない。
db/revprops/の下に保存されていたファイルが残っていたのは不幸中の幸い。

なぜファイルが1個だけ消えたのか原因は分からない。ハードディスクの異常か何かで偶発的にファイルが消えてしまったのか、それともリポジトリのフォルダをコピーしたときにエラーが発生してファイルが1個だけ消えてしまったのか。

リポジトリのバックアップは取ってあったが今回は役立たずだった。どうもリポジトリはだいぶ前に壊れていたがずっとそれに気づかずにいたせいで、最古のバックアップの時点ですでにリポジトリが壊れていた。そのためバックアップから消えたファイルをコピーして復活させることも出来ない。
さてどうしたものか。

結論から言うと、消えたリビジョンで行った操作がすべて分かっており、かつ変更された後のファイルが残っているという条件があればリポジトリを修復することが出来る。
今回は、消えたリビジョンにおいて変更されたファイルがそのまま作業フォルダにチェックアウトされていたので助かった。

db/revsの中を「r████」(████は消えたリビジョン番号)でgrepすると、そのリビジョンを参照するリビジョンが分かる。
また、db/revpropsの中には消えたりビジョンに対応するコミットメッセージが残っている。
これらをもとにそのリビジョンでの操作に当たりをつける。

次に、壊れたリポジトリの複製を作成する。
単にリポジトリのフォルダをコピーすればよい。

コピーしたリポジトリを書き換え、消えたリビジョンの直前のリビジョンに戻す。
コピーしたリポジトリのフォルダのdb/revs、db/revpropsの中の、消えたリビジョンおよびそれ以降に対応するファイルをすべて削除する。フォルダの中はリビジョン1000ごとにフォルダが分かれており、その中のファイル名=リビジョン番号となっている。リビジョン1234ならdb/revs/1/1234、db/revprops/1/1234だ。
また、コピーしたリポジトリのフォルダの中にあるファイルcurrentをテキストエディタで編集し、消えたリビジョンの直前のリビジョン番号に書き換える。
その後以下のコマンドを実行する。これをすることで、ファイルrep-cache.dbに残った整合性のないデータが削除される。

svnadmin recover 複製したリポジトリのフォルダ
 
コピーしたリポジトリの最新のリビジョン(つまり消えたリビジョンの直前のリビジョン)を適当なフォルダにチェックアウトする。

チェックアウトしたリポジトリに対し、消えたリビジョンのときと完全に同じファイルを使って同じ操作をし、コミットする。
消えたリビジョンと同じリビジョン番号でコミットされ、当時と全く同じ変更が記録されるはずだ。

作り直した変更ファイルをもとのリポジトリに書き戻す。
コピーしたリポジトリのフォルダの中のdb/revsの中にある、消えたリビジョンに対応するファイルを、元のリポジトリのフォルダの対応する場所にコピーすればよい。
念のため、整合性の確認をしておいたほうがよいだろう。
リポジトリのフォルダの中のdb/revsの中のファイルは、他のリビジョンを参照するときに「1-234.5-678.r1234/12345678」のような書式の識別子を使用する。
「1-234.5-678」がファイルの識別子、「r1234」は参照するリビジョン、12345678はdb/revsの中のファイルにおける識別子が指すデータの開始位置(単位はバイト)である。
db/revsの中を「r████」で検索すれば、消えたリビジョンのファイルに含まれているはずの識別子を一括して取得することができる。
消えたリビジョンに対応する作り直したファイルの中に、取得した識別子が正しい位置に書かれていることを確認する。
Linuxであれば、ファイルの任意の位置を表示するには以下のコマンドが使える。
dd if=ファイル名 ibs=1 skip=開始位置 | head
作り直したファイルの所定の位置に「id: ~」という形で識別子が書かれていれば問題ないだろう。(逆に識別子がなかったり、開始位置がずれていたりしたらデータの復元に失敗したということである。諦めろ。試合終了だ)

これで無事リポジトリは復元された。どっとはらい。
スポンサーサイト

PageTop

コメント


管理者にだけ表示を許可する