パーフェクト dirstate 〜 トラブル対処編
このエントリは、Mercurial Advent Calendar 2013 の24日目です。
本エントリでは、基礎編での .hg/dirstate
に関する説明を踏まえて、いくつかのよくあるトラブルに対して、その原因と対処方法について説明します。
プラットフォームによるモード判定の可否
Mercurial では、いわゆる「実行権限」ビットの有無も、履歴管理の対象になります。
但し、Windows 環境では、以下のように「実行権限」に関する情報は無視されます。
- 作業領域へのファイルの取り出しでは、「実行権限」の有無は無視
- 作業領域中のファイルに対する「実行権限」の変更は、履歴記録の際に無視
Windows の NTFS の場合、「親フォルダの権限設定の引き継ぎ」等の機能があり、UNIX 系環境が期待する権限設定周りの話とは馴染まないのが、上記のような「実行権限」を無視する挙動の理由だと思われます(あくまで私自身の推測ですが)。
なお、作業領域中のファイルに関しては、「実行権限」の有無を無視しますが、.hg/dirstate
への記録/更新は、どの環境でも同じ挙動になります。
そのため、Windows 環境で作業領域が更新されたリポジトリを、以下のような方法で、そのまま UNIX 系環境に持って行った場合:
- Windows 上のリポジトリ(作業領域+管理領域)をアーカイブしたものを、UNIX系環境で展開
- NFS や SMB/CIFS で共有される領域に、Windows 側で作成したリポジトリを、UNIX 系環境から参照
作業領域中のファイルと .hg/dirstate
中の情報の間で、「実行権限」に関する食い違いが生じる可能性があります。
このようなケースでは、以下のような現象が見られる筈です。
hg status
出力で想定外のファイルが「変更あり」扱いされるhg diff
出力が無い
UNIX 環境側であれば、hg diff
に --git
/-g
オプションを指定することで、「実行権限」変更の有無を確認することができます。
$ hg status M a.sh $ hg diff $ hg diff --git diff --git a/a.sh b/a.sh old mode 100755 new mode 100644
想定外の「実行権限」変更が見られる場合は、対象ファイルの実行権限設定を、以下の要領で本来の状態に戻して下さい。
$ hg revert 対象ファイル
もしも hg diff --git
でも差分が表示されない場合は、後述する eol エクステンション絡みの現象かもしれませんので、そちらの説明も参照してみてください。
なお、設定ファイルにおいて [diff] git = True
を指定することで、常時 Git 形式差分を使うようにすれば、hg diff
への --git
オプション指定無しでも、「実行権限」変更の有無が確認できるようになります。
但し、Git 形式差分を選択した場合、バイナリファイル変更の際には、BASE64 形式のバイナリ差分も出力されてしまいますので、各自の利用形態に応じて設定するようにしてください。
Mercurial がデフォルトで Git 形式差分を生成しないのは、Git 形式差分を受理できない、旧来の patch
コマンド等への配慮によるものです。決して、Git へのライバル意識によるものではありません(笑)。
eol による変換
eol エクステンションが有効な場合、hg status
による作業領域中のファイルの状態確認は、以下の流れになります。
- サイズが異なれば、変更あり
- モードが異なれば、変更あり
- 作業領域中の
a.txt
から、タイムスタンプを取得 - タイムスタンプが異なれば、変更の可能性あり
- 履歴情報から、対象ファイルの内容を取り出す
- 作業領域中の対象ファイルを、
.hgeol
での設定に従って、履歴格納時の形式に変換 - 対象ファイルの内容と比較
- 内容が異なれば、変更あり
.hg/dirstate
中のエントリを、最新の情報で更新
eol による介入は、サイズ確認の後になってしまいますので、「.hgeol
設定とファイルの改行形式」の一致/不一致と、hg status
での「変更あり」出力の有無は、感覚的な期待と必ずしも一致するとは限りません。例えば、以下のような食い違いが発生するかもしれません:
- 作業領域中のフィルの改行形式と
.hgeol
の設定が食い違っている:
作業領域中のファイルの「サイズ」/「タイムスタンプ」が、hg update
時に.hg/dirstate
に記録されたものと一致するなら、「変更なし」扱い - 作業領域中のフィルの改行形式と
.hgeol
の設定が一致している:hg update
後に作業領域中のファイルの改行形式を変更した場合、.hg/dirstate
中の「サイズ」情報との食い違いから、「変更あり」扱い
.hgeol
に対する事後変更を元に、作業領域中のファイルの改行形式を変更したい場合は、以下の様な手順を踏んでください。
.hgeol
の設定を変更- 対象ファイルを Mercurial を経由せずに削除
(UNIX系環境ならrm
コマンド、Windows環境ならdel
コマンドやエクスプローラ経由) hg revert 対象ファイル
を実施hg debugrebuildstate
を実施
hg revert
は、作業領域中のファイルの内容/実行権限設定は復旧しますが、.hg/dirstate
の内容には関与しません。
そのため、変更後の .hgeol
設定に従って書き出したファイルのサイズは、.hg/dirstate
に記録されているサイズ(= 以前の .hgeol
設定に従って作業領域に書き出したファイルのサイズ)とは、異なることになります。結果として、状態判定処理では、内容を比較する前の段階で、サイズ違いにより「変更あり」とみなされてしまいます。
そこで hg debugrebuildstate
の出番です。
hg debugrebuildstate
は、.hg/dirstate
に保持されている全てのファイルのサイズ情報を、「未設定」状態を表す -1 に設定します。
サイズ情報として -1 を保持するエントリが検出された場合は、「同一サイズでタイムスタンプ違い」時と同じ手順で、内容の比較〜.hg/dirstate
エントリ情報の更新が実施されます。
「内容一致」と判断された場合のエントリ情報更新では、サイズ情報は作業領域中のファイルのサイズ、つまり改行コード形式変更後のファイルサイズで更新され、以後のファイル状態判定には、変更後の改行コード形式に従ったファイルサイズが用いられるわけです。
なお、hg debugrebuildstate
を実行した場合、hg add
や hg remove
等で指示した、追加登録/登録除外の情報も破棄してしまいますので、未コミットの追加/除外が残っている場合は、十分注意して実行してください。