Gitでプッシュしたらやばいファイルをプッシュしてしまった時の対処法
みなさんenvファイルなどのあげたらやばいファイルをうっかりリモートのブランチにプッシュしてしまったという経験ありませんでしょうか?
僕は業務を始めたばかりの時に何回かやらかしました。
すぐ気づいた場合はgit rebase -iして、コミットをdropして、フォースプッシュすればなんとかなりますが、プッシュしたらやばいファイルが混入しているのに気づかずにそのまま作業を続行してしまった場合、Gitはファイル自体を消すのは簡単ですが、履歴そのものを消すのは結構大変です。
今回はGitのリポジトリに上がってるファイルを履歴ごと抹消する方法について解説します。
対処法
結論からいうと、filter-branchというコマンドを使えば履歴を抹消できます。
Gitの公式曰く最強のオプションらしいですw
git filter-branch --tree-filter "rm -f 消したいファイルのパス" HEAD
git filter-branch --tree-filter "rm -f -r 消したいディレクトリのパス" HEAD
filter-branchとはどのようなコマンドかといいますと、大量のコミットの書き換えを機械的に行うコマンドです。
公式では、大量のコミットと説明されていますが、今までの全コミットに対してという解釈で間違いなさそうです。
filter-branchの横にある--tree-filterをつけると、プロジェクト内の各チェックアウトに対して指定したコマンドの実行を行い、結果を再コミットすることができます。
どういうことなのか、ここまでの流れをまとめてみました。
- filter-branch・・・今までの全部のコミットに対して
- --tree-filter・・・プロジェクト内のチェックアウトの中から該当するファイルorディレクトリを強制的に抹消
- HEAD・・・今自分が作業しているブランチ
言語化して改めて思ったのですが、結構エグいことをやっていますね。
簡単にまとめるなら、全てのコミットの履歴の中から特定のファイルorディレクトリの存在を最初からなかったことにしているわけです。
このようなログが出たらうまくいっています。
Ref 'refs/heads/master' was rewritten
ファイルと履歴が抹消できたからといって油断は禁物です。
Gitでは、過去の操作を参照ログとして記録しています。
バックアップポイントを辿れば元に戻せてしまうわけです。
つまり、リポジトリ内の操作履歴やバックアップポイントも消す必要があります。
以下のコマンドを打つとリポジトリ上でガベージコレクションを実行し、不要なデータや履歴の削除とリポジトリの最適化を行いましょう。
git gc --aggressive --prune=now
最後はフォースプッシュすれば、無事対象のファイルorディレクトリの存在を歴史から消し去ることができます。
git push -f 対象のブランチ
まとめ
envなどには、DBの接続情報やAPIのKEYなど外部に漏れたらえらいことになる情報が多く書き込まれているので、そういったファイルをうっかりGitの管理下に含めてしまったという時には非常に有効な手段です。
→最初からgitignoreにちゃんと書いとけよって話ですがw
今回の対処法は過去のコミットやリモート上の同じブランチで作業している人にも多大な影響を与えます。
業務などで実際に行う場合は必ず他の人の了承を得てから行うようにしましょう。
おまけ
カテゴリーがバックエンドになっていますが、「Gitはバックエンドじゃないよねー」とか思いつつカテゴリーをバックエンドにした理由は、操作するときにCUIで操作するし、、、無理矢理感は否めないけどバックエンドにしとくかという理由でカテゴリーをバックエンドにしました。
GUIでもできんじゃんって言われたらそれまでですが、まあいんでねって感じです。