Mercurial 勉強中 (3) - .hgignore で無視リスト

要旨

SCM にはバージョン管理の対象外としたいファイルを指定するのに「無視リスト」という機能があります。

  • Subversion では svn:ignore プロパティにより無視リストを指定する
    • このため,cpmv 時に無視リストも引き継がれる
    • また,あくまで svn:ignore プロパティの設定されたディレクトリ直下のパスが対象となる
  • Mercurial では Working directory 直下の .hgignore ファイルにより無視リストを指定する
  • 指定方法は glob(シェルのワイルドカード風)と regexp正規表現)の 2 通りがある
    • 混在可
    • 用途により使い分けるとよい
      • *.tmp 等,ある拡張子のファイルをすべて無視するのなら glob
      • ダイレクトな指定や複雑な指定なら regexp

Subversion における無視リスト

Subversion の場合,ディレクトリに付与された [http://subversion.bluegate.org/doc/ch07s02.html#svn.advanced.props.special.ignore:title=svn:ignore] プロパティによって無視リストを指定します。

実例を。まずいくつかのファイルをおいてみます。

[svn]% mkdir -p a/b

[svn]% svn add a
A         a
A         a/b

[svn]% touch a/1.txt a/2.bin a/b/3.txt

[svn]% svn status
?      a/1.txt
?      a/2.bin
A      a
?      a/b/3.txt
A      a/b

次に,「a というディレクトリ内部の *.txt というファイル」をバージョン管理対象から外してみます。

[svn]% svn propset svn:ignore '*.txt' a
property 'svn:ignore' set on 'a'

[svn]% svn status --no-ignore
?      a/2.bin
I      a/1.txt
A      a
?      a/b/3.txt
A      a/b

a ディレクトリの下の 1.txt は無視されていますが,サブディレクトリの a/b 内部の 3.txt というファイルは無視されていません。つまり,Subversion の無視リストは下位ディレクトリに波及しません。

ちなみに,上記のように「--no-ignore」というオプションをつけると,無視リストに入ったファイルも含めてステータスを確認することができます。


Subversion の場合,無視リストはあくまでディレクトリに付与されたプロパティなので,cpmv した場合に無視属性が引き継がれます。

[svn]% rm a/2.bin a/b/3.txt

[svn]% svn ci -m "Initial import"
Adding         a
Adding         a/b

Committed revision 1.

[svn]% svn cp a x
A         x

[svn]% svn propget svn:ignore x
*.txt

[svn]% touch x/5.txt

[svn]% svn status --no-ignore
I      a/1.txt
I      x/5.txt
I      x/1.txt
A  +   x

x ディレクトリには明示的に svn:ignore プロパティを指定していないのですが,svn cp した時にプロパティもコピーされたため,x ディレクトリ直下の *.txt も無視されるようになっています。

Mercurial における無視リスト

Mercurial の場合の実例を。

[hg]% mkdir -p a/b

[hg]% touch a/1.txt a/2.bin a/b/3.txt

[hg]% hg status
? a/1.txt
? a/2.bin
? a/b/3.txt

a や a/b というディレクトリ単体がバージョン管理対象に含まれていないのは Mercurial の特徴です(⇒Mercurial は空ディレクトリを管理対象に含めることができない - daily dayflower)。

Mercurial で無視リストを指定するには,Working directory 直下の .hgignore というファイルに無視したいファイルのパターンを記述していきます。

ここで,「*.txt」というファイルを管理対象から外してみます(下記 ^D という表現は,CTRL+D を入力して EOD をしていることを示します)。

[hg]% cat > .hgignore
syntax: glob
*.txt
^D

[hg]% hg status
? .hgignore
? a/2.bin

syntax: glob というのはシェルのワイルドカードと同じような形式で指定する,という意味です(詳しくは hgignore を参照)。全下位ディレクトリにいたるまで,指定されたパターンにマッチするファイルが管理対象から外されていることがわかります。ここ,Subversion と違うところですね。

ところで svn status --no-ignore と同じように無視対象のファイルも表示するためには,hg status -A というオプションを指定します。

[hg]% hg status -A
? .hgignore
? a/2.bin
I a/1.txt
I a/b/3.txt

表示形式は Subversion と同じかんじです。

regexp で無視リストを設定する

では,たとえば a というディレクトリの直下の *.txt だけを無視したい場合にはどうするか。このときは,syntax: regexp で対象ファイルを正規表現で指定することになります。

[hg]% cat > .hgignore
syntax: regexp
^a/[^/]*\.txt$
^D

[hg]% hg status
? .hgignore
? a/2.bin
? a/b/3.txt

複雑な正規表現になってしまっていますが,無事 a/b/3.txt が無視対象からはずれました。

glob syntax でもパスコンポーネントを指定できる

syntax: glob の場合,一般的にはある拡張子のファイルだけ無視する,などの使い方が向いています。ですがディレクトリを含んだ指定をすることもできます。

[hg]% cat > .hgignore
syntax: glob
foo/bar

[hg]% hg status -A
? .hgignore
I foo/bar/baz.txt
? foo/baz.txt
? hoge/bar

この指定例では「fuga/foo/bar/moge.txt」なども無視対象になってしまうので,実用上は混乱を招くと思います。

無視対象のファイルを add する

無視リスト,というのは,あくまで status / add / push 等時にデフォルトで対象とはしませんよ,という意味しかありません。

ですので無視リストの対象となっているファイルを hg addすることもできます。

[hg]% hg status -A
? .hgignore
? foo/baz.txt
I foo/bar/baz.txt
I hoge/bar

[hg]% hg add foo/bar/baz.txt

[hg]% hg status -A
? .hgignore
? foo/baz.txt
A foo/bar/baz.txt
I hoge/bar

ちなみに Subversion でも同じように無視対象のファイルを svn add できます。

Mercurial の無視リストの注意点

0.9.3 以前ですと,syntax: regexp正規表現が slash で終わる場合,その trailing slash が無視されるため意図しない挙動となるようです。詳しくは 夜でもアッサム: mercurialでディレクトリをhgignoreに追加するときの注意 をご参照ください。

まとめ

Subversion の場合,プロパティという概念がある*1ので無視リストの指定もシステマティックで洗練された仕組みになっています。

それに対し,Mercurial は単一のファイルで指定するというシンプルな仕組みです。コードメンテナが自己責任で全体管理してねというスタンスといえるかもしれません。少人数で扱う小規模なレポジトリであれば,管理の手間が少ない分,楽かもしれません。

*1:バージョン管理システムというより,もはやモダンな OS でのファイルシステムのようですね。実際 DAV と絡めてファイルシステムとして利用可能ですし。