経緯

とあるリポジトリでファイルを LFS 管理しようとしたところ、下記のように LFS が無効化されている、というエラーが発生しました。

 git push -u origin git-lfs-conflict
Uploading LFS objects:   0% (0/1), 0 B | 0 B/s, done.
batch response: Git LFS is disabled for this repository.
error: failed to push some refs to 'https://github.com/ganyariya/playground.git'

Imgur

url: https://docs.github.com/ja/billing/managing-billing-for-your-products/managing-billing-for-git-large-file-storage/about-billing-for-git-large-file-storage#included-bandwidth-and-storage-per-month
title: "Git Large File Storage の支払いについて - GitHub Docs"
description: "新しい課金プラットフォームを使用する Git Large File Storage の請求について説明します。"
host: docs.github.com
favicon: https://docs.github.com/assets/cb-345/images/site/favicon.png
image: https://docs.github.com/assets/cb-345/images/social-cards/default.png

ganyariya の LFS 設定を確認したところ、 9.54 GB がどうやらすでに格納されているらしく、これのせいで LFS ファイルの新しいファイルが push できないのではと考えました。

そのため、 LFS を利用しているリポジトリの LFS ファイルの履歴を完全に削除し、新しい LFS ファイルが push できるようになるか確認します。

ganovel という Unity プロジェクトの過去のコミット履歴から LFS ファイルを消す

LFS ファイルが存在するリポジトリですが、 ganovel という Unity プロジェクトです。 https://github.com/ganyariya/ganovel Imgur Imgur

もともとは NotoSansJP-Regular.otf を LFS 管理していましたが、LFS の枠を食いつぶしたため LFS からすでに最新コミットからは除外済みでした。

過去コミットに LFS ファイルが存在する場合それらはずっと GitHub の LFS サーバに残り続ける

url: https://docs.github.com/ja/repositories/working-with-files/managing-large-files/removing-files-from-git-large-file-storage
title: "ファイルを Git Large File Storage から削除する - GitHub Docs"
description: "リポジトリに Git LFS をセットアップしてあれば、Git LFS からは、すべてのファイルを削除することも、ファイルのサブセットを削除することもできます。"
host: docs.github.com
favicon: https://docs.github.com/assets/cb-345/images/site/favicon.png
image: https://docs.github.com/assets/cb-345/images/social-cards/default.png
url: https://zenn.dev/minato86/articles/fb4a06c939c52e
title: "Git LFSからファイルを削除してStorageを解放する"
host: zenn.dev
image: https://res.cloudinary.com/zenn/image/upload/s--z_qy0tuj--/c_fit%2Cg_north_west%2Cl_text:notosansjp-medium.otf_55:Git%2520LFS%25E3%2581%258B%25E3%2582%2589%25E3%2583%2595%25E3%2582%25A1%25E3%2582%25A4%25E3%2583%25AB%25E3%2582%2592%25E5%2589%258A%25E9%2599%25A4%25E3%2581%2597%25E3%2581%25A6Storage%25E3%2582%2592%25E8%25A7%25A3%25E6%2594%25BE%25E3%2581%2599%25E3%2582%258B%2Cw_1010%2Cx_90%2Cy_100/g_south_west%2Cl_text:notosansjp-medium.otf_37:%25E5%25B7%25B3%25E6%25B3%25A2%25E3%2581%25BF%25E3%2581%25AA%25E3%2581%25A8%2Cx_203%2Cy_121/g_south_west%2Ch_90%2Cl_fetch:aHR0cHM6Ly9zdG9yYWdlLmdvb2dsZWFwaXMuY29tL3plbm4tdXNlci11cGxvYWQvYXZhdGFyL2Y2Y2Y5MmRhNmMuanBlZw==%2Cr_max%2Cw_90%2Cx_87%2Cy_95/v1627283836/default/og-base-w1200-v2.png

過去コミットに LFS ファイルが存在する場合、そのときの LFS ファイルはずっと LFS サーバに残り続けます。 そして、それらファイルは GitHub LFS の無料枠にも当然加算されてしまいます。

悲しいことに git の過去コミットから LFS ファイルの存在を抹消したとしても、 該当リポジトリの LFS ファイルは GitHub LFS サーバに残り続けてしまうようです。 つまり、完全に LFS ファイルを消すには他のリポジトリへ移動するしかありません…。

そのため、今回の ganovel プロジェクトについては

  • 新しいリポジトリ gnovel に移動する
  • 過去コミットから LFS ファイルはすべて消す
    • ただし、コミット履歴自体はそのまま残す
    • C# などの変更履歴は残したいため
  • ganovel リポジトリは GitHub から削除する を目的とします。

過去履歴から LFS ファイルをすべて消す

まずは作業するまえにバックアップを取っておきます。 新しい gnovel というリポジトリで作業しますが、なにかあったら悲しいです。 ganovel というリポジトリの zip ファイルをダウンロードしておきます。

url: https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository
title: "リポジトリからの機微なデータの削除 - GitHub Docs"
description: "機密データをリポジトリの履歴から削除できるのは、あなたがクローンを行った全員と慎重に調整することができて、副作用を管理する意思がある \"場合のみ\" です。__"
host: docs.github.com
favicon: https://docs.github.com/assets/cb-345/images/site/favicon.png
image: https://docs.github.com/assets/cb-345/images/social-cards/default.png

git filter-repo で作業することにします。

はじめに ganovel を gnovel という新しいリポジトリとして clone します。 そして、 remote origin を ganyariya/gnovel に変更します。

❯ git clone https://github.com/ganyariya/ganovel gnovel
cd gnovel
❯ git remote remove origin
git remote add origin https://github.com/ganyariya/gnovel.git

履歴を確認すると、 2 つのファイルが LFS ファイルとして過去コミットに登録されています。

❯ git lfs ls-files --all
34b41de3f6 - Assets/Fonts/NotoSansJP-Regular.otf
14fa70c4e9 - Assets/Fonts/NotoSansJP-Regular SDF.asset

git-filter-repo を brew で導入します。

brew install git-filter-repo
❯ git filter-repo --path 'Assets/Fonts/NotoSansJP-Regular.otf' --invert-paths --force
git filter-repo --path 'Assets/Fonts/NotoSansJP-Regular SDF.asset' --invert-paths --force
git filter-repo --strip-blobs-bigger-than 1M --force

上記で過去コミットから otf, .asset を削除します。 --force をつけるのは、つけないままだと Aborting: Refusing to destructively overwrite repo history since this does not look like a fresh clone. が表示されるためです。 今回はすでに ganovel を gnovel として clone 済みかつ、 remote 設定も gnovel に書き換えています。 そのため、直接 force で同じローカルリポジトリで作業しています。

過去コミットから消えたことが確認できました。

gnovel on  main [☁️ ]
❯ git lfs ls-files --all

履歴を強引に書き換えているため、当然ですがコミットログは書き換わってしまいますね。 Imgur

gnovel を remote に push しましたが、しっかり消えていますね。 同一のコミットですが、そもそも NotoSansJP-Regular.otf がなかったことになっています。 Imgur

最新コミットからも消えています。 Imgur

どういうわけか DynamicNotoSans.asset も消えていますね… 🤔 git filter-repo --strip-blobs-bigger-than 1M --force が原因ですかね…

起動すると Unity プロジェクトがうまく動作しなかったためやり直す

Imgur

Imgur

ganovel (=ganovel2) はちゃんと TESTING に 5 つファイルがあります。 しかし、 gnovel 側は TESTING にファイルが 4 つしかありません。 また、それ以外にも RenderGroups におきかえる、など必要な過去の情報が消えています。

過去を書き換える

git clone https://github.com/ganyariya/ganovel.git gnovel-temp
cd gnovel-temp

git filter-repo --path "Assets/Fonts/NotoSansJP-Regular.otf" --invert-paths --force
git filter-repo --path "Assets/Fonts/NotoSansJP-Regular SDF.asset" --invert-paths --force

上記でうまくいきました。 おそらく、 git filter-repo --strip-blobs-bigger-than 1M --force コマンドを最初の試みで実行してしまったせいで LFS 以外のありとあらゆるものが消えてしまったことが原因でした。 LFS 管理だったファイルを直接記載することで

url: https://github.com/ganyariya/gnovel/tree/ba0c0b4ad3966e54a0698ca3cd12a3f15b557b90
title: "GitHub - ganyariya/gnovel at ba0c0b4ad3966e54a0698ca3cd12a3f15b557b90"
description: "Contribute to ganyariya/gnovel development by creating an account on GitHub."
host: github.com
favicon: https://github.githubassets.com/favicons/favicon.svg
image: https://opengraph.githubassets.com/c0a76163898828c37665e5320838f7780375a33ac61d5486b3051e9d1ec864ca/ganyariya/gnovel

上記に履歴を書き換えた gnovel の URL を記載しました。

まとめ

  • GitHubLFS においては過去コミットのファイルも保存されている
    • 特定コミットをダウンロードしたり、 fork するために必要であるため
    • これら過去コミットのファイルを削除する方法なく、リポジトリごと消すしかない
  • git filter-repo などのツールを使うことで、過去コミットから該当のファイルを消せる
    • コミットを書き換える操作であるため、ハッシュはすべて書き変わる
    • 同じリポジトリに push するのであれば force push ですべて書き換えるしかない

つぎにやること

2, 3 日様子をみて問題なければ ganovel を GitHub から削除します。

Dynamic Font の設定はそれはそれとして見直したいため、LFS とは関係ないですが修正します。