SysTutorials

Cleaning Up Git Commit History: A Practical Guide

“Cleaning” history usually means rewriting it to remove large files, sensitive data, or to squash messy commits into logical units. This is destructive—it rewrites history and affects anyone who’s cloned the repo. Only do this on branches you control or coordinate with your team first.

git filter-repo: The Standard Tool

git filter-repo replaced the deprecated git filter-branch years ago. It’s faster, safer, and handles complex rewrites without the footguns.

Install it via your package manager:

# Debian/Ubuntu
sudo apt install git-filter-repo

# macOS
brew install git-filter-repo

# Or from source
pip install --user git-filter-repo

Remove a large file from all commits:

git filter-repo --path large-file.bin --invert-paths

Remove sensitive data (like an API key) from all commits:

git filter-repo --replace-text replacements.txt

Where replacements.txt contains:

api_key_123456789==>REDACTED

Rewrite author and committer information across the entire history:

git filter-repo --mailmap mailmap.txt

Where mailmap.txt maps old emails to new ones:

New Name <new@example.com> <old@example.com>
New Name <new@example.com> Old Name <old@example.com>

Important: filter-repo rewrites all refs. After running it, you’ll need to force-push all branches:

git push --force-with-lease origin --all
git push --force-with-lease origin --tags

Interactive Rebase for Recent Commits

For squashing, rewording, or reordering commits on the current branch without affecting shared history:

git rebase -i HEAD~5

This opens your editor with the last 5 commits. Commands available:

After editing, save and exit. Git will apply the changes. If conflicts occur, resolve them and run git rebase --continue.

Use this only for unpushed commits or feature branches. Don’t rebase shared history without team agreement.

BFG Repo-Cleaner for Quick Removals

If you just need to yank a huge file or a secret from history, BFG is simpler than crafting a filter-repo command:

# Remove a file from all commits
bfg --delete-files large-file.bin

# Remove a folder
bfg --delete-folders node_modules

# Replace sensitive strings
bfg --replace-text secrets.txt

BFG is faster for simple cases and less error-prone if you don’t need fine-grained control.

Safe Force Pushing

Rewriting history always requires a force push. Use --force-with-lease instead of --force:

git push --force-with-lease origin branch-name

--force-with-lease checks that no one else has pushed to the branch since your last fetch. If they have, the push is rejected. This prevents accidentally overwriting teammates’ work.

Standard --force blindly overwrites everything and should be avoided in shared repos.

Before You Clean History

If something goes wrong on a personal branch, you can recover from the reflog:

git reflog
git reset --hard <commit-hash>

When NOT to Clean History

Don’t rewrite history on:

For most teams, it’s safer to just accept messy history or create a new repo with a clean start.

← All posts