引言:你删除的提交,并没有完全消失
很多开发者以为,只要强制推送(git push -f)就能把上一次不小心提交的敏感信息从仓库里'抹掉'。但现实是:即使提交在分支里消失了,它仍然存在,并且依然可访问。
这种现象意味着:
- 误提交的密钥依旧可能被恢复
- 只要有人知道提交哈希,就能找到它
- 这类'消失的提交'可能长期存在,不会被自动删除
换句话说:删除(覆盖)提交 ≠ 删除风险。
什么是'被删除的提交'?
当你执行以下操作:
git reset --hard HEAD~1
git push --force origin main
你只是让这个提交在分支历史中变得'不可见',而不是'不可访问'。
Git 的工作机制决定了:
- 提交对象仍然存在于服务器
- 只是没有分支引用它
- 但只要知道哈希值,就可以访问
所以它更像是'隐藏',而非'抹除'。
为什么 GitHub 会保留这些提交?
原因可能包括:
- 审计与追踪需求
- 拉取请求(PR)历史
- 分叉网络依赖关系
- 代码托管的内部索引
这些系统让平台很难(也不愿意)彻底删除提交对象。结果就是:强推后'消失'的提交仍在后台保留。
示例
- 直接创建仓库并且模仿错误提交密钥
# 初始化仓库
mkdir test-repo && cd test-repo
git init
echo "正常文件" > file1.txt
git add . && git commit -m "第一次提交"
# 不小心提交密钥
echo "SECRET_KEY=abc123" > .env
git add . && git commit -m "添加配置文件"
- 尝试强制推送来删除上一次提交
# 回退到第一次提交
git reset --hard HEAD~1
# 强制推送
git push --force origin main
- 通过上一次提交的哈希访问提交
# 即使强制推送后,只要知道提交哈希仍可查看
git show <commit_hash>
我们在 GitHub 上在线检查,发现该提交仍然可以被访问(甚至使用四个十六进制数字访问也足够了)。
如何找到这些'消失的提交'?
核心思路是:通过 Git 或平台日志中记录的'零提交推送事件'来定位被覆盖的提交。
当你执行 git push -f 时,如果这次强制推送没有新增提交,而仅仅是将分支指针移动到了更旧的提交位置,那么这次推送事件在日志中会呈现一种特殊现象:
- 推送事件存在
- 但提交列表为空
这意味着:本次推送实质上是'丢弃'了原先在分支头部的一些提交,使分支指向了历史中的某个旧节点。而这些被跳过的提交,就是因强推而'消失'的提交。


