Git Orphan Branch — 彻底清理仓库历史

场景

需要公开一个曾经提交过敏感信息(API Key、.env、密码、大文件等)的仓库,且只保留当前文件状态,不暴露任何历史提交。

原理

创建一个孤儿分支(orphan branch),它不继承任何父提交,相当于一张白纸。在这个分支上只提交你想保留的文件,然后强制覆盖远程分支,旧历史就彻底被丢弃了。

旧历史(包含敏感数据):
  A ── B ── C ── D ── E ── F

用 orphan 覆盖后:
  X (root-commit, 无父节点)

操作步骤

# 1. 创建孤儿分支(切换到一个无父节点的新分支)
git checkout --orphan clean-main

# 2. 按当前工作目录状态暂存所有文件
git add -A

# 3. 从暂存区移除不想提交的敏感/大文件(本地保留)
git rm --cached .env dist/ server/ai/kb_vectors.json

# 4. 提交干净的初始快照
git commit -m "Initial commit: clean slate"

# 5. 强制覆盖远程 main 分支
git push origin clean-main:main --force

关键点

命令 作用
--orphan 创建一个没有 parent 的分支,和原历史完全独立
git rm --cached 只从暂存区移除,不删本地文件(配合 .gitignore 防止再被误加)
--force 必须强制推送,因为远程旧历史不是快进合并(non-fast-forward)

注意事项

  • ✅ 对 GitHub 上未被人 clone 过的仓库,这种方式是彻底的
  • ⚠️ 如果有人已经 pull/clone 过旧历史,他们本地仍保留着,只能让他们重新 clone
  • ⚠️ 即使历史被覆盖,密钥仍然建议轮换(去对应平台重新生成),因为曾经暴露过
  • ☝️ 推送前先用 git status 确认只有想提交的文件

替代方案

  • git filter-branch / git filter-repo:可以精准地从历史中移除某个文件,保留其余提交。适合不想丢掉历史记录的场景,但操作更复杂
  • 本文的方案是"一刀切":完全重写历史,只保留当前快照