keeping-git-repos-clean について
開発者やメンテナー向けに、秘密情報や生成物、個人用ファイルを Git リポジトリに含めない方法をまとめたスキルです。追跡対象の監査、安全な除外、認証情報のローテーション、検証後の変更確認を扱います。
主な機能
- 01追跡ファイルの秘密情報や生成物の監査
- 02ローカルコピーを残して追跡解除
- 03漏えいした認証情報の先行ローテーション
- 04グローバル除外とコミット時スキャン
- 05検証前後のリポジトリ状態確認
活用シーン
- 01追跡済みの秘密情報や一時ファイルの発見
- 02ローカルファイルを安全に追跡解除
- 03履歴に残った認証情報漏えいへの対応
- 04生成物がレビュー対象に混ざるのを防止
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wdm0006/ python-skills --skill keeping-git-repos-cleanインストール後、Claude Code で/keeping-git-repos-cleanを入力して読み込まれたか確認します。
詳細
Git リポジトリをクリーンに保つ
リポジトリに入り込みがちなファイルは、大きく分けてシークレットと開発用アーティファクトの 2 種類です。 どちらも事前に防ぐのは簡単ですが、後から片付けるのは大変です。Git の履歴は残り続け、公開リポジトリではすべてが公開されるためです。
みんなが忘れがちな唯一のルール
.gitignore は、すでにコミット済みのファイルの追跡を停止しません。 パスを .gitignore に追加しても、防げるのは今後、追跡されていないファイルがステージングされることだけです。Git がすでに追跡しているファイルは、その後もコミットされ続けます。これは何度も問題になります。たとえば .env が .gitignore に記載されていても、そのルールが追加される前にコミットされていたため、公開され続けることがあります。
ローカルのコピーを残したまま、ファイルの追跡を実際に停止するには:
git rm --cached path/to/file # untrack, leave working-tree copy in place
git rm -r --cached some/dir/ # for a directory
echo "path/to/file" >> .gitignore # then ignore it so it doesn't come back
git commit -m "Stop tracking <file>; add to .gitignore"
重要なのは --cached フラグです。通常の git rm は作業コピーも削除します。
リポジトリが実際に追跡しているものを監査する
何がクリーンかを判断するために .gitignore を信用せず、インデックスを直接確認してください:
git ls-files | grep -iE '\.(env|pem|key|p12|profraw|log|bak|db|sqlite3?)$'
git ls-files | grep -iE '(^|/)(\.DS_Store|~\$|todo\.db|node_modules/|__pycache__/)'
git ls-files '*.db' '*.sqlite*' # scratch databases
git ls-files | xargs -I{} du -h {} | sort -rh | head # surprisingly large tracked files
実際のリポジトリでよく見かける要注意ファイル:
- シークレット: 有効なトークンを含む
.env、設定モジュールにハードコードされたAWS_*/DB の認証情報、本番環境に含まれたSECRET_KEY = "CHANGEME"/"foobar"などのプレースホルダー。 - ビルドアーティファクト: LaTeX の
.aux/.toc/.log/.synctex.gz/.pdf、LLVM の*.profraw、コンパイル済みバイナリ、htmlcov/、dist/、*.egg-info/。 - 一時ファイル/個人用アーティファクト:
todo.dbなどのツール固有の一時 SQLite DB、エディターのバックアップ(*.backup、*.bak、Word のロックファイル~$*.docx)、紛れ込んだ*.log。 - OS の不要ファイル:
.DS_Store、Thumbs.db。
シークレットには git rm だけでは不十分です
HEAD からシークレットを削除しても、履歴からは削除されません。git log -p や、シークレットを追加したコミットから、引き続き閲覧できます。以下の 3 つを順番に行う必要があります。そのうち、実際に保護につながるのは最初の手順だけです。
- 認証情報をローテーションします。 リモートに一度でも送られたシークレットは、漏えいしたものとして扱ってください。新しいトークン/キー/パスワードを発行し、古いものを無効にします。まずこれを行ってください。漏えいした値は、プッシュされた時点で公開されているためです。
- 今後の追跡を停止します(
git rm --cached+.gitignore+ 必要な環境変数をプレースホルダー値のみで記載した.env.example)。 - 必要に応じて履歴を消去します(
git filter-repo --invert-paths --path .env、または BFG を使用)し、強制プッシュします。これにより SHA が書き換えられ、共同作業者に影響するため、通常は認証情報のローテーション後にメンテナーが意図的に行う作業であり、自動化された PR では行いません。
(2)と(3)を実施しても(1)を省略した PR は、誤った安心感を与えます。値は引き続き有効で、履歴を含むクローンやフォークにも残っています。ローテーションが人による対応として必須であることを、必ず明記してください。
「コミット済み」は「公開済み」を意味します
公開リポジトリでは、特に path: '.' でデプロイされる静的サイト(GitHub Pages はリポジトリ全体をアップロードします)の場合、追跡対象のファイルはすべて公開 URL から取得できます。サイトのリポジトリのルートにある一時ファイル todo.db は、/todo.db として配信されます。公開リポジトリにコミットする前に、誰でもダウンロードできるものと考えてください。
予防策:グローバル ignore +シークレットスキャナー
エディターファイル、OS ファイル、todo.db のようなツールの一時 DB など、開発者ごとに発生する不要ファイルは、各プロジェクトの .gitignore ではなくグローバルに無視するべきです。そうすれば、どのリポジトリにも入り込みません:
git config --global core.excludesFile ~/.gitignore_global
printf '%s\n' '.DS_Store' '*.swp' 'todo.db' '*.profraw' >> ~/.gitignore_global
シークレットが履歴に残るのを防ぐため、コミット時に pre-commit フックでブロックしてください:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks: [{id: gitleaks}]
- repo: https://github.com/Yelp/detect-secrets
rev: v1.5.0
hooks: [{id: detect-secrets, args: ["--baseline", ".secrets.baseline"]}]
スタータープロジェクト用の .gitignore(これをコミットしてください):
# Secrets / local config
.env
.env.*
!.env.example
*.pem
*.key
# Python build/test artifacts
__pycache__/
*.py[cod]
build/
dist/
*.egg-info/
.coverage
htmlcov/
.pytest_cache/
# Scratch / OS / editor
*.db
*.sqlite
*.sqlite3
*.profraw
*.log
*.bak
*.backup
.DS_Store
~$*
検証によって作業ツリーが汚れることがあります
コンパイラー、テストランナー、アセットパイプラインは、変更の検証だけを目的としたコマンドであっても、チェックアウト内にファイルを書き込むことがよくあります。無視されていないキャッシュファイルを作成したり、PDFやコンパイル済みCSSなどの追跡対象の配布物を再生成したりすることがあります。コマンドが成功しても、作業ツリーに自分の変更だけが残っているとは限りません。
検証の前後に状態を確認し、レビューしたパスだけをステージングしてください。
git status --short
make test # or the project's real build command
git status --short
git diff -- path/you/changed
git add path/you/changed # never sweep in generated files with git add -A
git diff --cached --check
git diff --cached --stat
ツールが必然的に大量の出力を生成する場合は、チェックアウトの使い捨てコピーで実行してください。これにより、実際のビルドを維持しつつ、生成ファイルがパッチに混入するのを防げます。
scratch_dir=$(mktemp -d)
rsync -a --exclude .git ./ "$scratch_dir/"
(cd "$scratch_dir" && make build)
検証によって、意図的に対象外としている追跡対象の生成物が変更された場合は、差分を確認した後に限り、その正確なパスだけを復元してください。
git diff -- docs/manual.pdf
git restore -- docs/manual.pdf
クリーンアップに広範囲な復元やリセットを使用しないでください。チェックアウトには、すでに他の人の作業が含まれている可能性があります。また、未追跡の生成物のクリーンアップに git stash を頼らないでください。通常のスタッシュでは未追跡ファイルが除外されます。また、--include-untracked を指定しても、stash pop の前に再生成されたファイルと競合することがあります。既知の生成パスは、明示的に生成を防ぐか削除してください。
チェックリスト
Audit:
- [ ] `git ls-files` reviewed for secrets, build output, scratch DBs, OS files
- [ ] No live credentials in tracked source or .env
- [ ] No surprisingly large/binary tracked files
Remediate (if dirty):
- [ ] Secret rotated/revoked FIRST (history is public the moment it was pushed)
- [ ] `git rm --cached` + .gitignore entry for each offending file
- [ ] .env.example documents required vars with placeholder values only
- [ ] History scrub flagged as a maintainer follow-up if the secret is in history
Prevent:
- [ ] Project .gitignore covers secrets, build artifacts, OS/editor noise
- [ ] Global core.excludesFile catches per-developer scratch files
- [ ] gitleaks / detect-secrets pre-commit hook installed
- [ ] Verification bracketed by `git status --short`; only reviewed paths staged
ソースコードの脆弱性やハードコードされたシークレットのパターンをスキャンする場合は、auditing-python-security スキルを参照してください。このスキルの対象は、Gitが追跡するものです。