Git LFSからファイルを削除してStorageを解放する
Git(というかGithub)の機能にGit Large File Storage(以下Git LFS)というものがあります。
GitHubでは、1ファイル100MB(50MBで警告)という制限があります。
ではそれを超過したファイルはどうするのか?というと、Git LFSという場所に保管します。
しかし、Git LFSはストレージ容量1GB、転送量1GB/月という制限があり、それを超えるとデータパックを購入する必要があり、また過去のコミットのファイルはいつまでも保持されるため、すぐに足りなくなってしまいます。
私はちょっと大きいファイルをGit LFSで管理していたのですが、Git LFSで管理する必要がなくなって削除したいな~と思い、方法を調べてみたのですが、かなり面倒だったので、備忘録として書いておきます。
原則削除できない
まず、Githubには、Git LFSのファイルを削除するような機能はありません。
Git LFSのStorageを解放するためには、過去のコミットまで遡ってファイルを削除する必要があります。
しかし
Git LFS からファイルを削除した後でも、Git LFS オブジェクトはそのままリモートストレージに存在し、Git LFS ストレージ容量に対するカウントも継続します。
Git LFS オブジェクトをリポジトリから削除するには、リポジトリを削除して再作成します。 リポジトリを削除すると、関連する Issue、Star、フォークもすべて削除されます。
ファイルを Git Large File Storage から削除する - GitHub Docs
と公式ドキュメントにあるように、過去のコミットからファイルを削除したとしても、リポジトリがある限り、ファイルは残り続けます。
つまり、Git LFSのStorageを解放するには
- Git LFSの追跡を無効にして
- 過去のコミットからGit LFSに保存しているすべてのファイルを削除して
- Githubのリポジトリを削除・新規作成
する必要があります。
めんどくさ!
ただ、このままずっと使わないLFSにお金を払い続けるのは嫌なので、手順を紹介したいと思います。
Git LFSのStorageを解放する
今回の環境はWindows 11を使用していますが、MacOSやLinuxでも同様にできます。
後述するBFGはMacだとBrewで簡単に入るらしいので、Macの方は調べてみてください。
Git LFSの追跡を無効化する
.gitattributes
に書いてある追跡ルールをすべて削除します(ファイルごと消してもOK)
また、Git LFSで管理していたファイルも削除します(100MB以下であれば削除不要)
そしてそれらの変更をコミットし、リモートにpushします。
コミットからファイルを削除する
それでは過去のコミットからファイルを削除します。
git filter-repo
というコマンドでもできるのですが、ディレクトリの指定やその他いろんな機能があり便利なBFG Repo-Cleanerを今回は使用します。
(ちなみに公式ドキュメントでもおすすめされています)
BFGはJava 8以降が導入されている必要があるので、入っていなければインストールします。
まずはBFGを公式サイトよりダウンロードします。
適当なフォルダを作り、そこにダウンロードしたBFG(今回は bfg-1.14.0.jar
)を入れます。
次に
git clone --mirror https://github.com/hoge/fuga.git
を実行し、mirrorオプションを付けてリポジトリをクローンします。
lfs-clean/
├─ fuga.git/
├─ bfg-1.14.0.jar
それではコミット履歴からファイルを削除します。
以下のコマンドを実行します。
// ファイルを消す場合
java -jar .\bfg-1.14.0.jar --delete-files fileA fuga.git
// フォルダごと消す場合
java -jar .\bfg-1.14.0.jar --delete-folders folderA fuga.git
複数指定する場合は "{fileA,fileB,fileC}"
のように指定します。
BFG run is complete! When ready, run: git reflog expire --expire=now --all && git gc --prune=now --aggressive
と表示されれば完了です。
Protected commits
Protected commits
-----------------
These are your protected commits, and so their contents will NOT be altered:
* commit xxxxxxxx (protected by 'HEAD') - contains 100 dirty files :
- example/test.webm (132 B )
- example/test2.webm (132 B )
- ...
WARNING: The dirty content above may be removed from other commits, but as
the *protected* commits still use it, it will STILL exist in your repository.
Details of protected dirty content have been recorded here :
C:\Users\hoge\Desktop\lfs-clean\fuga.git.bfg-report\xxxx-xx-xx\xx-xx-xx\protected-dirt\
If you *really* want this content gone, make a manual commit that removes it,
and then run the BFG on a fresh copy of your repo.
このように、Protected commitsにファイルが表示された場合、削除されずに残ったファイルがあります。
Git LFSの追跡を無効化する
の項で削除対象のファイルを削除していなかった場合、GitのHEADにファイルが残っているためこのような警告が出ます。
BFGでは、HEADにあるファイルは削除されない(Protectされる)仕様になっているためです。
意図してファイルを削除しなかった場合は、この警告は無視して大丈夫です。
新しいリポジトリにpushする
あらかじめGithubで新しいリポジトリを作成しておきます。
今回は https://github.com/hoge/piyo.git
が新しいリポジトリとします。
cd fuga.git
git remote remove origin
git remote add origin https://github.com/hoge/piyo.git
git reflog expire --expire=now --all | git gc --prune=now --aggressive
git push --set-upstream origin master
これで新しいリポジトリに、Git LFSが削除されたコミットがpushされます。
古いリポジトリを削除する
古いリポジトリは、必要なIssueやWikiなどを手動で移行し削除します。
これでGit LFSのStorageが解放されます。
そもそも
Git LFSは安易に使うもんじゃないですね。
デカいファイルは社内サーバーやDropboxとかのストレージで管理しましょう。
今回の場合は100MBもないファイルをLFSにあげてたみたいで、普通にアホでしたね。
Git BucketなどにはLFSからファイルを消す機能があるみたいで、Githubもつけてくれよ、とは思いました。
おわり
悲しいね
Discussion