A close up of a computer monitor displaying code inside a command line terminal.

Photo by Pixabay on Pexels

You committed a 140 megabyte .env file or local SQLite database three commits ago, and GitHub just rejected your push. The file is already part of your local history, so deleting it with git rm in a new commit will not shrink the packfile or pass the remote host size limits.

The broken push

You try to send your branch to the remote:

bash
git push origin feature-auth

The output gives you a hard rejection:

text
remote: Resolving deltas: 100% (14/14), completed with 8 local objects.
remote: error: Trace: 4b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e
remote: error: See https://gh.io/lfs for more information.
remote: error: File data/local_dev.db is 142.10 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.
To github.com:org/repo.git
 ! [remote rejected] feature-auth -> feature-auth (pre-receive hook declined)
error: failed to push some refs to 'github.com:org/repo.git'

If you run git rm data/local_dev.db and make a fourth commit, the push will fail with the exact same message. Git transfers trees and blobs, not a simple unified diff of the tip. The 142 megabyte blob is referenced in commit b3a1f8c, so Git tries to upload it.

The manual fix using interactive rebase

If the commit history is local and only a few commits deep, do not reach for heavy external tools like git-filter-repo. You can drop the file from the past commit using git rebase -i.

First, look at your log to see where the file entered:

bash
git log --oneline -n 5
text
e91b4a2 Add token refresh route
4c88f11 Connect session store to redis
b3a1f8c Set up development database schema
a019d33 Initial project layout

The file was added in b3a1f8c. Start an interactive rebase targeting the parent of that commit:

bash
git rebase -i a019d33

Your text editor opens with an instruction sheet:

text
pick b3a1f8c Set up development database schema
pick 4c88f11 Connect session store to redis
pick e91b4a2 Add token refresh route

# Rebase a019d33..e91b4a2 onto a019d33

Change the word pick to edit (or just e) next to b3a1f8c:

text
edit b3a1f8c Set up development database schema
pick 4c88f11 Connect session store to redis
pick e91b4a2 Add token refresh route

Save and close the file. Git will rewind your branch to b3a1f8c and pause:

text
Stopped at b3a1f8c...  Set up development database schema
You can amend the commit now, with

  git commit --amend

Once you are satisfied with your changes, run

  git rebase --continue

Now remove the large file from Git tracking without deleting it from your actual disk:

bash
git rm --cached data/local_dev.db

Add the path to .gitignore so you do not make this mistake again in ten minutes:

bash
echo "data/local_dev.db" >> .gitignore
git add .gitignore

Amend the paused commit to seal the changes:

bash
git commit --amend --no-edit

Now let Git replay the remaining commits on top:

bash
git rebase --continue

If there are no merge conflicts with downstream commits, Git exits cleanly:

text
Successfully rebased and updated refs/heads/feature-auth.

The gotcha: downstream references and dangling objects

There are two traps here.

The first happens during the rebase itself. If one of your later commits (like 4c88f11) modified that database file, git rebase --continue will stop with a merge conflict. Git thinks you deleted a file that a later commit modified.

To resolve that, finish deleting it for that later step too:

bash
git rm data/local_dev.db
git rebase --continue

The second trap is packfile size. Even though your commit tree no longer points to the 142 megabyte file, the object still sits in your .git/objects folder. It also sits in your reflog.

If your remote allows the push now, you can leave it alone. Git cleans itself up every few weeks via automatic garbage collection. But if you are on a metered connection or a shared drive and need that disk space back immediately, you have to flush the references:

bash
git reflog expire --expire=now --all
git gc --prune=now

Before running those two lines, check your working tree. git gc --prune=now deletes every unreachable commit immediately. If you made a mistake during your rebase, this strips away your safety net.

Verification

Before pushing, verify that the large object is completely absent from the commits you are about to send.

Run git log with the file path against your remote branch:

bash
git log origin/main..HEAD -- data/local_dev.db

If that prints nothing, the file does not exist in any commit between origin/main and your current branch tip. You can run your push:

bash
git push origin feature-auth

The remote will accept the branch without mentioning LFS or payload sizes.


Published by the Old Lane Developer Desk. This article was drafted with AI assistance.

ADVERTISEMENT

More in Bash