彷徨えるフジワラ

年がら年中さまよってます

文字大小を無視する .hgignore 記述

Twitter 上で以下のエントリを去年見かけたのですが:

丁度、Windows (NTFS) や Mac OS (HFS+) 等の case-insensitive な環境におけるパターンマッチングでの文字大小有効性を、環境に応じて自動で切り替えられるようにできないかと、色々試行錯誤していた時期だったので:

ということにしていました。

しかし、ファイル名に対するパターンマッチング周りは、履歴や管理対象ファイルが大量にある場合等の性能劣化への配慮や、現状の実装中の最適化配慮との整合性など、思った以上に難物です。

そのため、対応を提案するのは暫く先になりそうだなぁ、などと考えていたのですが、別の修正提案でPython の正規表現文法を確認した際に、"(?i)" 記述を使うことで、正規表現記述の中で『文字大小無視』(re.IGNORECASE 指定相当)を指定できることに気付きました。

先日、.hgignore例外条件指定の記述に関して正規表現文法を確認した際にも、この文法は目にしていた筈なのですが、どういうわけかその時は、.hgignore 記述に利用することを思い付かなかったんですよねぇ。なんでだろう?
それでは実際に試してみましょう。

$ hg status -A
? BAR.TXT
? Baz.Txt
? foo.txt

作業領域に、上記のような管理対象外 (unknown: "?") ファイルが存在する状況で:

syntax: glob
*.txt

上記のような内容の .hgignore を作成すると、"hg status -A" による各ファイルの状態表示は以下のようになります。

$ hg status -A
? BAR.TXT
? Baz.Txt
I foo.txt

指定したパターン *.txt と、文字大小が厳密に一致する foo.txt のみが無視対象 (ignore: "I") ファイルとみなされ、それ以外の .TXT や .Txt を拡張子を持つファイルは、管理対象外ファイルのままです。

ここで .hgignore の記述を以下のように変更すると:

# 文法形式の変更 glob ⇒ regexp に注意!
syntax: regexp
(?i)\.txt$

"hg status -A" による各ファイルの状態表示は以下のようになります。

$ hg status -A
I BAR.TXT
I Baz.Txt
I foo.txt

拡張子 .txt を持つファイルは、拡張子の文字大小に関わりなく、無視対象とみなされるようになりました。

なお、正規表現パターンでの (?i) 使用の際には、注意すべき点があります。

.hgignore例外条件指定の記述に関するエントリでも書きましたが、.hgignore への記述内容は、Mercurial 内部で以下のように処理されます。

  1. .hgignore 等からパターンの読み込み
  2. 全てのパターンを OR 結合して単一正規表現パターン化
  3. 単一正規表現パターンのコンパイル
  4. 単一のコンパイル済みパターンで合致が検出されたなら、無視対象とみなす

2番目の手順における『単一正規表現パターン化』の影響で、(?i) を使っているパターンが1つでもある場合、文法形式 (glob/regexp) の区別なく、他のパターンも全て文字大小を無視するようになります。

まぁ、case-insensitive な環境での作業を想定する必要があるケースであれば、全てのパターンが文字大小を無視するようになっても、それほど大きな問題は無いとは思いますが……

むしろこれを逆手に取って、(?i) を使うダミーのエントリを1つ追加した上で、syntax: glob なエントリはこれまで通り記述する、という手もありますね。

# 全パターンで文字大小を無視させるためのダミー
syntax: regexp
(?i)^\.hg$

# (?i) を使うエントリの影響で文字大小を無視して合致
syntax: glob
*.txt

これなら syntax: glob な既存エントリを、個別に正規表現化しなくても良いですし。