GitHubのファイルサイズ制限に引っかかったときの対処法
GitHubのファイルサイズ制限に引っかかったときの対処法
注: 自分の備忘のため Github Copilot (Claude Sonnet 4.6) と作成した内容です。 Github Copilot による記述も含まれます。
概要
GitHubはファイルサイズの上限を 100MB に設定している。一度でもその上限を超えるファイルをコミットしてしまうと、後続のコミットでそのファイルを削除しても Gitのオブジェクトデータベースには残り続ける。そのため、単純に git rm してコミットするだけでは push 時のエラーが解消しない。
本記事では、誤ってコミットした大容量ファイルをGitの履歴から完全に除去して push を成功させる手順をまとめる。
エラーの内容
remote: error: File some/path/BigFile.exe is 106.16 MB; this exceeds GitHub's file size limit of 100.00 MB
remote: error: GH001: Large files detected.
! [remote rejected] your-branch -> your-branch (pre-receive hook declined)
error: failed to push some refs to 'https://github.com/your/repo.git'
原因
Gitは スナップショット方式でファイルを管理する。git rm はあくまで「次のコミットでそのファイルを追跡しない」という指示であり、過去のコミットオブジェクトは手つかずのまま残る。
push の際、GitHubはローカルに存在する全オブジェクトを受け取るため、履歴のどこかに大容量ファイルが存在するだけで制限に引っかかる。
対処手順
Step 1. 大容量ファイルがどのコミットで追加されたか特定する
# 全履歴から対象ファイルが追加されたコミットを探す
git log --all --oneline --diff-filter=A -- "*.exe"
出力例
a1b2c3d Add some files
ファイルの正確なパスを確認する
git show a1b2c3d --name-only | grep '\.exe$'
出力例
projects/my-app/installer/setup-1.0.0.exe
Step 2. .gitignore に追加して再発を防ぐ
まず今後のコミットで同種のファイルが追跡されないよう .gitignore に追記する。
echo "*.exe" >> .gitignore
git add .gitignore
git commit -m "Add *.exe to .gitignore"
Step 3. git filter-branch で履歴からファイルを除去する
補足:
git filter-branchは公式に非推奨とされており、代替としてgit-filter-repoが推奨されている。ここでは追加インストール不要なfilter-branchを使う手順を示す。
origin/your-branch が指すコミット(例: e4f5a6b)より後の履歴に対して filter-branch を実行する。
FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch "projects/my-app/installer/setup-1.0.0.exe"' \
--prune-empty -- <origin/your-branchのコミットハッシュ>..HEAD
オプションの意味
| オプション | 説明 |
|---|---|
--index-filter | ツリーをチェックアウトせずインデックスのみ操作(高速) |
--ignore-unmatch | ファイルが存在しないコミットでもエラーにしない |
--prune-empty | 変更が空になったコミットを除去 |
--force | 既存の refs/original/ バックアップを上書き |
<hash>..HEAD | リモートとの差分コミットのみを対象にする(全履歴を書き換えずに済む) |
成功すると Ref 'refs/heads/your-branch' was rewritten と表示される。
注意:
--ignore-unmatchを忘れると、対象ファイルが存在しないコミットでエラーが発生し処理が止まる。
Step 4. 古いオブジェクトをGCで物理削除する
filter-branch で履歴を書き換えても、古いオブジェクトはすぐには消えない。git gc で完全に除去する。
git reflog expire --expire=now --all
git gc --prune=now --aggressive
コマンドの意味
reflog expire --expire=now:reflogの参照を即時失効させ、古いオブジェクトへの参照を切るgc --prune=now:参照されなくなったオブジェクトを即時削除--aggressive:より強力な圧縮を実施(時間はかかるが確実)
Step 5. force push する
git push --force
履歴が書き換わっているため、通常の push ではなく --force が必要である。
まとめ(コマンド一覧)
# 1. 対象ファイルが含まれるコミットを特定
git log --all --oneline --diff-filter=A -- "*.exe"
git show <コミットハッシュ> --name-only | grep '\.exe$'
# 2. .gitignore に追加
echo "*.exe" >> .gitignore
git add .gitignore && git commit -m "Add *.exe to .gitignore"
# 3. 履歴からファイルを除去
FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch "<ファイルの正確なパス>"' \
--prune-empty -- <origin/your-branchのコミットハッシュ>..HEAD
# 4. 不要オブジェクトを物理削除
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# 5. push
git push --force
--ignore-unmatch オプションにより、パスが間違っていてもエラーにならない。処理が Ref 'refs/heads/your-branch' is unchanged で終わっている場合は、ファイルパスの指定ミスを疑う。
# パスが間違っている場合の出力(rewrittenではなくunchanged)
WARNING: Ref 'refs/heads/your-branch' is unchanged
正しくファイルを除去できた場合の出力:
rm 'projects/my-app/installer/setup-1.0.0.exe'
Ref 'refs/heads/your-branch' was rewritten
git gc を省略してしまう
filter-branch 後に git gc を実行しないと、オブジェクトDBに大容量ファイルが残ったままになる。push のデータ量が減らず、再度 GitHub の制限に引っかかる場合がある。
代替ツール git-filter-repo
git filter-branch は処理が遅く、意図しない副作用が起きやすいため、公式は git-filter-repo の使用を推奨している。
# インストール
pip install git-filter-repo
# 実行(こちらの方がシンプルかつ高速)
git filter-repo --path "<ファイルの正確なパス>" --invert-paths --force
ただし git-filter-repo はリモートの設定を削除するため、実行後に再設定が必要である。
git remote add origin https://github.com/your/repo.git
git push --force
# 実行ファイル・インストーラ
*.exe
*.dmg
*.pkg
*.msi
# アーカイブ
*.zip
*.tar.gz
# 動画・音声
*.mp4
*.mov
*.wav
また、コミット前にステージされたファイルサイズを確認する習慣も有効である。
# ステージ済みファイルの一覧とサイズを確認
git diff --cached --stat