Skip to content
This page has been auto-translated and may contain errors.View in English

ファイルを無視することと良い習慣

docs.scrimba.com

weather-app というプロジェクトをクローンして、npm install を実行し、新しいリポジトリのステータスを確認します。

bash
$ git status
Untracked files:
  (use "git add <file>..." to include in what will be committed)
	node_modules/
	dist/
	.env

3 つのフォルダとファイルが未追跡として表示されますが、どれもコミットに含めるべきではありません。node_modules/npm installpackage.json から数秒で再生成する数千ものファイルです。dist/ はソースから毎回再構築されるビルド出力です。そして .env は API キーを保持しています。これらはどれもプロジェクトの履歴に入るべきではなく、考えずに git add . を入力するとそうなってしまいます。

Git に無視するものを告げる

.gitignore ファイルは、Git が追跡すべきではないファイルとフォルダのパターンを一覧にします。プロジェクトのルートに一度作成します。これは、最初のリポジトリでリポジトリをセットアップするときに git init を実行した場所と同じです。その後、git statusgit add . は一致するものをひっそりとスキップします。

bash
# .gitignore
node_modules/
dist/
.env

各行はパターンです:dist/ のようなプレーンなフォルダ名はそのフォルダ全体を無視し、プロジェクトのどこに表示されても無視されます。 .gitignore ファイル自体をコミットします。それは小さく、プロジェクトをクローンしたすべての人に役立ち、ソースコードと同じ方法で履歴に属しています。

bash
$ git status
On branch main
nothing to commit, working tree clean

パターンが配置されると、git status は静かになります。それはまさにポイントです:コミットしたくないフォルダとファイルがノイズとして表示されなくなるので、表示されるものは実際に重要なものです。

パターンはグロブをサポートしているため、*.log は名前に関係なく .log で終わるすべてのファイルを無視します。末尾のスラッシュはパターンをディレクトリに制限するため、dist/dist という名前のフォルダのみに一致し、同じ名前を共有するファイルはそのままにします。行の先頭に ! を付けて、より広いパターンでキャッチされるものを無視解除します。これは、無視されたフォルダ内の 1 つのファイルを追跡する必要がある場合に役立ちます:

bash
# .gitignore
logs/
!logs/keep-this.log

Git は、プロジェクトに関係なく、すべてのリポジトリで必要なパターン用に、マシンごとに 1 つのグローバル無視ファイルも読み取ります:.DS_Store*.swp などのエディタジャンク。git config --global core.excludesfile ~/.gitignore_global で一度セットアップすれば、プロジェクトごとにこれらのパターンを追加する必要はもうありません。

無視パターンはシェルグロブと同じ方法でマッチングされます:* は任意の文字の実行用、? は 1 つ用、** はディレクトリ全体でマッチングする用です。スラッシュなしのパターンはプロジェクト内の任意の深さでマッチングされますが、スラッシュを含むパターンはそのパスにアンカーされます。これらのパターンのマッチングは純粋にローカルな動作です:Git は .gitignoregit statusgit add に何を表示するかを決定するときだけ参照します。ファイルが Git によってすでに追跡されると、ファイルがファイルから追跡されるのを停止しません。これは次のセクションで重要です。

JunoGit に無視するものを告げる.gitignore ファイルは、Git が追跡すべきではないものを一覧にします:node_modules/dist/.env はほぼすべてのプロジェクトの常識です。プロジェクトをクローンした全員が同じクリーンなステータスを取得するように、.gitignore ファイル自体をコミットしてください。私は一度、事故で数千の依存ファイルをステージングしてこれを学ぶ前に一日中を無駄にしました。
JunoGit に無視するものを告げる パターンは *.log のようなグロブをサポートし、末尾のスラッシュは「ディレクトリのみ」を意味し、先頭の ! はより広いマッチング内のものを無視解除します。グローバル無視ファイルは core.excludesfile で設定され、エディタと OS のジャンクが属する場所なので、すべてのプロジェクトの .gitignore に手で .DS_Store を追加するのを止められます。
JunoGit に無視するものを告げる 無視ルールはシェルスタイルのグロブマッチングで、クライアント側で適用され、Git に何が表示されるかを変更するだけです。ルールは Git がすでに知っているファイルに影響を与えることができないことに注意してください。その違いは紙の上では小さく、実際には費用がかかります。

シークレットをコミットしない

API キー、データベースパスワード、または署名証明書はコミットに表示されるべきではありません。 これはプライベートリポジトリと同じくらい公開リポジトリに保持され、最初のコミットからずっと保持されます。シークレットを .env のようなファイルに入れ、初日に .gitignore にそのファイルを追加し、ソースファイルに書き込む代わりに値をそこから アプリケーションにロードします。

bash
# .env
WEATHER_API_KEY=sk_live_9f8e7d6c5b4a
bash
# .gitignore
.env

このルールを自動的に、毎回、例外なしに従ってください。コミットに存在するキーはリポジトリへのアクセス権を持つ人なら誰でも到達でき、公開リポジトリでは誰でも。

実際のプロジェクトでは、シークレットは通常 1 か所以上に存在します:自分のマシン用の .env ファイルと、デプロイされた何かのシークレットマネージャーまたはホストの環境変数設定。どちらも実際には同じように機能します:値はソースファイルと Git の外に存在し、コードは実行時に環境から読み込みます。代わりに .env.example ファイルを配布します。変数名は含まれますが実際の値は含まれません。チームメンバーが実際のキーを見ることなく何を設定するかを知ることができます。

このルールを交渉不可能にする事実は次の通りです:シークレットを削除した コミット は履歴から削除されません。キーを持っていたすべての以前のコミットはそれをまだ持っており、リポジトリの履歴に永遠に保存され、誰かが作成したすべてのクローンはそれらの古いコミットとともに実行されます。新しいコミットでファイルを削除することは、最新のスナップショットがそれを持たなくなったことを意味するだけです。古い値を保持するオブジェクトはまだ .git に存在し、以前のコミットをチェックアウトしたり、ログを掘り下げたりする人なら誰でも到達できます。

「ファイルを削除してもう一度コミットする」は、すでに漏らされたシークレットを修正することはありません。実際のシークレットがコミットに着地した場合、プロバイダーでそれを回転または無効化します(新しいキーを発行し、古いキーを無効化します)。漏らされた値がリポジトリの後の操作に関係なく役に立たなくなります。履歴を書き直してシークレットを削除することは可能ですが、キーが公開されている間にすでに使用されたことを取り消しません。回転を修正として扱い、履歴のクリーンアップを任意の追加として扱います。

Junoシークレットをコミットしない 実際の API キー、パスワード、または証明書をコミットに入れないでください。シークレットを .env のようなファイルに保持し、git add を実行する前に .envgitignore に追加してください。これはこの章全体で絶対として扱う価値のある 1 つのルールです。
Junoシークレットをコミットしない ローカルシークレットは .env に入り、デプロイされたシークレットはホストの環境変数またはシークレットマネージャーに入り、どちらもコミットされません。変数名のみで env.example を配布してください。チームメンバーが実際の値を見ることなく何を設定するかを知ることができます。
Junoシークレットをコミットしない 新しいコミットでシークレットを削除することは、履歴から削除されません。すべての以前のコミットとすべての既存のクローンはそれをまだ持っています。リークが発生した瞬間にプロバイダーでキーを回転させます:その回転が実際に重要な修正です。

追跡後に .gitignore にファイルを追加する

これはほぼすべての人を少なくとも一度は引っかかる落とし穴です。誤ってファイルをコミットし、間違いに気付き、.gitignore に追加します(以下の echo 行はそのファイルにパスを追加します)。Git がそれを忘れることを期待します。それはしません。

bash
$ git add config/settings.json
$ git commit -m "Add app settings"

# 後で、これが間違いだったことに気付きます
$ echo "config/settings.json" >> .gitignore
$ git status
On branch main
nothing to commit, working tree clean

git status はクリーンなツリーを表示しますが、config/settings.json はまだ追跡されており、将来のすべての git log とすべてのクローンに引き続き表示されます。無視ルールは Git がまだ知らないファイルにのみ適用されます。 ファイルが追加されてコミットされたら、それは履歴の一部であり、.gitignore は履歴に既に存在するファイルに対して何も言うことはありません。

実際に今後の追跡を停止するには、Git にディスク上のファイルを残したまま、追跡するファイルを削除するよう指示してください:

bash
$ git rm --cached config/settings.json
$ git commit -m "Stop tracking config/settings.json"

git rm --cached <file> は、作業ディレクトリから削除することなく、Git の追跡からファイルを削除します。その削除をコミットすれば、そこから .gitignore パターンは最初に求めていた仕事をします。

同じ修正は git rm -r --cached <folder> でフォルダ全体に機能します。これは、誰も .gitignore をまったく追加する前にプロジェクトの初期に node_modules/ または dist/ がコミットされたことに気付いた後の通常の動きです。一度実行してコミットを削除すると、フォルダはすべての将来のコミットから消えますが、すべてのチームメイトのファイルのローカルコピーはディスク上にそのまま残ります。

この動作はステージング領域が実際に何であるかから直接フォローします。Git はファイルを インデックス を通じて追跡します。次のコミットに入るものの正確な記録です。ファイルを追加してコミットすると、インデックスにそのエントリが書き込まれ、コミットのスナップショットに書き込まれます。.gitignore はただ Git がまだインデックスエントリを持たないファイルで何をするかを決定するときにのみ参照されます。

エントリが存在したら、無視パターンはそれに無関係です。git rm --cached はファイルをディスク上に保ったままインデックスエントリを削除します。これが実際にこれを修正するコマンドである理由です。単に無視パターンを編集することは何もしません。

Juno追跡後に .gitignore にファイルを追加する Git がそれを既に追跡した後に .gitignore にファイルを追加することはそのファイルに対して何もしません。無視ルールは Git がまだ追加していないファイルにのみ適用されます。事故で滑り込んだファイルの追跡を停止するには、git rm --cached <file> を実行してその変更をコミットしてください。
Juno追跡後に .gitignore にファイルを追加する.gitignore は未追跡ファイルにのみ影響します。すでにコミットされたものには、git rm --cached <file>、またはフォルダ全体には git rm -r --cached <folder> でドロップしてからコミットしてください。みんなのローカルファイルはそのまま、Git の追跡のみ変わります。
Juno追跡後に .gitignore にファイルを追加する インデックスはすべての追跡ファイルのエントリを保持し、.gitignore はまだエントリがないときだけ参照されます。git rm --cached はディスク上のファイルに触れずにインデックスエントリを削除します。これが実際の修正です。ファイルがスリップバックできないように無視パターンを追加するのは良い対策です。

良い習慣:小さなコミットとクリーンなリポジトリ

きれいな .gitignore は、リポジトリを価値あるものにするの半分です。もう半分はコミットをどのように形成するかです。各コミットを 1 つのフォーカスした変更にします:バグ修正、1 つの小さな機能、1 つのリファクタリング。午後の仕事が複数の関連のないものに触れる場合、日の終わりにすべてを 1 つにドロップする代わりに複数のコミットに分割してください。

bash
$ git add src/weather-widget.js
$ git commit -m "Fix temperature rounding in weather widget"

小さなコミットは確認が容易で、何か壊れた場合は復帰が容易で、6 か月後に戻ってコード行が存在する理由を思い出すときは読むことが容易です。コミットループ は有用なコミットメッセージを構成するものをカバーしています。この習慣は変更自体を十分に小さく保つことで、良いメッセージを書くことさえ可能にすることです。

その習慣をクリーンな .gitignore と組み合わせると、利益は複合します:小さく意図的なコミットで作られた履歴、生成されたフォルダやストレイ設定ファイルはdiff を乱すことはありません。dist/ チャーンと誰かが編集した 2 つの実際の行で満ちた変更を確認することはすべての人にとって痛ましく、初日に .gitignore を設定すれば完全に回避できます。

スケールでこれは読みやすさを超える方法で支払います。生成されたファイルと依存フォルダから自由なリポジトリはクローンするのに十分小さく保ち、フォーカスした コミットの履歴は git log --oneline と 2 つの時点間の git diff を実際に何が起こったか、なぜそうなったかを理解するのに実際に役立ちます。半分のコミットが node_modules/ チャーンで、もう半分が 5 つの関連のない変更を混ぜている履歴ではうまく機能しません。

Juno良い習慣:小さなコミットとクリーンなリポジトリ 各コミットを 1 つのフォーカスした変更に保ち、追跡すべきではないフォルダとシークレットをカバーする .gitignore を保ちます。これらの 2 つの習慣は、リポジトリを楽しく働く理由のほとんどです。小さな習慣は早期に形成されるので、後で多くのクリーンアップを保存します。
Juno良い習慣:小さなコミットとクリーンなリポジトリ 小さく、フォーカスしたコミットプラスクリーンな .gitignore は、レビューと diff を生成されたノイズに埋没する代わりに実際に読むことができるようにします。最初のコミット前に .gitignore をセットアップしてください。5 回目のプル要求で node_modules/ が表示されるのを待つことは、保存する以上のクリーンアップコストです。
Juno良い習慣:小さなコミットとクリーンなリポジトリ リーンなリポジトリとフォーカスしたコミットの履歴は、クローンを高速に保ち、それを通じて検索するときに履歴を読める状態に保つものです。履歴に滑り込む生成されたファイルまたは依存フォルダはすべて、すべての将来のクローンと検索が支払う小さな税です。無視ルールを事前に設定すると、それはもう再考する価値がある問題になることを止めます。