git filter-branch
コマンドと BFG Repo-Cleaner は、リポジトリの履歴を書き換えます。変更を加えた既存のコミットや依存関係にあるコミットの SHA を変更します。 コミットの SHA が変更されると、リポジトリでオープンされたプルリクエストに影響する可能性があります。 ファイルをリポジトリから削除する前に、オープンプルリクエストをすべてマージまたはクローズすることを推奨します。
git rm
によって、最新のコミットからファイルを削除することができます。 最新のコミットに追加されたファイルの削除の詳しい情報については、「リポジトリの履歴からファイルを削除する」を参照してください。
警告: コミットを GitHub Enterprise にプッシュしたら、そこに含まれるデータが危険にさらされることを考慮する必要があります。パスワードをコミットした場合は、変更してください。 キーをコミットした場合は、新たに生成してください。
この記事では、機密データを含むコミットに GitHub Enterprise リポジトリのブランチやタグから到達できないようにする方法を説明しています。 ただし、こうしたコミットも、リポジトリのクローンやフォークからは、GitHub Enterprise でキャッシュされているビューの SHA-1 ハッシュによって直接、また参照元のプルリクエストによって、到達できる可能性があることに注意することが重要です。 GitHub Enterprise では、リポジトリに既存のクローンやフォークについては何もできませんが、キャッシュされているビューや、プルリクエストでの機密データへの参照は、GitHub Enterprise サイト管理者 へ連絡することにより恒久的に削除することができます。
ファイルをリポジトリの履歴からパージする
BFG を使用する
BFG Repo-Cleaner は、オープンソースコミュニティによって構築およびメンテナンスされているツールです。 これは、不要なデータを削除する手段として、git filter-branch
より高速でシンプルです。 たとえば、機密データを含むファイルを削除して、最新のコミットをそのままにしておくには、次を実行します:
$ bfg --delete-files 機密データを含むファイル
passwords.txt
にリストされているすべてのテキストについて、リポジトリの履歴にあれば置き換えるには、次を実行します:
$ bfg --replace-text passwords.txt
完全な使用方法とダウンロード手順については、BFG Repo-Cleaner のドキュメントを参照してください。
filter-branch を使用する
警告: 変更を stash した後に git filter-branch
を実行した場合、他の stash コマンドで変更を取得することはできなくなります。 git filter-branch
を実行する前に、加えた変更を unstash するようおすすめします。 stash した最後の一連の変更を unstash するには、git stash show -p | git apply -R
を実行します。 詳しい情報については、Git Tools Stashing を参照してください。
git filter-branch
がどのように機能するかを図解するため、機密データを含むファイルをリポジトリの履歴から削除し、.gitignore
に追加することで、誤って再コミットされないようにするための方法を示します。
-
機密データを含むリポジトリのローカルコピーが履歴にまだない場合は、ローカルコンピュータにリポジトリのクローンを作成します。
$ git clone https://hostname/ユーザ名/リポジトリ > Initialized empty Git repository in /Users/ファイルパス/リポジトリ/.git/ > remote: Counting objects: 1301, done. > remote: Compressing objects: 100% (769/769), done. > remote: Total 1301 (delta 724), reused 910 (delta 522) > Receiving objects: 100% (1301/1301), 164.39 KiB, done. > Resolving deltas: 100% (724/724), done.
-
リポジトリのワーキングディレクトリに移動します。
$ cd リポジトリ
-
次のコマンドを実行します。
機密データを含むファイルへのパス
は、ファイル名だけではなく、削除するファイルへのパスで置き換えます。 その引数により、次のことが行われます:- 各ブランチとタグの履歴全体を強制的に Git で処理するが、チェックアウトはしない
- 指定のファイルを削除することにより、生成された空のコミットも削除される
- 既存のタグを上書きする
$ git filter-branch --force --index-filter \ "git rm --cached --ignore-unmatch 機密データを含むファイルへのパス" \ --prune-empty --tag-name-filter cat -- --all > Rewrite 48dc599c80e20527ed902928085e7861e6b3cbe6 (266/266) > Ref 'refs/heads/master' was rewritten
メモ: 機密データを含む当該ファイルが (移動されたか名前が変更されたため) 他のパスに存在していた場合、このコマンドはそのパスでも実行する必要があります。
-
機密データを含むファイルを、誤って再度コミットしないようにするため、
.gitignore
に追加します。$ echo "機密データを含むファイル" >> .gitignore $ git add .gitignore $ git commit -m "Add 機密データを含むファイル to .gitignore" > [master 051452f] Add 機密データを含むファイル to .gitignore > 1 files changed, 1 insertions(+), 0 deletions(-)
-
リポジトリの履歴から削除対象をすべて削除したこと、すべてのブランチがチェックアウトされたことをダブルチェックします。
-
リポジトリの状態が整ったら、ローカルでの変更をフォースプッシュして、GitHub Enterprise リポジトリと、プッシュしたすべてのブランチに上書きします。
$ git push origin --force --all > Counting objects: 1074, done. > Delta compression using 2 threads. > Compressing objects: 100% (677/677), done. > Writing objects: 100% (1058/1058), 148.85 KiB, done. > Total 1058 (delta 590), reused 602 (delta 378) > To https://hostname/ユーザ名/リポジトリ.git > + 48dc599...051452f master -> master (forced update)
-
機密データをタグ付きリリースから削除するため、Git タグに対しても次のようにフォースプッシュする必要があります。
$ git push origin --force --tags > Counting objects: 321, done. > Delta compression using up to 8 threads. > Compressing objects: 100% (166/166), done. > Writing objects: 100% (321/321), 331.74 KiB | 0 bytes/s, done. > Total 321 (delta 124), reused 269 (delta 108) > To https://hostname/ユーザ名/リポジトリ.git > + 48dc599...051452f master -> master (forced update)
-
GitHub Enterprise サイト管理者 に連絡し、GitHub Enterprise 上で、キャッシュされているビューと、プルリクエストでの機密データへの参照を削除するよう依頼します。
-
コラボレータには、 作成したブランチを古い (汚染された) リポジトリ履歴からリベースする (マージしない) よう伝えます。 マージコミットを 1 回でも行うと、パージで問題が発生したばかりの汚染された履歴の一部または全部が再導入されてしまいます。
-
一定の時間が経過し、
git filter-branch
に意図しない副作用がないことが確信できるようになったら、次のコマンドによって、ローカルリポジトリのすべてのオブジェクトが強制的に参照から外されガベージコレクトされるようにします (Git 1.8.5 以降を使用)。$ git for-each-ref --format="delete %(refname)" refs/original | git update-ref --stdin $ git reflog expire --expire=now --all $ git gc --prune=now > Counting objects: 2437, done. > Delta compression using up to 4 threads. > Compressing objects: 100% (1378/1378), done. > Writing objects: 100% (2437/2437), done. > Total 2437 (delta 1461), reused 1802 (delta 1048)
注釈: フィルタした履歴を、新規または空のリポジトリにプッシュして、GitHub Enterprise から新しいクローンを作成することによっても、同じことができます。
将来にわたって誤ったコミットを回避する
コミット対象でないものがコミットされるのを回避するためのシンプルな方法がいくつかあります。
- GitHub Desktop や gitk のようなビジュアルプログラムを使用して、変更をコミットします。 ビジュアルプログラムは通常、各コミットでどのファイルが追加、削除、変更されるかを正確に把握しやすくするものです。
- コマンドラインでは catch-all コマンド、
git add .
およびgit commit -a
は使用しないようにします。ファイルを個別にステージングするには、代わりにgit add filename
およびgit rm filename
を使用します。 - 各ファイル内の変更を個別にレビューしステージングするには、
git add --interactive
を使用します。 - コミットのためにステージングされている変更をレビューするには、
git diff --cached
を使用します。 これはまさに、git commit
で-a
フラグを使用しない限りにおいて生成される diff です。