すでにGitの管理対象になっているファイルを後から除外したい場合には、.gitignore
を追加、追記するだけでは除外されず、git rm --cached <file-name>
などで、管理対象から除外してあげないといけません。
しかし、除外ファイル数が多くなったり、うっかりgit rm --cached
を忘れて管理対象から外れていないファイルがあったりした場合には少々手間です。
よく紹介されているgit rm --cached <file-name>
だと、除外したいファイルを一つ一つ指定していないといけませんし、git rm --cached .
だと、管理対象のファイルが一旦全て除外されてしまうため、その後にgit add
してまたステージングエリアに上げないといけないので二度手間です。
後から.gitignore
する方法に関してはすでに色々な情報がありますが、なかなか万能な方法が見つからなかったので僕なりのベストプラクティスを考えてみました。
動作環境
この記事は、以下のバージョンのGitで動作確認しています。
git version 2.32.0
まずは結論から
.gitignore
を追加、または追記した後に以下のコマンドを実行することで、管理対象から新しく除外したいファイルのみを除外することができます。
git rm --cached `git ls-files -ci --exclude-standard`
なお、ローカルのファイル自体が削除されるわけではありません。
上記の方法だと.gitignore
以外にも、.git/info/exclude
やグローバルな設定などの一般的なGitの除外設定も含まれるので、リポジトリの.gitignore
だけを対象に除外したい場合は以下のようにします。
git rm --cached `git ls-files -ci --exclude-from=.gitignore`
これで、ワンコマンドで管理対象になっているファイルの中から、.gitignore
で指定されているファイルだけを除外することができます。
特に理由がなければ--exclude-standard
の方でいいかなと思います。
.gitignore
に差分がある場合は、そちらも忘れずにステージングエリアに上げてからコミットしましょう。
とりあえず目的が達成できればいいよって方は、エイリアスにしておくと便利まで飛ばしてもらって、紹介しているエイリアスを設定して使ってもらうと便利です。
理解してないコマンドは叩きたくない!という方のために、以降から詳しく解説していきます。
背景
対象ファイルが把握できる数ならgit rm --cached <file-name>
でも問題ないですが、例えば後から新規に.gitignore
のテンプレを追加したりして、除外対象のファイルが多くなってくると面倒です。
また、過去の遺物や引き継ぎなどで.gitignore
に記載はあるものの、管理対象から除外されていないファイルがあるリポジトリを整備する際などには一気に除外したいものですが、前述の通りgit rm --cached .
では管理対象のファイルが一旦全て除外されてしまうので、git add
してステージングエリアに上げなおす手間がかかります。
今回の方法だと、ワンコマンドで目的のファイルだけピンポイントに除外できるのでオススメです。
引き継ぎリポジトリとかだとメンテが行き届いていなくて、除外対象のファイルが大量に出てくる場合があるので、エイリアスにしておくと便利セクションではそんなときに便利な、除外する前に除外されるファイルを確認する方法も紹介しています。
できるだけGitの公式リファレンスのリンクをつけているので、より詳細な説明はそちらも参考にしてみてください。(リンクは全てGitバージョン2.32.0のリファレンスへのリンクになっています。)
コマンドの説明
使用している各コマンドの詳細を説明します。
ここからは便宜上、--exclude-standard
や--exclude-from=.gitignore
のexclude系のオプション群を、まとめて--exclude*
と表記します。
git rm --cached **<**file1 file2 ...>
指定されたファイルを管理対象から除外します。スペース区切りで複数ファイルの指定が可能です。
ローカルのファイルは削除されずに残ります。
--cached
オプションを付けないとファイルごと削除されてしまうので注意です。
今回の方法ではgit ls-files -ci --exclude*
で出力されたファイルを、管理対象から除外しているというわけです。
git ls-files
オプションなしで実行することで、Gitの管理対象になっているファイルを表示します。
暗黙的に-c
オプションがデフォルトで指定されています。
-o
オプションを指定することで、逆にGitの管理対象になっていないファイルを表示することができます。
オプション説明
git ls-files -ci --exclude*
で、除外対象ファイルの特定に使用しているgit ls-files
コマンドのオプションについて説明します。
-c, --cached
出力の中でキャッシュされているファイルのみを表示します。
キャッシュされているとは、Gitのインデックスに登録されている、つまり管理対象であるということで、要は管理対象のファイルが表示されるということです。
これはls-files
のデフォルトの挙動となっていますが、-i
オプションを使用する際には明示的に指定してあげないといけません。
-i, --ignored
無視されたファイルのみを出力に表示します。
少なくとも1つの--exclude*
オプションを使って無視ルールを指定する必要があります。
このオプションは--exclude*
オプションとセットで使う必要があるため、否定の否定みたいで少しわかりにくいですが、--exclude*
で無視ルールとして指定されたパターンに一致するファイル(無視されたファイル)が(逆に)出力されるというイメージです。
また、-o
または-c
オプションも一緒に使用する必要があります。
指定しないと以下のエラーが出ます。
fatal: ls-files -i must be used with either -o or -c
--exclude*
系のオプションは-o
または-i
オプションが指定されている場合にのみ利用できるようです。
git ls-files can use a list of "exclude patterns" when traversing the directory tree and finding files to show when the flags --others or --ignored are specified. gitignore[5] specifies the format of exclude patterns.
git ls-files -c --exclude-standard
でも実行自体はできましたが、出力からexcludeパターンに指定されているファイルが除外されませんでした。
git ls-files -o --exclude-standard
だと除外ルールが適用され、excludeパターンに指定されているファイルが出力から除外されました。
--exclude-from=**<**file>
file
から除外パターンを読み込んで、そのルールにしたがって出力から除外します。
今回はこのオプションに.gitignore
を渡しています。
出力から.gitignore
に指定のあるファイルのみを除外するイメージです。
前述の通り、--exclude*
系のオプションは-o
または-i
オプションが指定されている場合にのみ利用できるようです。
--exclude-standard
標準的なGitの除外設定で指定されているファイルを出力から除外します。
.git/info/exclude
、各ディレクトリのgitignore
、ユーザーのグローバルな除外ファイルが対象のようです。
Add the standard Git exclusions: .git/info/exclude, .gitignore in each directory, and the user’s global exclusion file.
少しわかりにくいのが、-i
オプションと--exclude*
オプションを組み合わせたときに、**「excludeパターンにマッチするものだけが表示される」**というところです。冒頭のコマンドの一部を以下のように、オプション指定の順番を入れ替えてみるとわかりやすいかもしれません。
git ls-files -c --exclude-standard -i
つまり、git ls-files -c
の出力の中で、--exclude*
オプションによって、無視(除外)されたファイルを、-i
オプションで表示させている、と考えると少しわかりやすいかもしれません。
エイリアスにしておくと便利
Gitのグローバル設定でaliasにしておくと便利です。
# 以下を追記
[alias]
# 除外設定されているファイルのうち、まだ管理対象から除外されていないファイルを表示します。
unignored = ls-files -ci --exclude-standard
# または
# unignored = ls-files -ci --exclude-from=.gitignore
# 除外設定されているファイルのうち、まだ管理対象から除外されていないファイルを除外します。
cache-clean = !git rm --cached `git ls-files -ci --exclude-standard`
# または
# cache-clean = !git rm --cached `git ls-files -ci --exclude-from=.gitignore`
以下のようにして使います。
-
.gitignore
を追加するなり追記するなりして、Git管理から除外したいファイルを設定する。 -
.gitignore
に記載されているものの、まだ管理対象から除外されていないファイルを表示して確認する。git unignored
-
前の手順で確認したファイルを管理対象から除外する。
git cache-clean
-
コミットする時は、忘れずに
.gitignore
の変更をステージングエリアに上げてからコミットする。
補足
-
unignored
は英単語として正しくないかもしれませんが、利便性を考慮してこのように表現してみました。 -
cache-clean
は文法的にはclean-cache
の方が正しいかもしれませんが、万が一git clean
が実行されてしまうと怖いので、このように表現しています。 -
cache-clean
にて、Gitのエイリアスではバッククォートがコマンド置換されないので、エイリアスの先頭に!
をつけてシェルコマンドとして扱うようにしています。ただし、!
のコマンドの実行ディレクトリはカレントディレクトリではなく、リポジトリの最上位ディレクトリになるというところには注意が必要です。
If the alias expansion is prefixed with an exclamation point, it will be treated as a shell command.
Note that shell commands will be executed from the top-level directory of a repository, which may not necessarily be the current directory.
何か気づいた点があればコメントいただけると嬉しいです🙌