Git
gitignore

[Git] .gitignoreの仕様詳解

対応バージョン

この記事の内容は、少なくともGitのバージョン2.19.1までは対応している。
もし最新のGitで新しい動きがあれば随時更新する。

基本

.gitignoreを使うと無視する(Gitのトラッキングの対象外とする)ファイル or ディレクトリを指定できる。

.gitignoreは複数のディレクトリに置くことができる。
深い階層の.gitignoreに書かれた指定の方が優先順位が高い。(後に解釈される)

.gitignore内の記述は上の行から順に以下のように解釈される。

/を含まない行(fileなど)
.gitignore以下の全サブディレクトリ下にあるこの名前のファイル or ディレクトリを無視する
末尾以外にのみ/を含む行(/file, /path/to/file, path/to/fileなど)
.gitignoreが置いてあるディレクトリをカレントディレクトリとする相対パスで指定されるファイル or ディレクトリを無視する
末尾だけ/な行(directory/など)
.gitignore以下の全サブディレクトリ下にあるこの名前のディレクトリを無視する
末尾以外にも末尾にも/を含む行(/directory/, /path/to/directory/, path/to/directory/など)
.gitignoreが置いてあるディレクトリをカレントディレクトリとする相対パスで指定されるディレクトリを無視する
!で始まる行(!/path/to/fileなど)
!以降のパターン文字列が示すファイル or ディレクトリを無視しない
前の無視指定を上書きする
以降の無視指定に上書きされうる
空行 or #で始まる行
解釈されない

上にも書いてあるが、以下のようなディレクトリ構成の場合、.gitignoreにfileとだけ書くと、filedirectory/fileも共に無視されることに特に注意してほしい。

├.gitignore
├file
└directory
  └file

.gitignoreと同ディレクトリにあるfileのみを無視したい場合は、/fileと書く必要がある。

ちなみに、Gitリポジトリのルート、あるいはOSのルートからの絶対パス指定をする方法はない。
.gitignoreはそこから下のディレクトリにしか影響を及ぼせない。

ワイルドカード

.outという拡張子のファイルをまとめて無視したい場合*.outのように記述できる。
このようなワイルドカードの類には以下のようなものがある。

*
/以外の0文字以上の文字列にマッチ
シェルのパス名展開で使われる*と同じ意味
?
/以外の1文字にマッチ
シェルのパス名展開で使われる?と同じ意味
[0-9]など
/以外の指定した1文字にマッチ
シェルのパス名展開で使われる[0-9]などと同じ意味
**
0個以上のファイル or ディレクトリにマッチ
例えば/a/**/a, /a/x, /a/x/yなどにマッチし、/**/b/b, /x/b, /x/y/bなどにマッチし、/a/**/b/a/b, /a/x/b, /a/x/y/bなどにマッチする
Git 1.8.2以降で使用可能

エスケープ

ほとんど使うことは無いだろうが一応説明すると、\(バックスラッシュ)でエスケープできる。

# Emacsの自動保存ファイルを無視する
\#*#

おすすめの書き方

# 特定の拡張子を無視する場合はどこにも/を付けない
*.o

# 特定のファイルを無視する場合は先頭に/を付ける
/npm-debug.log

# 特定のディレクトリを無視する場合は先頭と末尾に/を付ける
/bin/

gibo使っとけという話ではあるのだが、.gitignoreに自分で手を入れないといけなくなったときはこんな感じで書くと良い。

FAQ

初心者はほぼ必ずハマるのであわせて解説したい.gitignore絡みのエトセトラ。

.gitignoreという名前のファイルを作れない

Windowsでは、エクスプローラーやメモ帳を使う場合、.で始まる名前のファイルを作るとエラーになってしまう。
対処法としては、

といったものが挙げられる。
いったんファイルさえ作ってしまえば、あとは普通に編集できる。

.gitignoreにファイルを追加したのに無視されない

あなたは以前にそのファイルをgit addでインデックスに登録していたり、あるいはさらに進んで既にコミットしていたりしないだろうか?
.gitignoreが無視するファイルは、まだインデックスに登録されていないものだけである。

既にインデックスに登録したりコミットしたりしてしまっているファイルを再び無視するようにしたければ、シェルからgit rm --cached path/to/file、またはgit rm path/to/fileのコマンドを打とう。
ディレクトリの場合は、git rm --cached -r path/to/directory、またはgit rm -r path/to/directoryである。

--cachedオプションのあるなしの違いは、ファイルを手元(難しく言うとワーキングツリー)から削除するかどうかである。
--cachedありの場合、そのファイルはGitの管理対象から外れるが、手元からは削除されずに残ったままの状態となる。
コンパイルの生成物を誤ってインデックスに登録してしまったときなどに使うとよいだろう。
--cachedなしの場合、そのファイルはGitの管理対象から外れ、手元からも削除される。
すぐに消すつもりで作ったデバッグ用ファイルを誤ってインデックスに登録してしまったときなどに使うとよいだろう。

あるディレクトリ以下の全ファイルを、特定のファイルだけ除いて無視したい

次のようなディレクトリ構成を考える。

├.gitignore
└tmp
  ├.gitkeep
  ├gomi
  ├gomi2
  └gomi3

/tmp/.gitkeepだけは例外として、tmpディレクトリの中身を全て無視したいと考えたとき、どういう.gitignoreを書けばいいだろうか?
次のように書きたくなるが、これは誤りである。

間違い
/tmp/
!/tmp/.gitkeep

何故なら、Gitはパフォーマンス上の理由から、ディレクトリを無視した場合、そのディレクトリ中の一部のファイルやディレクトリだけを無視しないようにすることはできないという制限を設けているからだ。
よって、こう書く必要がある。

正解
/tmp/*
!/tmp/.gitkeep

「tmpディレクトリ」を無視してからその中の特定のファイルだけ除外しているのではなく、「tmpディレクトリ中の全てのファイルやディレクトリ」を無視してから特定のファイルだけ除外していることに注目してほしい。
これならば制限に引っかからずにやりたいことを実現できる。

より複雑なケースでも考え方は同じだ。
次のディレクトリ構成で、/tmp/deep/directory/.gitkeepだけは例外として、tmpディレクトリの中身を全て無視したい場合を考える。

├.gitignore
└tmp
  ├deep
  │├directory
  ││├.gitkeep
  ││└gomi
  │└gomi2
  └gomi3

この場合、(面倒だが)こう書く。

複雑なケース
/tmp/*
!/tmp/deep/
/tmp/deep/*
!/tmp/deep/directory/
/tmp/deep/directory/*
!/tmp/deep/directory/.gitkeep

前述の制限を巧妙に回避していることを確認してみてほしい。

ちなみに、Gitのバージョン2.7.0だけは、上の複雑なケースを次のようにシンプルに書ける機能を搭載していて、一時期話題になった。

Git-2.7.0限定
/tmp/
!/tmp/deep/directory/.gitkeep

ところがこの機能は一部状況下ではうまく動かないことが明らかになったため、Git 2.7.1以降ではrevertされている。
Git 2.7.0の新機能を中途半端に知っていた人は気を付けよう。

Thumbs.dbや.DS_Storeを.gitignoreに入れるのはよくないって本当?

Thumbs.dbはWindowsが自動で作るファイルであり、プロジェクトと関係ないので無視したいファイルの一つである。
また、.DS_StoreはmacOSが自動で作るファイルであり、これもまたプロジェクトと関係ないので無視したいファイルの一つである。
では、これらのファイルを.gitignoreに入れることは望ましいことだろうか。

この問題を考えるために次のような状況を考えてみる。
あなたのGitリポジトリはWindowsを使用している開発者にcloneされるかもしれないし、はたまたmacOSを使用している開発者にcloneされるかもしれない。
もしかしたら、名前も知らないようなOSを使用している開発者がcloneする可能性も捨てきれないだろう。
さて、これらの「全ての環境」に対して適切な.gitignoreを書くのは望ましいことだろうか?
答えは否であるし、そもそも現実的に不可能である。

.gitignoreはあくまで、プロジェクト固有の無視したいファイル or ディレクトリを記録する場所であるとされている。
開発者個人の環境に依存する、無視したいファイル or ディレクトリは、本来は開発者個人がglobal .gitignoreという場所に書いて管理するというのが望ましい習慣である。

global .gitignoreとは、~/.config/git/ignoreのことである。
ここに書かれた設定はすべてのGitリポジトリにおいて最初に読み込まれるので、開発者個人の共通無視設定を書くのにうってつけである。

余談だが、多くのサイトでは、global .gitignoreの場所をデフォルトである~/.config/git/ignoreから~/.gitignore_globalにわざわざ上書きする方法を解説している。
このような解説が広まっている原因は、Git 1.7.11以前はglobal .gitignoreの場所のデフォルト値が存在しなかったため、明示的にglobal .gitignoreの場所を設定する必要があったからである。
global .gitignoreの場所はなんでもいいけれど慣習があった方がいいので、とりあえず~/.gitignore_globalをglobal .gitignoreの場所にしましょう、ということにした古の慣習が、存在意義をなくした今も残っているのである。

もしあなたがGit 1.7.12以上を使用しているのであれば、global .gitignoreの場所を~/.gitignore_globalに変更する必要は特にない。
1.7.11以前を使用している場合でも、~/.gitignore_globalを使うことには、古の慣習に従うという以上の意味は特にない。

参考リンク

git/gitignore.txt at v2.19.1 · git/git