跳到正文
DEV Community· Libme·· 3 小時前AI 評分49

GitHub GH001「File Exceeds 100 MB」推送遭拒:用 filter-repo 或 LFS 修復

GitHub Push Rejected: "File Exceeds 100 MB" (GH001) — Fix It With filter-repo or LFS

AI 導讀

GitHub 的 GH001 錯誤會檢查推送中所有提交的檔案;即使在新提交刪除超大檔案,舊提交仍會令推送遭拒。若檔案不應留在儲存庫,可用 git filter-repo 重寫歷史;若檔案需要版本管理,則可用 git lfs migrate 將歷史中的檔案移至 Git LFS。兩種方法都會重寫歷史,Git LFS 另有儲存及頻寬配額。

正文

Deleting the big file in a new commit does not fix this error, because Git still carries the file in an earlier commit and GitHub rejects the whole push. You have to either rewrite history so the blob never existed (git filter-repo), or move the file to Git LFS and rewrite history so old commits point to LFS instead (git lfs migrate). Pick filter-repo if the file should not be in the repo at all, LFS if it should be versioned but is legitimately large.

What does the error actually look like?

remote: error: Trace: 3f1c...
remote: error: See https://gh.io/lfs for more information.
remote: error: File data/embeddings.bin is 143.21 MB; this exceeds GitHub's file size limit of 100.00 MB
remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com.
 ! [remote rejected] main -> main (pre-receive hook declined)
error: failed to push some refs to 'github.com:you/project.git'

As of late 2026, GitHub prints a warning for files over 50 MB and blocks pushes containing files over 100 MB. The check runs on every blob in the commits you are pushing, not just the tip of your branch. That is the part that surprises people.

Takeaway: GH001 is about every commit in the push, so the fix has to change history, not just the working tree.

Why doesn't git rm and a new commit fix it?

I have watched this dead-end happen more than once: the developer runs git rm data/embeddings.bin, commits, pushes, and gets the identical error. The file is deleted from the latest snapshot, but the commit that added it is still an ancestor of your branch, and it is part of what you are pushing.

Other dead ends that look plausible:

  • git commit --amend only works if the big file is in the very last commit.
  • Adding it to .gitignore after the fact does nothing for a file already tracked or already committed.
  • git reset --soft and recommit works for unpushed local history and is actually fine for a small number of commits, but gets tedious fast and loses your commit messages.

First, find out which commit and which path is the problem:

# list the 10 largest blobs anywhere in history
git rev-list --objects --all \
  | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
  | awk '$1 == "blob" {print $3, $4}' \
  | sort -rn \
  | head -10

The first column is bytes, the second is the path. If the same path shows up in multiple versions, every version is in history and every one over the limit will be rejected.

Takeaway: before touching anything, list the largest blobs in history so you know whether you are removing one file or twenty.

Option A: Remove the file entirely with git filter-repo

Use this when the file was committed by accident (a dataset, a build artifact, a database dump, a model checkpoint) and does not belong in the repository.

git filter-repo is the tool the Git project itself recommends over the older git filter-branch. Install it with pip install git-filter-repo or your package manager. Work on a fresh clone, because filter-repo deliberately refuses to run on a repo that looks like it has remotes and local work you could lose.

git clone git@github.com:you/project.git project-clean
cd project-clean

# remove one path from all history
git filter-repo --path data/embeddings.bin --invert-paths

# or: remove every blob over a size threshold, whatever its path
git filter-repo --strip-blobs-bigger-than 50M

filter-repo removes the origin remote as a safety measure, so re-add it and force-push:

git remote add origin git@github.com:you/project.git
git push --force-with-lease origin main

Two honest drawbacks. First, this rewrites every commit hash from the first affected commit onward, so every collaborator must re-clone or hard-reset, and open pull requests will break. Second, if the file contained a secret, rewriting history on GitHub does not make the secret safe: assume it leaked and rotate it. Cached views of old commits can persist on the platform even after a force push.

If you want a single-purpose tool with a simpler interface, BFG Repo-Cleaner does the same job, but it is less flexible with path filtering than filter-repo.

Takeaway: use git filter-repo --invert-paths when the file was a mistake, and treat every collaborator clone as needing a reset afterward.

Option B: Keep the file but move it to Git LFS

Use this when the file is a legitimate part of the project, like design assets, test fixtures, or a small model, and you want it versioned.

git lfs install
# rewrite ALL history so matching files become LFS pointers
git lfs migrate import --include="*.bin,data/*.parquet" --everything
git push --force-with-lease origin main

--everything rewrites every branch and tag; without it only the current branch is migrated and the other refs still contain the big blobs. Check the result with git lfs ls-files, and confirm .gitattributes now contains the tracking rules, since that file has to be committed for teammates to get the same behavior.

The drawback is that LFS is a separate storage and bandwidth quota on GitHub. The free allowance is limited, large files in CI re-download on every checkout unless you cache them, and forks have their own quirks with LFS objects. Check GitHub's current LFS billing page before committing a multi-gigabyte dataset to it.

Takeaway: LFS keeps big files versioned, but you are trading a hard push error for a storage and bandwidth quota you now have to watch.

Which fix should you use?

Situation Best fix History rewritten? Ongoing cost
Accidental commit of dump, build output, .env, node_modules git filter-repo --invert-paths Yes None
Several oversized blobs, unsure which filter-repo --strip-blobs-bigger-than Yes None
Large assets that must be versioned git lfs migrate import Yes LFS quota
Large file only in unpushed local commits git reset --soft and recommit Local only None
Dataset or model that changes often Object storage (S3, R2) plus a download script No Storage fees

The last row is the one I would choose most often for ML-ish repos: store the artifact in a bucket and keep a small script plus a checksum in Git. It keeps clones fast and leaves no history problem to fix later.

How do you stop this from happening again?

Add the pattern to .gitignore before the file exists, and add a pre-commit check so the failure happens on your laptop instead of at push time:

#!/usr/bin/env bash
# .git/hooks/pre-commit — block staged files over 50 MB
limit=$((50 * 1024 * 1024))
for f in $(git diff --cached --name-only --diff-filter=AM); do
  size=$(wc -c < "$f")
  if [ "$size" -gt "$limit" ]; then
    echo "Refusing to commit $f ($size bytes, limit $limit)" >&2
    exit 1
  fi
done

Make it executable with chmod +x .git/hooks/pre-commit. Hooks in .git/ are not shared, so for a team use a framework such as pre-commit, which has a built-in large-file check.

FAQ

Why does GitHub reject my push after I deleted the large file?
Because the commit that added the file is still in your branch history, and GitHub scans every commit being pushed. You must rewrite history with git filter-repo or git lfs migrate to remove the blob from those commits.

What is GitHub's maximum file size for a regular Git push?
GitHub blocks files larger than 100 MB and warns above 50 MB, as of late 2026. Larger files need Git LFS or external storage.

Is it safe to force-push after running git filter-repo?
It is safe for your repo if you use --force-with-lease and coordinate with collaborators, who will need to re-clone. It is not a fix for leaked secrets, which must be rotated regardless.

Bottom line

If the file should never have been committed, run git filter-repo on a fresh clone and force-push once. If you genuinely need large files versioned alongside code, Git LFS is the standard route on GitHub, with a quota to monitor. For datasets and model weights that change often, keep them in object storage and let Git hold only the script that fetches them.

Related reading

來源:DEV Community · dev.to