0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Gitリポジトリの歴史改変(履歴を書き換える)

Posted at

概要

最近担当した、SVNからGitへの移行の中でぶち当たった壁について。
今回、SVNから変換したリポジトリをGitHubに移行したのですが、その際、変換したそのままではエラーになってpushできませんでした。
どうやらGitHubにアップロードできるファイルの上限は100MBだそうで、そこに引っかかったみたいです。端的に言えば、そのエラーの元凶たる100MB超えのファイルをリポジトリから削除すればいいのですが、こちらに関してGit標準の機能を用いて解決できましたのでご紹介します。

作業環境

OS:Windows10
利用ソフトウェア:Git for Windows, PowerShell

100MB超えのファイルがありエラーになる場合の対応について

まずは、100MBを超えたファイルをGitHubにアップロードしようとしてエラーになった場合の対処法についてお話ししておきましょう。方法は大きく2つあります。

  • LFSを使用してアップロードする
  • アップロードを諦めて削除する

私のケースではエラーの元凶になっているファイルがそもそも不要なものであったため、今回は削除することにしました。
ちなみに、LFSはLarge File Storageのことで、デカいファイルをリポジトリとは別の場所の保存しておいて、リポジトリにはそのポインタを保存しておくという手法でGitHubの制限を回避することができるサービスおよびツールのことを指します。

参考:Git Large File Storageについて

「Gitに上げたファイルを削除する」とはどういうことか

削除コミットすればいいんだろ?と思ったそこのあなた、残念。
履歴と一緒にファイルの実体もリポジトリにいるのでそれも全部削除する必要があります。
そんなときはfilter-branchコマンドを使用しましょう。
こいつは簡単にいうと「特定の条件に合致する履歴に対して任意の処理が行える」コマンドです。
これを使って、容量の大きいファイル入ったディレクトリがコミットされた履歴削除していきましょう。

構文
git filter-branch [オプション] '[実行したいコマンド]'... [git rev-listの引数]

よくつかうオプション

  • filter-branch最速
    indexをもとに検索するので一番早く結果が見られるようになるオプションです。(それでも遅いが)
git filter-branch --index-filter
  • tagにも紛れている時に使うオプション
    tag名を書き換えるオプションですが、実行するコマンドをcatのみとすることでtag名は書き換えないけど履歴は書き換えてくれるようになるみたいです。
git filter-branch --tag-name-filter cat
  • 絶対改変マン
    強制的に改変されます。
git filter-branch --force
  • ゴミ掃除も一緒に
    もし、容量の大きいファイルだけをコミットしていた場合、このオプションがないと空のコミットが履歴に残ってしまうそうです。入れておきましょう。
git filter-branch --prune-empty
  • git rmのオプション その1
    作業ツリーのファイルは削除しないオプションです。速度変わるんでしょうか?
git rm --chached /bigSizefilePath
  • git rmのオプション その2
    条件に一致しない時は何もしないオプションです。こちらは速度変わってきそう。
git rm --ignore-unmatch /bigSizefilePath

git rev-listの引数って?

構文の末尾に、「git rev-listの引数」というのがありますがこれは履歴の先祖を辿っていくためのコマンドです
filter-branchで対象を見つけた時に「ここの親コミットにもいるんだろ?」と探してくれます。
「どこからどこまで辿るか?」や、「ここからは辿れないやつ」といった条件が指定できますが、今回やりたいのは不要ファイル全部削除することですので、指定する引数は1つです。

git filter-branch --index-filter 'git rm -rf /bigSizefilePath' -- --all

一番後ろの「--all」ですね。
とりあえず全部辿れ!ということです。潔い。
ちなみに、その前の「--」はfilter-branchとの境だよーという記号です。
filter-branchの最後にrev-listの引数が引っ付けられるルールなのでここでfilter-branch終わり!ってことですね。

早速消してみよう

実行する時はローカルでやりましょう。(いきなりリモート、ダメ絶対。)
上記を参考に、もしくはこちらも参考にして自分の環境にあったコマンドを作成して実行してみましょう。時間はかかりますので余裕ある時か、別の端末で。

改変結果をリモートに反映

filter-branchで改変された歴史はそれ以前とはまったくの別物になります。故に原則pushできません。「別モンやでー」と怒られます。
なので別で空のリポジトリを作成してそちらに移行するか -fで強制的にpushしましょう。

git push -f

refs/original?なにそれ

filter-branchを実行した時に、実行前の状態がバックアップとして退避されます。それがrefs/originalです。
もしpushしようとした時にまだエラーが出ている場合、これが残っていることが原因の場面がほとんどでしょう。
.git/refs/originalのフォルダを削除すれば消えてなくなりますのでひとおもいにやっちゃってください。

あとしまつ

gitはお利口でreflogという操作ログを残しているのでそれを削除します。
不要なコミットなどが消されて、到達不可能になった現時点より古いログも削除するようにexpire-unreachableオプションもつけておきましょう。

git reflog expire --expire-unreachable=now --all

最後にガベージコレクションできれいさっぱり

git gc --prune=now

もし複数回filter-branchを行う場合は、この2点の掃除法を毎filter-branch行ってください。
さもないとなんか変な感じになってゴミが残ります。(よくわかってない)

まとめ

めっちゃおそい

参考文献

git filter-branchで過去の全てのcommitから画像ファイルの追加/変更をなかったことにしてリポジトリを軽量化する

※めちゃくちゃ助かりましたありがとうございました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?