Gitの.gitignoreで、フォルダーの一部だけを管理する方法

​​Git​gitignore,コマンドライン

.gitignore で無視したフォルダーの中の一部のファイルだけを Git で管理するには、フォルダーを logs/ ではなく logs/* と書き、その下の行に !logs/.gitkeep のような「!」付きの行を書きます。logs/ と書くとフォルダーごと無視され、あとから「!」を書いても効きません。ログやキャッシュのフォルダーを空のまま残したいとき(.gitkeep を置く方法)や、データ用フォルダーにある説明ファイル(README など)だけをコミットしたいときに使う書き方です。

補足

検証環境は Ubuntu 24.04、Git 2.43.0 です。作者の名前とメールアドレスには例示用の値(Neko <neko@example.com>)を設定し、コミットの日時は環境変数 GIT_AUTHOR_DATE と GIT_COMMITTER_DATE で決めました。Windows・macOS や、.git/info/exclude、グローバルの除外設定(core.excludesFile)での書き方は試していません。

例のリポジトリ:logs フォルダーの .gitkeep だけを残したい

logs/ の中に、.gitkeep(空のファイル)と、アプリが書き出す app.log、error.log がある状態から始めます。管理したいのは .gitkeep だけです。

console
$ ls -A logs
.gitkeep
app.log
error.log

$ git status --short
?? logs/

git status は、追跡していないフォルダーを logs/ のようにまとめて表示します。-uall を付けると、フォルダーの中のファイルが1つずつ表示されます(git status の公式リファレンスにある -u の説明)。この記事では、どのファイルが無視されたかを見るために -uall を付けます。

console
$ git status --short -uall
?? logs/.gitkeep
?? logs/app.log
?? logs/error.log

logs/ と書くと、「!」を足しても戻らない

まず、logs/ でフォルダーを無視し、次の行に !logs/.gitkeep を書きます。

console
$ cat .gitignore
logs/
!logs/.gitkeep

$ git status --short -uall
?? .gitignore

logs/.gitkeep は一覧に出てきません。どの行で無視されたのかは、git check-ignore -v で確かめられます。出力は「ファイル名:行番号:パターン」と、対象のパスです。

console
$ git check-ignore -v logs/.gitkeep
.gitignore:1:logs/	logs/.gitkeep

1行目の logs/ で無視されたままで、2行目の !logs/.gitkeep は使われていません。git add で追加しようとしても、無視されているのは logs フォルダーだと言われます。

console
$ git add logs/.gitkeep
The following paths are ignored by one of your .gitignore files:
logs
hint: Use -f if you really want to add them.
hint: Turn this message off by running
hint: "git config advice.addIgnoredFile false"

gitignore の公式リファレンスには、親のフォルダーが除外されているファイルは、「!」で戻せないと書かれています。Git は処理を速くするために、除外したフォルダーの中を見ません。そのため、中のファイルについて書いた行は、どこに書いても効きません。

logs/* と書くと、.gitkeep だけが管理の対象に戻る

1行目を logs/* に変えます。logs/* は「logs フォルダーの中にあるもの」に一致するので、logs フォルダー自体は無視されません。Git はフォルダーの中を見て、無視するかどうかをファイルごとに判断します。

console
$ cat .gitignore
logs/*
!logs/.gitkeep

$ git status --short -uall
?? .gitignore
?? logs/.gitkeep

git check-ignore -v は、「!」付きの行に一致して無視されないファイルも、その行と一緒に表示します。.gitkeep は2行目の「!」付きの行に一致し、ログの2つは1行目に一致しています。付けている -n は、どの行にも一致しないファイルも表示するためのオプションです。

console
$ git check-ignore -v -n logs/.gitkeep logs/app.log logs/error.log
.gitignore:2:!logs/.gitkeep	logs/.gitkeep
.gitignore:1:logs/*	logs/app.log
.gitignore:1:logs/*	logs/error.log

git add -n .(実際には追加せず、追加されるファイルを表示する)でも、.gitkeep だけが入ることが分かります。

console
$ git add -n .
add '.gitignore'
add 'logs/.gitkeep'
上は logs/ と書いた場合で、logs フォルダーが丸ごと灰色になり「!logs/.gitkeep は効かない」と書かれている。下は logs/* と書いた場合で、フォルダーの中の .gitkeep だけがピンクで示され、app.log と error.log は灰色になっている
logs/ と logs/* の違い。logs/* ならフォルダーの中のファイルが1つずつ判断され、「!」の行が効く

この状態でコミットすると、logs フォルダーからは .gitkeep だけが入ります。

console
$ git add . && git commit -q -m "logs フォルダーを .gitkeep だけ管理する"

$ git ls-files
.gitignore
app.js
logs/.gitkeep

$ git log --stat -1
commit 7495bd05aae5ac549f112523d698a950e6fe8ab5
Author: Neko <neko@example.com>
Date:   Fri Nov 13 10:00:00 2026 +0900

    logs フォルダーを .gitkeep だけ管理する

 .gitignore    | 2 ++
 logs/.gitkeep | 0
 2 files changed, 2 insertions(+)

コミットのあとに app.log へ書き足しても、git status には何も出ません。

console
$ echo "next" >> logs/app.log && git status --short -uall

「!」の行は、無視する行より下に書く

同じ .gitignore の中では、一致した行のうち最後の行で結果が決まります(gitignore の公式リファレンスにある優先順位の説明)。順番を逆にして、!logs/.gitkeep を先に書くと、.gitkeep もあとの logs/* で無視されます。

console
$ cat .gitignore
!logs/.gitkeep
logs/*

$ git status --short -uall
?? .gitignore

$ git check-ignore -v logs/.gitkeep
.gitignore:2:logs/*	logs/.gitkeep

深い階層のファイルだけを戻すときは、途中のフォルダーも戻す

次は、data/ の中の data/samples/readme.md だけを管理したい場合です。data/ には raw/big.csv と samples/small.csv もあります。data/* と !data/samples/readme.md の2行だけでは、readme.md は戻りません。

console
$ cat .gitignore
logs/*
!logs/.gitkeep
data/*
!data/samples/readme.md

$ git status --short -uall
 M .gitignore

$ git check-ignore -v data/samples/readme.md
.gitignore:3:data/*	data/samples/readme.md

data/* は data の中にある samples フォルダーにも一致します。親の samples フォルダーが除外されるので、logs/ のときと同じく、中の readme.md を「!」で戻せません。

そこで、途中のフォルダーを「!」で戻し、そのフォルダーの中身をもう一度 /* で無視してから、残したいファイルを戻します。

console
$ cat .gitignore
logs/*
!logs/.gitkeep
data/*
!data/samples/
data/samples/*
!data/samples/readme.md

$ git status --short -uall
 M .gitignore
?? data/samples/readme.md

$ git check-ignore -v -n data/raw/big.csv data/samples/small.csv data/samples/readme.md
.gitignore:3:data/*	data/raw/big.csv
.gitignore:5:data/samples/*	data/samples/small.csv
.gitignore:6:!data/samples/readme.md	data/samples/readme.md

3つのファイルが、それぞれ別の行で判断されています。公式リファレンスにも、/*、!/foo、/foo/*、!/foo/bar の4行で foo/bar 以外を除外する、同じ形の例があります。どちらも、途中のフォルダーについて「フォルダーを戻す行」と「その中身を無視する行」を書いています。

.gitignore を変えずに git add -f で追加する方法もある

.gitignore は logs/ のままにして、特定のファイルだけを git add -f で追加することもできます。-f は、無視されるファイルの追加を許すオプションです(git add の公式リファレンス)。

console
$ cat .gitignore
logs/

$ git add logs/manual.txt
The following paths are ignored by one of your .gitignore files:
logs
hint: Use -f if you really want to add them.
hint: Turn this message off by running
hint: "git config advice.addIgnoredFile false"

$ git add -f logs/manual.txt && git status --short
A  logs/manual.txt

これをコミットして追跡を始めると、.gitignore に一致していても、変更が git status に出ます。

console
$ git commit -q -m "logs/manual.txt を追加" && git status --short

$ echo "edited" >> logs/manual.txt && git status --short
 M logs/manual.txt

このとき git check-ignore -v logs/manual.txt は何も表示せず、終了コード 1 で終わります。追跡しているファイルなので、無視の対象として扱われないためです。.gitignore のどの行に一致しているかを見たいときは、インデックス(追跡しているファイルの情報)を見ない --no-index を付けます(git check-ignore の公式リファレンス)。

console
$ git check-ignore -v --no-index logs/manual.txt
.gitignore:1:logs/	logs/manual.txt

git add -f で追加しても、.gitignore は logs/ の1行のままです。どのファイルを例外にしたかを .gitignore に書いて残したいなら、logs/* と「!」の書き方が向いています。

​Posted by 86