.hgignore における『無視対象に対する例外条件』指定の記述
Twitter 上で去年見かけた以下のエントリに関して:
正規表現で合致(=無視対象)パターンを事細かに書くのが面倒なのは、非常に共感できる話です。
年末時点の個人的見解としては、無視判定周りの実装が:
といった感じであることを想定していたので、例えば "!" で始まるパターンを例外条件扱いにして:
- .hgignore 等からパターンの読み込み
- 各パターン毎に正規表現のコンパイル
- コンパイル済みパターンでループを回す
- 合致が見つかったなら判定処理終了
- "!" で始まるパターンなら『無視対象ではない』とみなす
- それ以外なら『無視対象』とみなす
みたいな対応をするのが妥当かなぁ?などと考えていました。
しかし、改めて実装を確認してみると:
- .hgignore 等からパターンの読み込み
- 全てのパターンを OR 結合して単一正規表現パターン化
- 単一正規表現パターンのコンパイル
- 単一のコンパイル済みパターンで合致が検出されたなら、無視対象とみなす
という具合。
まぁ、指定パターンのループを Python 側で回すよりは、正規表現処理の内部で OR 判定した方が最適化が効き易い、という理由からであろうことは理解できます。
しかし、全ての指定パターンが OR 結合されることで、正規表現の合致判定が一括で実施されるため、特定のパターンへの合致をもって判定処理を打ち切る、などいう実装はできそうにありません。
もっとも、『冒頭 "!" で始まるパターンは例外条件扱い』 的な、新規文法を導入するとなると、旧バージョンの hg コマンドや、リポジトリ資産に対する互換性を考えて、.hgaccept
とか .hgrecognize
みたいな、別ファイルの導入を提案する必要があるので、これまたパッチを受け取ってもらえなさそうな予感……
というわけで、既存機能の範囲で『無視対象に対する例外条件』を指定するには、正規表現で頑張るしかないという結論になります。
合致パターンを正規表現で事細かに書くのは面倒だけど、付加的な例外条件の記述であれば、"(?!...)
" 表記を使うことで、そこそこ妥当な複雑さの範囲で記述できそうな気がします
syntax: re (?!^doc/).*\.txt$
上記の記述では、無視対象の判定条件は以下のようになります
- "
.*\.txt$
" に合致し、且つ - "
^doc/
" に合致しない
".*\.txt$
" に合致するパスに対する判定例を以下に示します。
パス | 無視 |
---|---|
foo.txt | する |
src/doc/foo.txt | する |
doc/foo.txt | しない |
doc/bar/baz/foo.txt | しない |
2つ目のパスは、doc/
が冒頭にないために、例外条件である "^doc/
" に合致しないことから、無視対象となります。
単一のパターンに例外条件を複数指定したい場合は、以下のように "|
" を使った OR 結合で記述すれば良いでしょう。
syntax: re (?!^(src|include)/).*\.[ch]$
資源効率を考えた場合、"(src|include)
" ではなく "(?:src|include)
" の方が良いのかな?
なお、"(?!....)
" 表記は『先読み (look ahead)』扱いとなり、合致したとしても、判定位置は移動しません。例えば:
syntax: re ^a/(?!b00/)b[0-9][0-9]/.*\.txt$
上記の記述では、無視対象の判定条件は以下のようになります。
- "
^a/b[0-9][0-9]/.*\.txt$
と合致し、且つ - "
^a/b00/
" に合致しない
感覚的には以下のように理解しておけば良いのかな?
- "
(?!....)
" 表記部分を除外した正規表現に合致し、且つ - "
(?!....)
" 表記の条件に合致しない
あくまで正規表現ベースなので、glob 系のパターン記述ほどは簡単ではないけれど、とりあえずは許容可能な範囲と言えそうです > "(?!....)
" 表記
なお、.hgignore
の記述は、リポジトリルートからの相対パスに対する部分一致で判定されるため、ディレクトリに対する条件記述の場合は、必要に応じて "^
" を付けるのを忘れないようにしてください。詳細は以下を参照。
- "
hg help hgignore
" (http://mercurial-users.jp/manual/hgignore.5.html) - "
hg help pattern
" (http://mercurial-users.jp/manual/hg.1.html#patterns)