git-filter-repo
名称、版本与用途
- 名称:git-filter-repo
- 当前包版本:2.47.0
- 当前命令构建标识:
a40bce548d2c - 路径:
~/.local/bin/git-filter-repo - 实际环境:
~/.local/share/uv/tools/git-filter-repo/ - 用途:快速、可编程地重写 Git 历史,是
git filter-branch的推荐替代。
基本语法(Synopsis)
git filter-repo [OPTIONS][] 表示可选项,... 表示可重复;大写名称表示需要替换的参数。
当前安装与更新
当前由 uv tool 管理:
uv tool list
uv tool upgrade git-filter-repo如需重装:
uv tool install --force git-filter-repo使用约定
它会读取仓库自身的 Git 数据和部分 Git 配置。执行后通常会写入重写映射及相关元数据。
基本功能与推荐用法
git filter-repo --analyze
git filter-repo --path secret.txt --invert-paths
git filter-repo --sensitive-data-removal --path secret.txt --invert-paths
git filter-repo --path old/ --path-rename old/:new/
git filter-repo --replace-text replacements.txt
git filter-repo --mailmap ../mailmap
git filter-repo --help推荐流程:
- 使用新的
--mirror克隆或完整备份。 - 先运行
--analyze查看历史结构。 - 在副本中执行过滤。
- 检查分支、标签、对象数量和敏感内容。
- 与所有协作者协调后再推送重写历史。
历史重写会改变 commit ID,并可能让其他克隆产生复杂冲突。不要在唯一副本上执行。不要用
--force绕过“新克隆”检查,除非已经理解全部后果。
删除已经泄露的密钥后,还必须在服务端撤销或轮换密钥;仅重写 Git 历史不等于密钥重新安全。
示例中的选项、参数与子命令
本表解释本页示例实际出现的选项、参数、子命令及必要的辅助命令语法。
| 语法元素 | 含义 |
|---|---|
--analyze | 分析仓库历史并生成报告,不重写历史。 |
--path PATH | 只选择指定路径,可重复使用。 |
--invert-paths | 反转路径选择,用于删除所选路径。 |
--sensitive-data-removal, --sdr | 针对敏感数据清理收集额外信息,并给出其他副本的后续清理提示;默认会从 origin 获取所有可获取引用。 |
--path-rename OLD:NEW | 在整个历史中重命名路径。 |
--replace-text FILE | 根据规则文件替换历史中的文本。 |
--mailmap FILE | 按 mailmap 重写作者和提交者身份。 |
--help | 显示完整帮助。 |
uv tool install --force TOOL | 强制重新安装已存在的 uv 工具。 |
清除历史提交中的敏感数据
假设 simulators/ 已写入 .gitignore,但历史中仍包含敏感文件。.gitignore 只能防止以后重新提交,不能清除已有提交。
先撤销凭据,再重写历史
API key、Token、密码或私钥一旦提交,就应视为已经泄露。第一步是在对应服务端撤销或轮换,而不是先运行 Git 命令。历史重写不能让旧凭据重新变得安全。
1. 协调维护窗口
重写开始前:
- 暂停向仓库推送。
- 记录受影响的路径、分支、标签和凭据。
- 确认谁有权限调整受保护分支。
- 通知协作者重写完成后必须重新 clone。
2. 使用 fresh mirror clone
在原仓库之外创建一次性镜像副本:
git clone --mirror <remote-url> repo-clean.git
cd repo-clean.git
git filter-repo --analyze如果需要回滚,可在重写前创建访问受限的 bundle:
git bundle create ../repo-before-rewrite.bundle --all备份也包含敏感数据
bundle、旧 clone、CI 缓存和本地副本仍保存泄露历史。备份必须限制访问,并在确认迁移完成后按安全策略销毁。
从本地路径 clone 时应加 --no-local,避免 Git 通过硬链接等本地优化导致 fresh clone 检查或隔离效果不符合预期:
git clone --mirror --no-local /path/to/repo repo-clean.git3. 重写所有可获取引用
删除整个目录:
git filter-repo \
--sensitive-data-removal \
--invert-paths \
--path simulators/删除单个文件可将路径改为文件名;文件曾经移动或改名时,要为每个历史路径重复添加 --path:
git filter-repo \
--sensitive-data-removal \
--invert-paths \
--path config/secret.env \
--path old-config/secret.env路径匹配不会自动跟随历史重命名。遗漏旧路径会让敏感 blob 继续留在其他提交中。
--sensitive-data-removal 默认从 origin 获取所有可获取引用,以减少遗漏远程分支或标签的风险。除非已经确认全部引用另有来源,不要使用 --no-fetch。
不要把
--force当作常规参数在 fresh clone 中通常不需要
--force。它会绕过安全检查,并立即清理 reflog 和旧对象;只应在已经理解仓库状态、拥有独立恢复副本时使用。
4. 验证本地结果
确认路径不再出现在任何本地引用:
git log --all --oneline -- simulators/
git rev-list --objects --all | rg '(^| )simulators/'两条命令都应无输出。然后检查仓库结构和重写报告:
git show-ref
git fsck --full
ls -lah filter-repo/还应使用原有的 secret scanner 重新扫描全部历史。不要把真实密钥直接写进命令行,因为它可能再次进入 Shell 历史、终端日志或自动化记录。
5. 更新远程历史
git-filter-repo 通常会移除 origin,避免误推送。核对地址后重新添加:
git remote add origin <remote-url>
git remote -v先阅读命令输出中的敏感数据清理提示,再按代码托管平台的要求更新所有受影响分支和标签:
git push origin --force --all
git push origin --force --tags受保护分支可能需要临时调整规则。托管平台还可能保留 pull request 引用、fork、缓存或对象视图;这些内容无法只靠本地 force push 清除,需要按平台文档处理,必要时联系平台支持。
6. 从远程重新验证
不要只检查重写用的本地副本。推送完成后,再创建一个 fresh clone:
cd ..
git clone --mirror <remote-url> repo-verify.git
cd repo-verify.git
git log --all --oneline -- simulators/
git rev-list --objects --all | rg '(^| )simulators/'重新运行 secret scanner,并确认默认分支、标签、CI、发布流程和部署仍然正常。
7. 协作者收尾
- 所有协作者重新 clone,不要把旧分支 merge 或 push 回新历史。
- 清理 fork、CI workspace、制品、缓存和其他镜像仓库。
- 更新因凭据轮换而受影响的 CI/CD、部署平台和本地环境。
- 保留事故记录,但不要在记录中复制真实秘密。
- 确认无需回滚后,按安全策略销毁包含旧历史的备份。
历史重写后 commit hash 会改变。只要旧 clone 或旧引用还能被推回远程,敏感数据就可能重新进入可达历史。