396

この記事は最終更新日から5年以上が経過しています。

@go_astrayer

Git リポジトリに上がっているファイルを履歴ごと消すには?

仕事で必要になったので、ファイルを履歴ごと消す方法を試してみました。

ファイルを消しても履歴は残っている

例えば、1GB のバイナリファイルを Commit & Push したとします。
そして、それを git rm で削除したとしてもリポジトリの容量は減りません。

なぜか?

git rm は「ファイルが削除されたことにするコマンド」であって、「Git リポジトリ内に保存されている履歴を消すコマンド」ではないからです。

このサイトに書かれていますが、Git は「差分」ではなく「スナップショット」を保存して、「どのスナップショットを参照するのか?」をコミット単位ごとに切り替える仕組みです。

git rm は、この「どのスナップショットを参照するのか?」という情報を削除するコマンドです。
もし、データを丸ごと消したいのであれば、保存されている全ての「スナップショット」を消さなければなりません。

保存されている全ての履歴を消す

では、どうすれば全てのスナップショットを消せるのか?
それは、以下のコマンドを実行すればできます。

cmd.exe
git filter-branch --tree-filter "rm -f [消したいファイルパス]" HEAD
git filter-branch --tree-filter "rm -f -r [消したいディレクトリパス] " HEAD

上が指定したファイルを消すためのコマンド、
下が指定したディレクトリ以下を消すコマンドです。(下は パスが / で終わる必要があります。)

コマンド実行後、

cmd.exe
Rewrite 30d202caa00757219fc7e1d2a0220c1501bc38b9 (9/9)
Ref 'refs/heads/master' was rewritten

このようなログが出たら成功です。

これで履歴がすべて削除されました……と言いたいところですが、リポジトリにまだ情報が残りっぱなしになっている場合があるので、以下のコマンドでリポジトリを最適化します。

cmd.exe
git gc --aggressive --prune=now

あとは、これをリモートリポジトリに Push するだけです。
が、普通に Push しようとすると、

cmd.exe
To C:\Users\*****\Workspace\Git\Remote
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'C:\Users\*****\Workspace\Git\Remote'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

このようなエラーが出て Push 出来ません。
どうやら、ハッシュが別物になっていてコンフリクトしてしまっているようです。

ですので、Push する際は

cmd.exe
git push -f

のように、-f オプションを使って強制的に上書きすると、無事に Push 出来ます。

検証時に発生した問題の数々

自分が検証した際に発生した数々の問題を載せておきます。

A previous backup already exists in refs/original/

git filter-branch を使って、別のファイルを消そうとしたところ、以下の様なエラーが出て消せないことがありました。

cmd.exe
A previous backup already exists in refs/original/
Force overwriting the backup with -f
rm:cannnot remove 'C:/Users/*****/Workspace/Git/Remote/.git-rewrite/backup-refs': Permission denied
rm: cannnot remove directory 'C:/Users/*****/Workspace/Git/Remote/.git-rewrite':Directory not empty

これは refs/original/ が原因で処理が実行できていないみたいです。
もし、このエラーが出たら、.git-rewrite フォルダを削除した上で

cmd.exe
git update-ref -d refs/original/refs/heads/master

このコマンドを実行すると、refs/original/ がなくなります。

リポジトリのサイズが減らない

git gc を実行してもリポジトリのサイズが減っていない場合がありました。
その場合、一度ローカルリポジトリを削除してクローンを作りなおした上で git gc を実行するとサイズが減ります。

また、その場合はリモートリポジトリのサイズも減っていないので、リモートリポジトリに対して git gc を行う必要があります。

リモートリポジトリから直接履歴を消してみた

git gc がリモートリポジトリに対してできるのであれば、出来るのではないか?と思って検証してみました。

結果、リモートリポジトリから直接消すことも出来ました。
リモートリポジトリなので、Push することなく反映されていました。

ただし、その後ローカルリポジトリで Pull してみたところ、消したはずのファイルがなぜか消えずに残っていました。
ログを確認してみたところ、

Merge branch 'master' of C:\Users\*****\Workspace\Git\Remote

ファイル名:delete_file.txt
状態   :追加

のようにマージがかかっていて、そこで消したはずのファイルが追加されていました。
どうやらローカルリポジトリでそのファイルを追加したことになっているらしいです。

履歴を編集したら、Pull ではなく Clone し直す

リモートリポジトリから直接履歴を消した状態、すなわち……

「リモートリポジトリでは履歴が消えていて」
「ローカルリポジトリでは履歴がまだが残っている」

という状況はリモートリポジトリから直接消す以外に、複数人の運用でも普通に発生し得る状況です。
例えば、

  1. Aさんが履歴を消して、リモートリポジトリに Push する。
  2. Bさんがそのリモートリポジトリを Pull する。

とした場合、Aさんがリモートリポジトリに Push した時点でそのような状況になります。

ですので、基本的に履歴を消した場合、ローカルリポジトリに反映する際は Pull ではなく Clone で複製し直すのが良さそうです。

参考サイト

Git のさまざまなツール - 歴史の書き換え
http://git-scm.com/book/ja/v1/Git-%E3%81%AE%E3%81%95%E3%81%BE%E3%81%96%E3%81%BE%E3%81%AA%E3%83%84%E3%83%BC%E3%83%AB-%E6%AD%B4%E5%8F%B2%E3%81%AE%E6%9B%B8%E3%81%8D%E6%8F%9B%E3%81%88

git最強のオプション filter-branch
http://qiita.com/Spring_MT/items/f60c391b5dbf569a1d12

git filter-branchで大事なデータを消去・上書き
http://qiita.com/wnoguchi/items/62f5e64ef2ae14b4f0ee

レポジトリからディレクトリを削除
http://d.hatena.ne.jp/ramen26/20111215/1323958593

リモートブランチにコミットしたファイルをなかったことにする方法
http://hack.aipo.com/archives/3932/

gitのユーザ名やメールアドレスをコミット後に書き換える。
http://koseki.hatenablog.com/entry/20081115/gitFilterBranch

ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
go_astrayer
都内某所で常に癒しを求めて働くゲームプログラマの端くれ。時間と気力のあるときに覚書みたいなものを書き残しています。今は仕事で UE4 を使ってるので、 UE4 がメインです。

コメント

・リモートリポジトリから直接履歴を消してみた。
・履歴を編集したら、Pull ではなく Clone し直す。

の2つを追加しました。
(編集履歴はドジって中途半端な状態になってます……。)

0

filter-branchもそうですが、push済みのbranchをrebaseした時も同じような問題が起きますね。

その際は、push -fで強引に変更を押し込むか、自分がcloneし直して、当該変更を破棄するか、選ぶことになりますが、
push -fは関係者全員にcloneし直してもらう必要がでてきてしまうため、何か、但し書きがあったほうがよいかもしれません。

0
あなたもコメントしてみませんか :)
ユーザー登録
すでにアカウントを持っている方はログイン
記事投稿イベント開催中
Qiita 10周年記念イベント - 10年前の自分に伝えたい、勉強しておきたかった技術
~
Qiita 10周年記念イベント - 10年後のために今勉強しておきたい技術
~