! を使っても無視されたディレクトリ内のファイルを残せないのはなぜ?

親ディレクトリが除外されたままでは、ファイルへの例外は効きません。例外を追加する前にディレクトリのルールを確認してください。

1. 効かないルールを確認する

リポジトリのルートに未追跡の build/keep.txt と build/discard.txt があるとします。次のルールは build 自体を除外するため、Git はその中の keep.txt を探しません。後の例外ではこのファイルを復帰できません。

/build/
!/build/keep.txt

2. 中身を除外してから1ファイルを戻す

ルートの .gitignore にある前の2行を次のルールに置き換えます。build を探索できる状態にし、直下の中身を除外してから keep.txt を再び対象にします。この例は build 直下の1ファイル用です。さらに深いファイルでは、すべての親ディレクトリを探索可能にする必要があります。

同じ優先順位では最後に一致したルールが適用されるため、例外は除外ルールの後に置きます。他の一致ルールや下位の .gitignore も確認してください。

/build/*
!/build/keep.txt

3. 両方のファイルを確認する

例の2ファイルを作成した後、リポジトリのルートで次のコマンドを実行します。詳細出力では keep.txt が !/build/keep.txt に、discard.txt が /build/* に一致するはずです。! の一致は再び対象になることを意味し、無視されているという意味ではありません。

git check-ignore -v -- build/keep.txt build/discard.txt

状態表示には build/keep.txt が未追跡として現れ、build/discard.txt は現れないはずです。追跡済みの場合は、その対処の記事を参照してください。除外ルールで既存のインデックス項目は変わりません。

git status --short --untracked-files=all -- build/

Git がすでに追跡しているファイルを無視するには?

正確な動作や詳細は、公式の資料を参照してください。 Git のドキュメント · git check-ignore の公式ドキュメント