はじめに
以前隠しファイルである.envファイル
を、git add .
で、誤ってgithubに上げてしまったことがあり、その反省から.gitignore
の使い方をマスターしようと思って、ドキュメントを見た学びを残します。
ざっくり、gitignore の目的・使い方から、設定時の注意点、具体的な記述例あたりをまとめています。
参考にしたドキュメントはこちらです。
gitignoreは、Gitでバージョン管理する際に、意図的に無視したいファイルやディレクトリを指定するための設定ファイルです。適切にgitignoreを設定することで、リポジトリを整理し、不必要なファイルのコミットを防ぎ、共同開発をスムーズに進めることができます。
なぜgitignoreは必要?
gitignoreの最大の目的は、意図しないファイルのコミットを防ぐことです。
開発プロジェクトでは、ソースコード以外にも様々なファイルが生成されます。例えば、
- 一時ファイル: エディタやIDEが自動生成するバックアップファイル、一時ファイル
- ビルド生成物: コンパイル後のオブジェクトファイル、実行ファイル、ライブラリ
- ログファイル: アプリケーションのログファイル
-
設定ファイル: 環境設定ファイル(APIキーを設定した
.env
など)、個人設定ファイル -
OS依存ファイル:
.DS_Store
(macOS),Thumbs.db
(Windows)
これらのファイルは、バージョン管理する必要がない、もしくは含めたくない場合がほとんどです。gitignoreを設定することで、これらのファイルをGitの管理対象から除外し、リポジトリをクリーンに保ち、必要なファイルだけを管理できるようになります。
gitignoreを設定するメリットは、以下が挙げられます。
- リポジトリの肥大化防止: 不要なファイルをコミットしないことで、リポジトリサイズを抑え、cloneやfetchの時間を短縮
- コミットログの可読性向上: 必要な変更のみがコミットログに残り、変更履歴が追いやすくなる
- 共同開発の効率化: 開発者間で不要なファイルの差分が発生するのを防ぎ、コンフリクトを抑制
- セキュリティ: 環境設定ファイルなど、機密情報を含むファイルを誤ってコミットするリスクを低減
gitignoreの仕組み
gitignoreは、パターンマッチングを用いて、無視するファイルを指定します。Gitは、以下の順序で gitignoreパターンをチェックし、最初にマッチしたパターンに基づいてファイルの追跡/無視を決定します。
gitignoreパターンの優先順位 (高い順):
-
コマンドラインパターン:
git add -f <パターン>
など、コマンドラインで直接指定されたパターン -
.gitignore
ファイル: プロジェクト内の.gitignore
ファイル (ディレクトリごとに設定可能) -
$GIT_DIR/info/exclude
: リポジトリ固有の除外設定ファイル -
core.excludesFile
: グローバルな除外設定ファイル (~/.gitconfig
で設定)
.gitignore
ファイルの探索範囲:
-
.gitignore
ファイルは、対象ファイルが存在するディレクトリからルートディレクトリ (.git
ディレクトリがあるディレクトリ) まで、階層を遡って探索されます。 - 上位階層の
.gitignore
に記述されたパターンは、下位階層の.gitignore
で上書きできます。
gitignoreの設定方法
gitignoreの設定は、主に以下の3種類の方法があります。
-
.gitignore
ファイルを作成する: プロジェクトルートディレクトリ、または各ディレクトリに.gitignore
ファイルを作成し、無視したいファイルのパターンを記述します。最も一般的な方法で、プロジェクト内で共有したい設定を記述するのに適しています。 -
$GIT_DIR/info/exclude
を編集する: リポジトリの.git/info/exclude
ファイルを直接編集します。リポジトリ固有の設定を記述する場合に便利ですが、.gitignore
と異なりバージョン管理されないため、共有には向きません。 -
core.excludesFile
を設定する:git config --global core.excludesFile <ファイルパス>
コマンドで、グローバルなgitignoreファイルを設定します。ユーザー全体で共通して無視したいファイル (例: エディタの一時ファイル) を記述するのに適しています。
デフォルトは$XDG_CONFIG_HOME/git/ignore
または~/.config/git/ignore
です。
.gitignore
ファイルの書き方:パターンフォーマット
.gitignore
ファイルに記述するパターンは、以下のルールに従います。
-
基本ルール
- 1行に1パターン: 各行が1つのパターンを表します。
-
コメント:
#
で始まる行はコメントとして扱われます。 - 空白行: 空行は無視されます (区切りとして利用可能)。
-
末尾のスペース: デフォルトでは無視されます。バックスラッシュ (
\
) でエスケープするとスペースもパターンに含められます。
-
パターン構文
-
/
(スラッシュ): ディレクトリ区切り文字。-
先頭: ルートディレクトリからの相対パス (
.gitignore
ファイルと同じディレクトリからの相対パスではない) - 中間/末尾: パターンをディレクトリに限定する場合 (末尾スラッシュ)
-
先頭: ルートディレクトリからの相対パス (
-
*
(アスタリスク): スラッシュを除く任意の文字列にマッチ -
?
(クエスチョンマーク): スラッシュを除く任意の1文字にマッチ -
[]
(角括弧): 文字クラス (例:[a-z]
,[0-9]
) -
!
(エクスクラメーションマーク): 否定パターン。以前のパターンで無視されたファイルを再度含める (再包含)。ただし、親ディレクトリが除外されている場合は再包含できない。 -
**
(二重アスタリスク): ディレクトリ全体にマッチ (再帰的なマッチ)-
**/foo
: どの階層のfoo
にもマッチ (ファイル、ディレクトリ両方) -
abc/**
:abc
ディレクトリ以下の全てのファイル、ディレクトリにマッチ -
a/**/b
:a/b
,a/x/b
,a/x/y/b
など、a
とb
の間に0個以上のディレクトリがあってもマッチ-
**/foo
の例:
ディレクトリ構造: project/ ├── bar/ │ └── foo (マッチ) ├── baz/ │ └── qux/ │ └── foo (マッチ) └── foo (マッチ) `.gitignore` に `**/foo` と記述した場合、上記ディレクトリ構造において、 - project/foo - project/bar/foo - project/baz/qux/foo が無視されます。
-
-
-
実際のgitignoreファイルの記述例:
# コメント: ビルド生成物
*.o # オブジェクトファイル (.o 拡張子のファイル)
*.exe # 実行ファイル (.exe 拡張子のファイル)
build/ # build ディレクトリ (ディレクトリとその中身全て)
# ログファイル
log/*.log # log ディレクトリ以下の .log ファイル
# 一時ファイル
*.tmp # .tmp 拡張子のファイル
._* # macOS の一時ファイル (._ で始まるファイル)
# 環境設定ファイル (注意: 機密情報を含む可能性があるので、コミットしないように注意!)
.env
# 特定のファイルを例外的に含める (再包含)
!important.txt # important.txt ファイルは無視しない
# ディレクトリを特定
doc/frotz/ # doc/frotz ディレクトリのみ (doc/frotz ファイルにはマッチしない)
# 任意の階層の foo ディレクトリ
**/foo # どの階層の foo ディレクトリにもマッチ
# 特定のディレクトリ以外を全て除外 (例: foo/bar ディレクトリのみ管理したい場合)
/*
!/foo
/foo/*
!/foo/bar
設定時の落とし穴
gitignoreを設定する上で、いくつか注意すべき点があります。
-
既に追跡されているファイルは無視されない: gitignore は、これから追跡するファイルに対して有効です。既にGitの管理下にあるファイル (indexに追加済みのファイル) を無視したい場合は、以下のコマンドでindexから削除する必要があります。
git rm --cached <ファイルパス>
その後、gitignore にパターンを追記します。
- 対策:
.gitignore
を設定する前にgit status
で追跡状況を確認し、意図せず追跡されているファイルがないか確認する
- 対策:
-
除外されたディレクトリは再包含できない: パフォーマンス上の理由から、除外されたディレクトリ自体はGitが探索しません。そのため、除外されたディレクトリ内のファイルを
!
で再包含することはできません。ディレクトリ単位で除外/包含を管理する必要があります。- 対策:ディレクトリを除外する前に、本当にディレクトリ全体を除外して問題ないか慎重に検討する
-
シンボリックリンクの扱い: Gitは、
.gitignore
ファイルを探索する際にシンボリックリンクをたどりません。シンボリックリンク自体を無視したい場合は、シンボリックリンクのパスをgitignoreに記述する必要があります。- 具体的には、シンボリックリンク自体ではなく、リンク先のファイルを無視したい場合は、
.gitignore
にリンク先のファイルのパスを記述します。 - 例えば、config_linkというシンボリックリンクがconfig.jsonを指している場合で、config.jsonを無視したい場合は、
.gitignore
にconfig.json
のように記述します。 - この場合、config_link自体はGitの追跡対象となりますが、リンク先のconfig.jsonの内容は無視されます。
- ポイント:
.gitignore
のパターンは、シンボリックリンク自体ではなく、リンク先のパスに対して適用されます。
- 具体的には、シンボリックリンク自体ではなく、リンク先のファイルを無視したい場合は、
-
否定パターン (
!
) の順序: 否定パターンは、直前の肯定パターンを打ち消す効果があります。否定パターンを記述する順序によって結果が変わる場合があるので注意が必要です。
Tips
.gitignore
を効果的に使うためのTipsを最後にまとめておきます。
- テンプレートを積極的に活用する
- 具体的なファイル名よりも、パターンで記述することを心がける
- コメントを適切に記述して、gitignore の意図を明確にする
-
.gitignore
を変更したら、意図通りにファイルが無視されるかgit status
で確認する - チーム開発では、メンバー間で
.gitignore
の設定を統一する - グローバルgitignore(core.excludesFile)は、プロジェクト固有の設定と混同しないように注意する
<よくある間違い>
- 既に追跡されているファイルを
.gitignore
に追加しても無視されない - 否定パターン(
!
)の使い方を誤る - ディレクトリを除外したつもりで、ファイルだけを除外してしまう(末尾スラッシュ
/
の有無による違い)
おわりに
gitignoreを適切に設定することで、リポジトリをクリーンに保ち、意図しないファイルのコミットを防ぎ、共同開発をスムーズに進めることができます。
gitignoreのパターンフォーマットは正直難しく、記述例でなんとなく文法を理解できた気がするものの、実際どんな感じのgitignoreファイルがあるんだろうか...と探っていると、便利なテンプレートを発見しました!
言語ごとに用意されていて最高ですね!
私はよくPythonを使うのですが、Pythonのテンプレートが参考になりましたので、さっそく活用したいと思います。