DEV Community

Mohamed Idris
Mohamed Idris

Posted on

How to Remove a File from Git History (And What to Watch Out For)

It happens to everyone. You commit a file you shouldn't have — maybe a .env with API keys, a huge binary, or just something private — and then you push it.

The bad news: deleting the file and committing again doesn't actually remove it from git history. Anyone can still go back and see it.

The good news: you can rewrite history to remove it completely. Here's how.


Why deleting the file isn't enough

Git keeps a full snapshot of every commit. Even after you delete a file, it still lives in older commits. Someone can always run git log -- filename or check out an old commit to get it back.

To truly remove it, you need to rewrite every commit that ever touched that file.


Step 1 — Make sure the file is deleted locally

If the file still exists on disk, delete it first. Then stage the deletion:

git rm the-file.txt
git commit -m "Remove the-file.txt"
Enter fullscreen mode Exit fullscreen mode

Or if you already deleted it manually:

git add -A
git commit -m "Remove the-file.txt"
Enter fullscreen mode Exit fullscreen mode

Step 2 — Rewrite git history

This is the actual scrubbing step. Run this command (replace the-file.txt with your file path):

FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch --force \
  --index-filter "git rm --cached --ignore-unmatch the-file.txt" \
  --prune-empty \
  --tag-name-filter cat \
  -- --all
Enter fullscreen mode Exit fullscreen mode

What each flag does:

  • --index-filter — runs a command on every commit's index (faster than checking out files)
  • git rm --cached --ignore-unmatch — removes the file from the index, silently skips commits that don't have it
  • --prune-empty — removes any commits that become empty after the file is gone
  • --tag-name-filter cat — rewrites tags to point to the new commits
  • -- --all — applies to all branches and refs

Note: Git will warn you about filter-branch being error-prone and recommend git-filter-repo instead. If you can install it (pip install git-filter-repo), it's faster and safer. The equivalent command is just:

git filter-repo --path the-file.txt --invert-paths

Step 3 — Clean up local dangling objects

After rewriting, old commits still hang around in your local reflog. Clean them up:

git reflog expire --expire=now --all
git gc --prune=now --aggressive
Enter fullscreen mode Exit fullscreen mode

Step 4 — Force push

This is the step that overwrites the remote history:

git push origin main --force
Enter fullscreen mode Exit fullscreen mode

Cautions — read before you do this

This is a destructive, irreversible operation. Once you force push rewritten history, the old commits are gone from the remote. Here's what that means in practice:

If others have cloned the repo

Anyone who cloned or pulled before your rewrite will have the old history. They'll need to re-clone or do a hard reset:

git fetch origin
git reset --hard origin/main
Enter fullscreen mode Exit fullscreen mode

Their local branches with the old history will conflict with the new remote. Coordinate with your team before doing this.

The secret may already be compromised

If you pushed API keys or credentials, assume they are already leaked. Bots scan GitHub in real time. Rewriting history is cleanup — but your first step should be revoking and rotating the credentials immediately, before anything else.

It doesn't work on forks

If someone forked your repo before the rewrite, they still have the old history. You can't rewrite their copy.

GitHub/GitLab may cache the old commits

GitHub keeps cached views of old commits for a short time even after a force push. You can contact GitHub support to purge the cache, but for leaked secrets, rotate first — don't wait.


Quick recap

Step Command
Delete the file git rm the-file.txt
Rewrite history git filter-branch ... or git filter-repo
Clean up local objects git reflog expire + git gc
Force push git push origin main --force

Best practices to avoid this in the first place

  • Add sensitive files to .gitignore before you create them
  • Use a .env.example file with dummy values instead of committing real secrets
  • Tools like git-secrets or gitleaks can scan commits before they're pushed and block accidental leaks

Rewriting history is a useful escape hatch — but the best commit to regret is the one you never made.

Top comments (0)