この記事の要点
.gitignore でサブディレクトリ /subdir/ 以下のファイルを追跡対象外にしたいとき、(ア) サブディレクトリ自体を追跡対象外にする /subdir/ こともできるし、(イ) サブディレクトリの中身を追跡対象外にする /subdir/* こともできる。使い分けとしては、
- パターンにマッチするサブディレクトリ群を一旦すべて追跡対象外にしておき、そこから特定のサブディレクトリを追跡対象に復活させるケースでは (ア) がよい (※)。
- サブディレクトリ単位での復活がないケースでは、
- サブディレクトリの中身の一部を追跡対象に復活させるなら (イ) にする必要がある。
- そうでないなら (ア) がよい (※)。
※ (イ) でも機能するが、(ア) の方が Git が無用にファイルを見なくて済む。
.gitignore (ケース1. 特定のサブディレクトリを復活させる)
/*/ # 一旦すべてのサブディレクトリを追跡対象から除外
!/aaa/ # /aaa/ だけは追跡対象に復活
.gitignore (ケース2. サブディレクトリ以下の特定のファイルを復活させる)
/bbb/* # /bbb/ の中身は追跡対象から除外
!/bbb/important.txt # /bbb/important.txt だけは追跡対象に復活
.gitignore (ケース3. 単にそのサブディレクトリを除外する)
/ccc/ # /ccc/ は追跡対象から除外
誤った例
ケース 1 で 1 行目の末尾にのみ * を付けるのは誤り (付けるなら 2 行目にも付ける)。
.gitignore (ケース1. 特定のサブディレクトリを復活させる) ※ 誤り
/*/* # 一旦すべてのサブディレクトリ「の中身」を追跡対象から除外
!/aaa/ # /aaa/ を追跡対象に復活 -> 意図通りにならない
# そもそも /aaa/ 自体は除外されていないので復活させなくてよい
# /aaa/ 自体ではなくその中身を !/aaa/* で復活させる必要がある
ケース 2 で 1 行目の末尾から * を除去するのは誤り (除去してはならない)。
.gitignore (ケース2. サブディレクトリ以下の特定のファイルを復活させる) ※ 誤り
/bbb/ # /bbb/ 自体を追跡対象から除外
!/bbb/important.txt # /bbb/important.txt だけは追跡対象に復活 -> 意図通りにならない
# /bbb/ 自体が対象外にされているためその中身を復活させることはできない
# まず /bbb/ 自体を復活させ、必要なら改めて中身を無視する必要がある
備考
-
/subdir/は「/subdir/の中身はそもそも見なくてよい」、/subdir/*は「見たファイルが/subdir/の中身であれば追跡しなくてよい」という指示と考えるとわかりやすい (指示として結構違う)。どちらかでも指示されていれば/subdir/の中身は追跡されない。逆に、パターンマッチによってどちらの指示もなされているファイルを追跡対象に復活させるなら、どちらの指示も否定!しなければならない。 -
.gitignoreの挙動を検証するには、適当なディレクトリに模式的なファイル群を作成しgit initして、git status --short -uallで追跡されるファイルを見ればよい。
この記事を書いた背景として、最近「直下のサブディレクトリは基本的に管理しない」リポジトリを運用することが多いため (以下)、.gitignore の挙動を検証して整理した。
- 例1. Claude Code 作業ベース - 直下に各プロジェクトのリポジトリを clone するためサブディレクトリは基本的に追跡対象外とするが、Claude Code 設定ファイルや Claude Code と共有する知識を格納するサブディレクトリは管理する (以下)。
- 例2. 実験ノート - 実験成果物は巨大になりうるためサブディレクトリは基本的に追跡対象外とするが、実験設定ファイルや実験結果の解析スクリプトは管理する。
.gitignore (Claude Code 作業ベース)
# 直下の追跡対象外ファイル
/himitsu.txt
# 一旦すべてのサブディレクトリを追跡対象から除外
/*/
# .claude は追跡対象に復活
!/.claude/
# docs も追跡対象に復活
!/docs/
/docs/sugoi_himitsu.txt # やはり追跡対象から除外
# どこにあっても除外するディレクトリ・ファイル
.obsidian/
*.aux
*.log