How to Filter Git History to Remove Secret File Entries in Python
A pure-Python mock that filters a repository's history to drop any commit that touched a secret file, so you can plan a cleanup before rewriting Git history.
Python code
20 linesfrom pathlib import Path
import json
def filter_history(history, secret_path):
"""Remove entries that touch the secret file."""
return [entry for entry in history if secret_path not in entry["files"]]
if __name__ == "__main__":
repo_history = [
{"commit": "a1b2c3", "message": "Add app", "files": ["main.py", "app.py"]},
{"commit": "d4e5f6", "message": "Fix bug", "files": ["utils.py"]},
{"commit": "g7h8i9", "message": "Leak", "files": ["secrets.env", "main.py"]},
{"commit": "j0k1l2", "message": "Add feature", "files": ["features.py"]},
]
cleaned = filter_history(repo_history, "secrets.env")
print(json.dumps(cleaned, indent=2))
print(f"\nOriginal commits: {len(repo_history)}")
print(f"Filtered commits: {len(cleaned)}")
Output
[
{
"commit": "a1b2c3",
"message": "Add app",
"files": ["main.py", "app.py"]
},
{
"commit": "d4e5f6",
"message": "Fix bug",
"files": ["utils.py"]
},
{
"commit": "j0k1l2",
"message": "Add feature",
"files": ["features.py"]
}
]
Original commits: 4
Filtered commits: 3
How it works
The filter_history function uses a list comprehension to keep only entries where the secret file is not in entry["files"]. This simulates the detection step before a real git filter-branch or git filter-repo cleanup. It works because each entry is a plain dict with a files field, and the in operator checks membership in that list. The script prints the cleaned history as JSON for easy inspection, plus counts so you can see how many commits would be removed. In a real repo, you'd feed this with git log --name-only output and then use the filtered result to craft a history-rewriting command.
Common mistakes
- Checking the secret filename against the whole commit message instead of the `files` list.
- Forgetting that `in` does a substring match, so `secrets.env` also matches `mysecrets.env2`.
- Relying on shallow history that doesn't include the file in older commits.
Variations
- Use `git log --name-only --pretty=format:` in a subprocess to build history from a real repo.
- Simulate with `filter(lambda e: secret_path not in e["files"], history)` instead of a list comprehension.
Real-world use cases
- Auditing a legacy repo before rewriting history to purge an accidentally committed API key.
- Building a pre-commit check that flags any staged change touching a secrets file.
- Planning a filter-branch script that sanitizes historical commits without touching current branches.
Sponsored
More from Git + Python
- Amend Last Commit Message in Python easy
- Bisect Good Bad Automation Script in Python easy
- Build a Simple Log Graph in Python easy
- Bump Semantic Version Git Tag in Python easy
- Count Unique Contributors from Git Shortlog in Python easy
- Create a Mock GitHub Release API in Python for Testing gh CLI easy
Keep learning
Related tutorials and quizzes for this topic.