このページは RepositoryCorruption の日本語訳です。

リポジトリ/作業領域状態(dirstate)破損への対応

破損の原因

Mercurial の実行は、格納領域に対する保護無しに、通常のユーザ権限で行われます。そのため、ユーザ、ツール、実行環境が、特別な権限等を持たなくても、うっかりリポジトリを破損してしまう可能性があります。破損の原因には、以下のようなものも含まれます:

多くの場合、利用者の不注意によるものが大半です。Mercurial はリポジトリ破損に対して、以下のような多段階に渡る防護策を講じています:

例えば clone 操作などによって、重要なデータを常時バックアップするのは、他のデータと同様に良い安全策と言えます。

破損の分類

破損は大きく2つに分類されます:

正確には、履歴情報に対する被害のみが 'リポジトリ破損' と呼ばれるべきですが、とりあえずここでは、両者に関して説明します。

Dirstate の破損

この破損は、管理対象ファイルに関する、現在実施されている操作の情報を記録したものが、被害を受けている状況です。ここで重要なのは .hg/dirstate ファイルです。このファイルには、親リビジョンの情報や、作業領域中の全管理対象ファイルに関する情報が記録されています。このファイルが破損した場合、以下のような挙動が見られます:

$ hg st
M foo
A bar
$ hg id
58745409d2e2+ tip
# ※ 以下は意図的に破損させるための操作です
$ echo fdsj..適当な文字列..goo > .hg/dirstate
$ hg st
abort: unknown revision '6664736a666b67736a64666867736b6466686b67'!

dirstate ファイルの破損を復旧するのは、多くの場合、単純です。現時点での作業領域の親リビジョンが分かっているなら、以下の実行で復旧できます:

$ hg debugrebuildstate -r tip  # tip で作業していたと仮定した場合の状態再構築
$ hg id
58745409d2e2+ tip
$ hg st
M foo
? bar

状態再構築しても、Mercurial は foo が改変されたことは認識できますが、bar が追加登録されたことは忘れてしまいます。

debugrebuildstate コマンドで復旧できない場合は、現在使用中のリポジトリから、別なリポジトリへと複製(clone)を行い、そちらで作業を再開する方法があります (--pull オプション指定は、潜在的なハードリンクに関する問題を回避します):

$ cd ..
# ※ repo は被害を受けた『元』リポジトリ
$ hg clone --pull repo fixed-repo
requesting all changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 1 files
updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd fixed-repo
$ hg id
58745409d2e2+ tip

あとは、被害を受けた元リポジトリ(repo)から、修復済みのリポジトリ (fixed-repo)へと、変更を加えたファイル等を複製すれば良い訳です。

リポジトリの破損

この破損は、履歴記録、特に .hg/store 配下のファイルが被害を受けている状況です。この破損には様々な原因がありますが、多くは利用者の不注意によるものです (例: パス名に 'foo.c' を含むファイルの削除)。具体的な例で見てみると:

$ hg verify
checking changesets
checking manifests
crosschecking files in changesets and manifests
checking files
1213 files, 8591 changesets, 17158 total revisions

$ rm .hg/store/data/mercurial/error.py.i  # うっかり削除!
$ hg verify
checking changesets
checking manifests
crosschecking files in changesets and manifests
checking files
 data/mercurial/error.py.i@7633: missing revlog!
 7633: empty or missing mercurial/error.py
 mercurial/error.py@7633: f6aad9f78a08 in manifests not found
 mercurial/error.py@7636: 4fb29207f327 in manifests not found
 mercurial/error.py@7637: 3bfff9613e8b in manifests not found
 mercurial/error.py@7640: 40ee894622ad in manifests not found
 mercurial/error.py@7641: e640820306d6 in manifests not found
 mercurial/error.py@7643: f43c616251f5 in manifests not found
 mercurial/error.py@7644: 455d738b74c7 in manifests not found
 mercurial/error.py@7646: a3128b43b03f in manifests not found
 mercurial/error.py@7947: b4a8b72bfa1c in manifests not found
 mercurial/error.py@8144: 1f996809c441 in manifests not found
 mercurial/error.py@8225: e1537136a8d0 in manifests not found
 mercurial/error.py@8226: 5f91269779c0 in manifests not found
 mercurial/error.py@8227: 6706abc98ab2 in manifests not found
1213 files, 8591 changesets, 17145 total revisions
15 integrity errors encountered!
(first damaged changeset appears to be 7633)

{X} リポジトリの修復を行う場合、必ずバックアップをとってから実施しましょう!

基本的な修復

以下の例では、リポジトリの被害が、リビジョン 7633 以降と仮定します。履歴の記録方式が、追記に限定されている Mercurial の特性を生かすことで、問題のリビジョン以前の履歴に関しては、以下の方法で破損したリポジトリから回収することができます:

$ cd ..
$ hg clone -r 7632 damage fixed
requesting all changes
adding changesets
adding manifests
adding file changes
added 7633 changesets with 14944 changes to 1126 files
updating to branch default
964 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd fixed
$ hg verify
checking changesets
checking manifests
crosschecking files in changesets and manifests
checking files
1126 files, 7633 changesets, 14944 total revisions

整合性が保たれている部位からの再構築

'pull -r' を用いて、整合性が保たれている履歴の部位ごとに、ファイルの履歴が失われたリポジトリへと、少しずつ取り込むことも可能です。あらかじめ複製しておいたリポジトリがあれば、失われたファイルの履歴を含む、完全な履歴が複製できるかもしれません。

失われた履歴ファイル(revlog)の再構築

単一の履歴を持つ履歴ファイル(revlog)が時々破損することがあります。該当するリビジョン時点の内容と 全く同じ ファイルが、まだ手元にある場合、他の場合に比べれば、履歴ファイルの手動修復は容易です。例えば、"broken" という名のリポジトリで、作業領域直下の "f2.i" ファイルが失われたケースの例を見ていきましょう:

$ hg verify
checking changesets
checking manifests
crosschecking files in changesets and manifests
checking files
 data/f2.i@1: missing revlog!
 1: empty or missing f2
 f2@1: 5266937d3e5f in manifests not found
3 files, 3 changesets, 2 total revisions
3 integrity errors encountered!
(first damaged changeset appears to be 1)

"@1" という表示は、失われた履歴ファイルがリビジョン 1 から参照されていた、ということを意味します。このファイルを修復するには、コミット実施を再現するための、新たなリポジトリが必要です。(新規リポジトリと既存リポジトリの repository format が同一となるように、リポジトリ複製の際には、適宜オプション指定等を行ってください)

$ cd ..
$ hg clone -r 0 broken fix # damaged revision minus 1
$ hg up 0 # parent of damaged changeset 1
$ cp ../broken/f2 f2
$ hg add f2
$ hg ci -m fix f2

以上の操作によって、リビジョン 1 から参照される履歴情報が格納された "f2.i" が新規作成されました。後は、このファイルを "broken" リポジトリへとコピーします:

$ cp .hg/store/data/f2.i ../broken/.hg/store/data/f2.i
$ cd ../broken
$ hg verify
checking changesets
checking manifests
crosschecking files in changesets and manifests
checking files
3 files, 3 changesets, 3 total revisions

<!> verify 実施時に、追加されたファイルが参照されるようにするためには、 .hg/store/fncache を編集する必要があるかもしれません。

この手順は、単一ファイルの履歴修復以外にも、履歴情報そのものの破損の修復にも応用可能です。

パッチキュー由来の履歴ファイルによるリビジョン参照の修復

修復されたリビジョンが、適用中のパッチキューのパッチの一部だった場合、修復作業を行ったリポジトリから複製した履歴ファイル(*.i ファイル、『インデックスファイル』とも呼ぶ)が、不適切なリビジョンを参照している可能性があるため、もう少々複雑な手順を踏む必要があります:

$ hg verify
checking changesets
checking manifests
crosschecking files in changesets and manifests
checking files
 data/f2.i@3: missing revlog! 
 f2@?: rev 0 points to unexpected changeset 2              
 (expected 3)
3 files, 3 changesets, 2 total revisions
2 integrity errors encountered!
(first damaged changeset appears to be 3)

この問題は、履歴ファイルにおけるリビジョン参照を、以下の Python スクリプトを使って、直接書き換えることで、解決することができます:

import struct
from mercurial import revlog

f = open('.hg/store/data/f2.i', 'r+')
raw = f.read()
unpacked_index = list(struct.unpack(revlog.indexformatng, raw[:64]))
unpacked_index[4] = 3  # replace with your own "expected changeset number" 
new_raw = struct.pack(revlog.indexformatng, *unpacked_index)
f.seek(0)
f.write(new_raw)
f.close()

備考: インデックスファイルの形式は以下に文書化されています: http://selenic.com/repo/hg-stable/file/tip/mercurial/revlog.py#l156

convert エクステンションによる修復

他の手段として、convert エクステンションによるリポジトリの強制的な再構築、という方法もあります。あらかじめ注意しておきますが、この方法でリポジトリを再構築した場合、全てのリビジョンのハッシュ値が変わってしまうため、全ユーザが修復後リポジトリから再度 clone を実施 する必要があります。

はじめに、convert エクステンションを有効にします。ここでは、リポジトリが REPO というディレクトリにあるものとします:

$ vim REPO/.hg/hgrc
...
[extensions]
hgext.convert=
...

ここですぐに変換しても良いのですが、安全のために、変換先の空のディレクトリを作成しておくのが良いでしょう:

$ mkdir REPOFIX

convert エクステンションによる修復を行います:

$ hg convert --config convert.hg.ignoreerrors=True REPO REPOFIX
scanning source...
sorting...
converting...
[多数のメッセージが表示されますが、多くはコミットメッセージです]
ignoring: data/.DS_Store.i@26a47e9188c: no match found
[更に多くのコミットメッセージ]
.hgtags@78ff9079978f, line 1: tag '1.0b1' refers to unknown node
updating tags

変換コマンド実行時により大量の出力がありますが、重要なのは、破損したファイルが無視されつつ、残りのファイルは新しいリポジトリに格納される、ということです。

{i} これでも処理が中断される場合、.hg/store/data/{破損したファイル}.i ファイルを削除することで、上手く行く可能性があります。このような操作をする際には、あらかじめバックアップを取っておきましょう。

高度な手法: リポジトリデータ構造の監査

Mercurial の内部データ構造は、理解が容易です。詳細は DesignFileFormats に書かれています。Mercurial には debugindex, debugdata, debugstateといった、内部情報を直接表示するようなコマンドもあります (DebuggingFeatures 参照)。

参照


CategoryJapanese

JapaneseRepositoryCorruption (last edited 2012-11-01 15:13:13 by KatsunoriFujiwara)