跳转到内容

Git 重置提交记录 恢复历史 清理提交记录

Git Commit History Reset

有时候,我们提交了一些隐私的数据例如密码等到 Github 仓库,就算更新了仓库文件,但依旧会在 commit 历史记录中保存这部分数据。这个时候,我们就需要一种方法,可以把Github分支下所有提交记录进行删除!

本文将介绍几种彻底重置 Git 提交记录的方法,帮助你清理敏感信息或重新开始一个干净的历史。

为什么需要重置提交记录?

常见场景

  1. 提交了敏感信息

    • 密码、API 密钥
    • 私钥、证书文件
    • 个人身份信息
  2. 提交了大文件

    • 视频、图片资源
    • 数据库备份
    • node_modules 目录
  3. 历史过于混乱

    • 大量 WIP 提交
    • 实验性代码
    • 错误的提交信息
  4. 项目重构

    • 完全重写代码
    • 改变项目结构
    • 迁移技术栈

方法一:使用 Orphan 分支(推荐)

这是最彻底的清理方式,创建一个完全没有历史的新分支。

操作步骤

一般使用新建分支,都会在当前 master 分支的基础上克隆一份,如下图所示:

Git

sh
# 1. 创建一个孤儿分支(没有任何历史)
git checkout --orphan latest_branch

# 2. 添加你想提交的所有文件到这个新分支
git add -A
git commit -m "commit message"

# 3. 先将旧分支删除
git branch -D main

# 4. 再将新分支的名字改为旧分支的名字
git branch -m main

# 5. 最后提交所有本地操作到Github仓库
git push -f origin main

详细解释

步骤 1:创建孤儿分支

bash
git checkout --orphan latest_branch

发生了什么:

  • 创建了一个全新的分支
  • 没有任何提交历史
  • HEAD 指向这个新分支
  • 工作区的文件保持不变

验证:

bash
git log
# 输出:fatal: your current branch 'latest_branch' does not have any commits yet

步骤 2:添加并提交文件

bash
# 添加所有文件
git add -A

# 或者选择性添加
git add src/
git add package.json
git add README.md

# 提交
git commit -m "feat: Initial clean commit"

提示:

  • 这是重新审视 .gitignore 的好机会
  • 确保不要再次提交敏感文件
  • 可以整理项目结构

步骤 3:删除旧分支

bash
# 删除本地的旧分支
git branch -D main

注意:

  • -D 是强制删除,即使有未合并的更改
  • 如果分支名不是 main,替换为实际名称(如 masterdevelop

步骤 4:重命名分支

bash
# 将孤儿分支重命名为原来的名字
git branch -m main

现在你有:

  • 一个名为 main 的分支
  • 只有一个提交
  • 没有任何历史包袱

步骤 5:强制推送到远程

bash
# 强制推送(覆盖远程分支)
git push -f origin main

⚠️ 重要警告:

  • 这会删除远程的所有历史
  • 所有协作者需要重新克隆仓库
  • 确保已通知团队成员

完整示例

bash
# 假设当前在 main 分支,有很多提交历史

# 1. 创建孤儿分支
$ git checkout --orphan clean-slate
Switched to a new branch 'clean-slate'

# 2. 查看状态(所有文件都显示为未跟踪)
$ git status
On branch clean-slate
No commits yet
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        README.md
        src/
        package.json

# 3. 更新 .gitignore
$ cat > .gitignore << EOF
node_modules/
.env
*.log
dist/
EOF

# 4. 添加文件
$ git add .

# 5. 提交
$ git commit -m "feat: Fresh start with clean history"

# 6. 删除旧分支
$ git branch -D main
Deleted branch main (was abc1234).

# 7. 重命名
$ git branch -m main

# 8. 推送到远程
$ git push -f origin main
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (5/5), 1.23 KiB | 1.23 MiB/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To github.com:user/repo.git
 + abc1234...def5678 main -> main (forced update)

方法二:使用 Filter-Repo 清理历史

如果你只想删除特定的文件或目录,而不是整个历史,可以使用 git filter-repo

安装

bash
# 使用 pip
pip install git-filter-repo

# 或使用 Homebrew
brew install git-filter-repo

删除特定文件

bash
# 从历史中删除敏感文件
git filter-repo --path password.txt --path secret.key --invert-paths --force

# 删除整个目录
git filter-repo --path node_modules/ --invert-paths --force

# 删除特定类型的所有文件
git filter-repo --path-glob '*.log' --invert-paths --force

替换敏感内容

bash
# 创建替换规则文件
cat > replacements.txt << EOF
password123==>REDACTED
api_key_abc123==>REDACTED
secret_token==>REDACTED
EOF

# 执行替换
git filter-repo --replace-text replacements.txt --force

推送到远程

bash
# 强制推送
git push --force --all
git push --force --tags

方法三:使用 BFG Repo-Cleaner

BFG 是一个更快、更简单的替代方案。

安装

bash
# macOS
brew install bfg

# Linux
sudo apt install bfg

# 或下载 JAR 文件
wget https://repo1.maven.org/maven2/com/madgag/bfg/1.14.0/bfg-1.14.0.jar

使用方法

bash
# 删除大文件(大于 100M)
bfg --strip-blobs-bigger-than 100M my-repo.git

# 删除特定文件
bfg --delete-files password.txt my-repo.git

# 删除包含敏感内容的文件
bfg --replace-text passwords.txt my-repo.git

# 清理
cd my-repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive

方法四:重置到特定提交

如果你想保留部分历史,可以重置到某个干净的提交。

bash
# 1. 找到要保留的提交
git log --oneline

# 2. 硬重置到该提交
git reset --hard abc1234

# 3. 强制推送
git push -f origin main

团队协作时的注意事项

通知流程

bash
# 1. 提前通知
echo "⚠️ 警告:我将重置 main 分支的历史记录
原因:移除了敏感信息
时间:今天下午 3 点
影响:所有人需要重新克隆仓库"

# 2. 执行重置
# ... 执行上述任一方法 ...

# 3. 通知团队重新克隆
echo "✅ 重置完成
请执行:
1. 备份你的本地更改
2. 删除旧仓库:rm -rf repo
3. 重新克隆:git clone <url>
4. 恢复你的更改"

团队成员的操作

bash
# 方法 1:重新克隆(推荐)
cd ..
rm -rf repo
git clone https://github.com/user/repo.git

# 方法 2:重置本地分支
git fetch origin
git reset --hard origin/main
git clean -fd

# 方法 3:如果有本地提交
git fetch origin
git rebase origin/main
# 解决可能的冲突

预防胜于治疗

1. 完善 .gitignore

gitignore
# 环境变量
.env
.env.local
.env.production

# 密钥文件
*.pem
*.key
*.p12
id_rsa
id_ed25519

# 配置文件
config/secrets.yml
credentials.json

# 日志文件
*.log
logs/

# 依赖
node_modules/
vendor/

# 构建产物
dist/
build/

2. 使用预提交钩子

创建 .git/hooks/pre-commit

bash
#!/bin/bash

# 检查是否提交了敏感文件
if git diff --cached --name-only | grep -E '\.env|\.pem|\.key'; then
    echo "❌ 错误:检测到敏感文件!"
    exit 1
fi

# 检查是否包含密码模式
if git diff --cached | grep -iE 'password\s*=\s*["\'][^"\']+["\']'; then
    echo "❌ 错误:检测到硬编码密码!"
    exit 1
fi

3. 使用 Git Secrets

bash
# 安装
brew install git-secrets

# 初始化
git secrets --install

# 添加规则
git secrets --add 'password\s*=\s*.+'
git secrets --add 'api_key\s*=\s*.+'

# 扫描历史
git secrets --scan-history

4. 环境变量管理

bash
# ✅ 正确:使用环境变量
const apiKey = process.env.API_KEY;

# ❌ 错误:硬编码
const apiKey = "sk-1234567890";

创建 .env.example

env
# 复制此文件为 .env 并填入真实值
API_KEY=your_api_key_here
DATABASE_URL=your_database_url
SECRET_KEY=your_secret_key

常见问题排查

问题 1:推送被拒绝

bash
# 错误:protected branch update failed

# 解决方案:
# 1. 临时禁用分支保护
# 2. 执行推送
git push -f origin main
# 3. 重新启用保护

问题 2:GitHub 仍然显示旧文件

bash
# GitHub 会缓存内容,需要:
# 1. 等待几分钟
# 2. 硬刷新浏览器(Ctrl+Shift+R)
# 3. 如果还不行,联系 GitHub 支持

问题 3:Fork 的仓库怎么办?

bash
# Fork 的仓库不会自动更新
# 需要通知所有 Fork 所有者:
# 1. 删除他们的 Fork
# 2. 重新 Fork 你的仓库

问题 4:CI/CD 失败

bash
# 重置后 CI/CD 可能需要:
# 1. 清除缓存
# 2. 重新配置环境变量
# 3. 更新部署脚本

最佳实践总结

选择合适的方法

场景推荐方法难度风险
完全重新开始Orphan 分支⭐⭐
删除特定文件filter-repo⭐⭐⭐
删除大文件BFG⭐⭐
回退到某版本reset --hard

操作清单

在执行重置前:

操作后检查

总结

重置 Git 提交记录是解决敏感信息泄露和历史混乱的有效方法:

  1. Orphan 分支:最彻底,适合完全重新开始
  2. Filter-Repo:精确控制,适合删除特定内容
  3. BFG:快速简单,适合大文件清理
  4. Reset:部分保留,适合回退到稳定版本

记住:

  • ⚠️ 这些操作都会改写历史
  • 📢 务必提前通知团队
  • 💾 操作前务必备份
  • 🔒 事后加强预防措施

下一步学习:

谨慎操作,安全第一!🔐