.gitignore
し忘れて他人に見えちゃマズいファイル(パスワードをベタ書きしたファイルや AWS_SECRET_ACCESS_KEY
を書いたファイルとか)を git commit
しちゃった!そんなときは
$ git reset --hard HEAD~
すればすぐ何もなかったことにできます。
が!そこで気付かずに GitHub へ git push
してしまった!こうなると容易に何もなかったことにはできません。
この記事では、こういうときに何もなかったことにする方法を紹介します。
そのデータを無効にする
特に Public Repository の場合はすでにそのデータが他人の目に触れていた…ということも十分ありえます。AWS_SECRET_ACCESS_KEY
なんかは取得用のクローラが存在するとも聞きます。ので、まずは不正利用されても影響が出ないように、__パスワードの書き換えやトークンの無効化__を施しましょう。
(この時点でもう何もなかったことになってない気がする)
git の履歴から該当のファイルを消す
git reset
と git filter-branch
2つの方法があります。
git reset
(2015-12-29 15:00 追記)
git reset
だとセンシティブファイル以外の作業履歴もすべて消去されてしまうので、それらを残しておきたい場合は後述 git filter-branch
でコミットを書き換えるようにしてください。
(追記終わり)
該当ファイルを git commit
してすぐ気づいた (3コミット以内) なら、まだ git reset
で消せます。
$ git reset --hard HEAD~2 # 消すコミットの数
git filter-branch
気づいたのはそこから何コミットもしたあと…だと git reset
でそこまでの履歴を全部消すのは現実的ではありません。そんな時に役立つのが git filter-branch
です。filter-branch
は普段見慣れない(であってほしい)ですが、__大量のコミットを機械的に書き換える__コマンドです。今回みたいにファイルを消す以外にも、リポジトリ全体のコミットオーサーの書き換えとかも一発でできます。
現在作業中の Pull Request に間違えてコミットしてしまったときは、そのブランチにだけ filter-branch
を適用するので以下の様なコマンドになります。/path/to/file_to_rm
は消したいファイル、1234abcd
はブランチ分岐元のコミットのハッシュ値、branch_name
は操作対象のブランチ名です。
$ git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch /path/to/file_to_rm' \
--prune-empty -- 1234abcd...branch_name --first-parent
ここで 1234abcd...branch_name --first-parent
は、git rev-list
というコマンドの引数と同等であり、書き換えるコミットを指定しています。実際に
$ git rev-list 1234abcd...branch_name --first-parent
を叩いてみれば、Pull Request の Commits に出てくるのと同じコミットハッシュの一覧が得られます。
他のブランチすべてを含めた全体から削除するときは以下のようにします。
$ git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch /path/to/file_to_rm' \
--prune-empty --tag-name-filter cat -- --all
これで履歴から該当ファイルが完全に取り除かれます。
git push -f
日頃忌避される、というか何もないときは__絶対に__叩いてはいけない git push -f
をここで使います。これしかやりようがないので…。GitHub のリモートリポジトリ上の履歴を書き換えるためです。
$ git push -f origin branch_name
(2015-12-30 15:10 追記) ローカルリポジトリから完全に痕跡を消す
ここまで reset
や filter-branch
した内容ですが、実はローカルリポジトリに reflog という形で作業履歴が残ってしまっています。
git最強のオプション filter-branch - Qiita
の記事にもあるように、直接コミットハッシュを選択すると中身が見えてしまいます…これらも消しましょう。
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --aggressive --prune=now
これで、ローカルリポジトリからは完全にセンシティブ情報が抹消されました。
(コメントでご指摘くださった @sachin21 さんありがとうございました )
他のチームメンバに手元のリポジトリを取得し直すようお願いする
他のチームメンバの手元にチェックアウトしてあるローカルリポジトリの履歴と、 GitHub 上の履歴は別物になってしまいました。なので、(自分しか触ってないような)トピックブランチならまだしも filter-branch
で master 含む全体を書き換えた場合は、再度 git clone
し直すなりブランチ消して fetch
し直すよう他のメンバーにお願いします。
GitHub のサポートに連絡する
これで GitHub 上で history や Pull Request のページからは該当ファイル・該当コミットにアクセスする道がなくなったので一見一段落に見えます。
…が、実はコミット単体のページは GitHub 上にキャッシュとして存在しており、https://github.com/dtan4/terraforming/commit/64f752dd8d93c5b7326175a69cece9e742fd010b のように__コミットハッシュを直接指定すると普通に閲覧できます__。
そこで、https://github.com/contact から GitHub のサポートにキャッシュを消すよう連絡する必要があります。勿論英語です。名前とメールアドレスは既に入っているので、
- Subject:
Request to remove page caches including sensitive data
- Body:
Hello,
We accidentally commited sensitive data into the repository.
According to https://help.github.com/articles/remove-sensitive-data/, we already ran `git filter-branch` on the branch and force-pushed.
Sensitive data was disappeared from git tree.
I'm sorry to trouble to you, but could you remove cached views of these commits from github.com?
*Target repository*
https://github.com/user/repo
*Commit hashes to be removed*
...
...
...
...
Thanks,
Daisuke
のように送ればよいです。
ここで記載するのはコミットハッシュだけで良いです。一応と思って Pull Request の URL も載せると Pull Request も一緒に消されます(実話)。
そうすれば、わりとすぐに(早ければ一時間かからないかも)「消しましたよ!」というメールが届きます。確認できれば、これで本当の一件落着です。お疲れ様でした
おわりに
GitHub 上からセンシティブなファイルを削除する方法を紹介しました。この記事のお世話にならないよう、日頃から__センシティブなファイルは .gitignore
する__、__コミット前に diff を見る__ことを忘れないようにしましょう。
REF
-
Remove sensitive data - User Documentation
- GitHub 公式 こういう状況に陥った場合の対処方法
- GithubでPull Requestは絶対消せない。ヤバい\(^o^)/オワタ - アジャイルSEの憂鬱
-
はてなブックマーク - GithubでPull Requestは絶対消せない。ヤバい\(^o^)/オワタ - sinsokuのブログ
- サポートに連絡したらすぐ消してくれるとのこと
- Git - 歴史の書き換え (filter-branch)