Git Filter-Branch — Remove File from History Fix
In this tutorial, you'll learn about Git Filter. We cover key concepts, practical examples, and best practices.
You accidentally committed a sensitive file (password, API key, large binary) that was pushed. Simply deleting it in a new commit does not remove it from history — anyone can still access it. git filter-branch rewrites history to purge it permanently.
The Problem
# Accidentally committed .env with secrets
git add .env
git commit -m "Add config"
git push origin main
Even after deleting and pushing:
git rm .env
git commit -m "Remove .env"
git push origin main
The file is still accessible via git show <old-commit>:.env.
Wrong Approach
# WRONG — git rm does not remove from history
git rm .env
git commit -m "Remove .env"
Right Approach
# Remove the file from all history
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch .env' \
--prune-empty --tag-name-filter cat -- --all
Expected output:
Rewrite abc123def4567890 (1/15) (0 seconds passed)
Rewrite ... (15/15) (3 seconds passed)
Ref 'refs/heads/main' was rewritten
Step-by-Step Fix
Step 1: Create a backup
git clone --mirror . /tmp/repo-backup.git
Step 2: Run filter-branch to remove the file
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch .env' \
--prune-empty --tag-name-filter cat -- --all
Step 3: Verify the file is gone
git log --all -- .env
Should show no commits.
Step 4: Force push rewritten history
git push origin --force --all
git push origin --force --tags
Step 5: Clean up local references
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
git reflog expire --expire-unreachable=now --all
git gc --prune=now
Step 6: Use git filter-repo instead (recommended)
pip install git-filter-repo
git filter-repo --path .env --invert-paths
Prevention Tips
- Add sensitive files to
.gitignorebefore the first commit - Use
.gitignoretemplates for your language/framework - Set up pre-commit hooks to detect secrets (e.g.,
git-secrets) - Use environment variables with
.env.example(never commit actual.env) - Scan history periodically with
truffleHogorgittyleak
Common Mistakes with filter branch
- Mixing let bindings with <- bindings in do notation, producing type errors
- Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
- Non-exhaustive pattern matches that compile with warnings then crash at runtime
These mistakes appear frequently in real-world GIT code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro