TL;DR: .gitignoreが効かないとき、そのファイルはほぼ確実に追跡済みです。gitは見たことの無いファイルしか無視しない——インデックスに入ったファイルは、どんなルールの効き目も受けません。 真相はgit check-ignore -v <path>で確かめます。出力が沈黙なら、そのパスは追跡済みで、どのルールも適用されていません。git rm --cached <path>で直してコミットすれば、無視ルールはそのコミット以降から効き始めます。残りのケースは、ルールの順序、否定(negation)の罠、VS Code由来の誤解で説明がつきます。
なぜ.gitignoreが効かないのか?
大多数のケースを1つの仕組みが説明します。ルールができる前にファイルがコミットされていた、というものです。.gitignoreはgitからファイルを隠すフィルタではありません。「未追跡の状態から何をgit addが拾うべきか」というルールです。ファイルがいったんインデックスに入れば、gitは明示的に追跡解除するまで内容の変更を追い続けます。後から.gitignoreを編集しても、追跡済みファイルには何も変わりません。「.envをコミット、青ざめる、.envを.gitignoreに追加、再コミット」という古典的手順がpushのたびに秘密を送り続けるのは、このためです。
これが全員を刺すのは、gitがほぼ万能だからです。Stack Overflow 2022調査では、プロの開発者の93%超がgitを使っています(survey.stackoverflow.co)。その全員が、遅かれ早かれ先週コミットしたファイルへのルールを書くはめになるのです。
この記事の残りは少数派のケースを扱います。ルール順序のミス、否定の罠、フォルダのエッジケース、そしてIDEの疑問。ただし先に次のセクションの健全性チェックを実行してください。10回のうち9回以上、そこで調査は終了します。
どのgitignoreルールがファイルにマッチしているか調べるには?
git check-ignoreが診断ツールで、その沈黙こそが診断結果です:
# ルールが適用される場合、マッチしたルール+ファイル+行番号を表示
git check-ignore -v debug.log
# .gitignore:3:*.log debug.log
# ルールが適用されない場合は何も表示されない——ファイルが追跡済み(またはルールが存在しない)
git check-ignore -v src/.env
# (沈黙=何を書いても、.gitignoreはこのパスを無視していない)
# 終了コード:0=無視されている、1=無視されていない——スクリプト化可能
git check-ignore -q debug.log && echo "ignored" || echo "tracked or unruly" -v出力の読み方。無視のソース(.gitignore、.git/info/exclude、またはグローバル無視ファイル)、続いて行番号:パターン、最後にパス。後のルールに驚かされたら、優先順位を思い出してください。最後にマッチしたルールが勝ちます。*.logの後の!important.logは、あの1ファイルだけを再包含します。
コミット済みファイルにgitignoreが効かないのはなぜ?
ファイルを追跡解除し、ディスクには残し、削除をコミットします:
# 1ファイルを追跡解除(ローカルのコピーは残る——--cachedはインデックスだけを操作する)
git rm --cached .env
git commit -m "stop tracking .env"
# 無視ルールが効くようになったか検証する
git check-ignore -v .env このコミット以降、パスの所有者は.gitignoreです。そこへの編集はgit statusに現れなくなり、git add .も二度と拾いません。ただしファイルは履歴には残ったままです。秘密だったなら、最新コミットからの削除では足りません。認証情報のローテーションだけが本当の修正で、履歴の書き換えは化粧です(そしてundoing the last commitが効くのは、悪いコミットがまだ先端にいる間だけです)。
過去にコミットされたゴミだらけのリポジトリ——ビルド出力、エディタの落とし物、早期に潜入したnode_modules——には、一括追跡解除の2行です:
git rm -r --cached .
git add .
git commit -m "apply .gitignore to tracked files" これは現在のルールに照らしてインデックスを組み直します。無視対象のパスは落ち、それ以外はそのまま再追加されます。diffは派手に見えます(何千もの削除)が、ディスクからは何も消しません。追跡解除ではなくファイル自体を消したいなら、それはgit clean -fdxの領分です。git remove untracked files, safelyを参照。
gitは見たことの無いファイルしか無視しません。インデックスにいるファイルは
.gitignoreに対して免疫を持っています——どんなルールを書いても、見たことを忘れさせることはできないのです。
フォルダに対してgitignoreが効かないのはなぜ?
フォルダ固有の罠が3つ:
1. 末尾スラッシュはマッチではなく意図に関わる。 buildもbuild/もディレクトリにマッチしますが、build/は「ディレクトリだけを意味している」と明文化します。buildという名前のファイルはこれをすり抜けます。ただし対称性は再包含で崩れます(次の罠)。
2. 否定は除外済みディレクトリ内のファイルを救えない。 gitのドキュメントは明解です。「ファイルの親ディレクトリが除外されている場合、そのファイルを再包含することは不可能」(git-scm.com/docs/gitignore)。これは性能上の決定です。gitは除外済みディレクトリを、中身を歩かず丸ごとスキップします。したがってこれは動きません:
build/
!build/keep.me # 死んだルール——gitはbuild/の中を見ない 直し方は、ディレクトリではなく中身を除外すること:
build/*
!build/keep.me # これなら効く——build/自体は調べられるので 3. ネストした.gitignoreは自分の管轄で勝つ。 subdir/.gitignore内のルールは、subdir配下のパスについてはルートのファイルに優先します。git check-ignore -vが予想外の無視ソースを名指してきたとき、たいていこれが理由です。
VS Codeでgitignoreが効かないのはなぜ?
ほぼ例外なく、VS Codeは無実です。エディタのソース管理ビューはgitと同じインデックスを読んでいるので、症状は同一。ファイルはすでにコミット済みで、IDEを何回再起動してもインデックスは変わりません。知っておく価値のある、VS Code周りの現実が2つ:
- エクスプローラーでグレー=無視対象、オレンジ/黄色=変更ありの追跡済み。 無視したあとに変更ありとして表示されるファイルは、追跡済みの確定情報です。上記の
git rm --cachedの修正を実行しましょう。 - エクスプローラーの変更リストに現れないのは、ルールが効いている証拠です。未追跡ファイルとしてそもそも姿を現しません。「VS Codeがgitignoreを無視する」という報告は、CLIの
git statusと古くなったSCMビューの食い違いであることがよくあります。gitを責める前にウィンドウを再読み込みしましょう(Cmd/Ctrl+Shift+P→「Reload Window」)。
.gitignore vs .git/info/exclude vs グローバル:どれをいつ使うか
| ファイル | スコープ | コミットされる? | 用途 |
|---|---|---|---|
.gitignore(リポジトリ) | クローンした全員 | Yes | ビルド出力、依存関係、.env——共有ルール |
.git/info/exclude | 自分のクローンのみ | No | 個人のゴミ:.scratch/、エディタの落とし物 |
core.excludesFile(グローバル) | 自分の全リポジトリ | No | OSのジャンク:.DS_Store、Thumbs.db、*.swp |
.gitignore+否定 | リポジトリ | Yes | 追跡済み設定の例外の再包含 |
グローバルファイルは、ほとんどの開発者が設定しないのに、設定すべきものです:
git config --global core.excludesFile ~/.gitignore_global
printf '.DS_Store\nThumbs.db\n*.swp\n' >> ~/.gitignore_global 盗んでいい家訓があります。ルールがチーム全員の得になるならリポジトリへ。自分だけの得ならexcludeかグローバルファイルへ。 個人的な無視ルールをコミットするからこそ、.gitignoreは300行に達し、どちらの半分がまだ生きているのか誰も分からなくなるのです。
追跡済みファイルの変更を無視するには?
追跡済みのファイル(設定テンプレートやIDEの設定ファイル)を残したい一方で、ローカルの編集がgit statusに出るのを止めたいことがあります。git update-indexの2つのフラグ。どちらもチームのワークフローに入るものではありません:
git update-index --skip-worktree config/local.dev # ローカルの編集が静かになる
git update-index --no-skip-worktree config/local.dev # ……そして元に戻す 擁護できるのは--skip-worktreeの方です。「ローカル版は意図的に分岐している」という宣言になります。親戚の--assume-unchangedは性能上の約束(「このファイルは変わらない」)であって、無視の仕組みではなく、gitが黙って破ることがあります。どちらのフラグも、上流が同じファイルを変更していた場合はpull時に大音量で失敗します。長持ちする答えは、作成時点からexclude経由でローカル専用設定にするか、gitが追跡して自分がコピーするテンプレートファイル(config.example)の形です。
30秒のgitignore健全性チェック
git check-ignore -v <path> # どのルール?(沈黙=追跡済み、ルールなし)
git ls-files --error-unmatch <path> # そもそも追跡されているか?
git rm --cached <path> # 追跡解除、ディスクには残す
git commit -m "stop tracking <path>"
git check-ignore -v <path> # ルールが表示されるようになる 診断、追跡解除、検証。「gitignore not working」報告の裏側は、いつも同じファイルが2つの帽子を被っている構図です。インデックスの片側では追跡され、反対側では無視される。--cachedフラグ1つで、2つ目の帽子が脱げます。
FAQ
なぜ.gitignoreが効かないのか?
ほとんどの場合、ファイルがすでに追跡済みです。gitは未追跡ファイルしか無視しません。インデックスに入ったファイルは、git rm --cachedで追跡解除してコミットするまで.gitignoreの効き目を受けません。
どのgitignoreルールがファイルにマッチしているか確認するには?
git check-ignore -v <path>を実行します。該当する.gitignoreの行とルール番号を表示します。ファイルが追跡済みで適用ルールが無い場合は、何も表示せずに終了します。
git rm --cachedでローカルファイルは削除されるのか?
いいえ。--cachedはファイルをインデックスから除去するだけで、ディスク上のコピーは残ります。削除をコミットするとファイルは未追跡になり、以降は.gitignoreが適用されます。
無視したフォルダ内のファイルをgitignoreで再包含できないのはなぜ?
gitは性能のため除外ディレクトリを丸ごとスキップします。gitドキュメントによれば、ファイルの親ディレクトリが除外されている場合、そのファイルを再包含することは不可能です。
— mrsaynothing
Get the next one by email
One email per post. No spam, no algorithms.
what is this?OllamaがGPUを使わない?Linux・Windows・WSLでの直し方
記事を楽しんでいただけましたか?私の本業はこのような構築です。 採用のご相談