在开发过程中,不小心把 config.yaml(包含Mysql,Redis 等配置)提交进了 Git 仓库。
虽然随后通过提交把文件删除并加入了 .gitignore,但回过头来看,这个处理方式是不完整的。
原因很简单: Git 会永久保留历史提交,只要有人 checkout 到旧 commit,配置内容依然是明文可见的。
很多人第一次遇到这个问题,都会下意识做这些操作:
这些方式的共同问题是: 它们只影响当前分支状态,不会影响 Git 历史中的对象。
只要配置文件曾经被提交过,就必须假设已经泄露。
目前官方推荐的是 git-filter-repo,专门用于:
brew install git-filter-repo
pip3 install --user git-filter-repo
~/.local/bin/git-filter-repo --help
如果提示找不到命令,把路径加入 PATH:
export PATH=$PATH:~/.local/bin
git clone --mirror https://github.com/codepzj/demo-repo.git demo-repo-mirror
cd demo-repo-mirror
--mirror 的目的不是开发,而是为了:
git filter-repo --path config.yaml --invert-paths
执行完成后,git-filter-repo 会:
如果看到类似:
Parsed 15 commits New history written Repacking your repo
说明处理是成功的。
git-filter-repo 会主动移除 origin,这是为了防止误推送。
git remote add origin https://github.com/codepzj/demo-repo.git git push --force --mirror
这一步会直接用新的干净历史替换远端仓库。
在任意新 clone 的仓库中执行:
git log --all -- config.yaml
如果没有任何输出,说明该文件已经不存在于历史中。
config.yaml
同时提供示例文件:
config.example.yaml
只放结构,不放真实值。
无论泄露时间长短,都应视为已暴露:
历史清理只能解决 Git 层面的问题,无法撤回已经泄露的密钥。
历史被重写后,所有协作者必须同步仓库状态:
git fetch --all git reset --hard origin/main
如果有人直接 git pull 并再次推送,旧历史可能会被带回远端。
这次问题的根源不是 Git 操作失误,而是:
对 Git 历史不可变性的低估。
只要敏感信息进入过仓库,正确的处理方式只有一种:
重写历史,而不是覆盖提交。
git filter-repo + mirror + force push是目前最稳妥、最可控的一套方案。