Skip to content

常见事故与恢复手册

本章记录了 Git 使用中最常见的"事故"场景及其恢复方法。

提交到错误分支

场景:在 main 上直接提交了应该在 feature 分支上的内容

bash
# 当前状态:main 上有不该有的提交 abc123
git log --oneline main
# abc123 feat: 新功能(错误地提交到了 main)
# def456 fix: 上一个正常提交

# 解决方案:
# 1. 在正确的分支上重建提交
git switch -c feature/new-feature  # 创建应该有这些提交的分支
# feature/new-feature 现在包含那些提交

# 2. 在 main 上撤销提交
git switch main
git reset --hard HEAD~1    # 或 git reset --hard def456

# 如果 main 已推送:
git push --force-with-lease origin main

合并了错误的分支

场景:将错误的 feature 分支合并到了 main

bash
# 如果还没 push:
git reset --hard ORIG_HEAD    # ORIG_HEAD 保存了 merge 前的状态
# 或
git reset --hard HEAD~1

# 如果已 push(需要 revert 合并提交):
git log --oneline --merges -5
# abc123 Merge branch 'wrong-feature'

git revert -m 1 abc123    # -m 1 保留 main 侧的代码
git push

revert 合并后无法再次合并

场景:revert 了合并提交后,bug 修复好了,但再次合并 feature 分支时,旧提交内容没有被引入

bash
# 问题原因:Git 认为 feature 的旧提交已被合并

# 解决方案:
# 方案1:revert the revert
git switch main
git revert <revert-commit-sha>  # 撤销上次的 revert
git merge feature/bug-fixed     # 现在可以合并新的改动

# 方案2:在 feature 分支上 rebase(推荐)
git switch feature/bug-fixed
git rebase main    # 重建提交,新 SHA-1 避免"已合并"问题
git switch main
git merge feature/bug-fixed

误执行 reset --hard

场景:不小心执行了 git reset --hard,丢失了未提交的改动

bash
# 坏消息:工作区改动(未提交)无法恢复!
# 如果是已提交的内容,可以通过 reflog 恢复

# 通过 reflog 找回
git reflog
# abc123 HEAD@{0}: reset: moving to HEAD~3  ← 误操作
# def456 HEAD@{1}: commit: 重要功能          ← 要恢复的位置

git reset --hard def456
# 或
git reset --hard HEAD@{1}

预防措施:

bash
# 危险操作前先创建备份分支
git branch backup-before-reset
# 然后再执行 reset

# 如果有临时改动不想丢失,先 stash
git stash
git reset --hard HEAD~3
git stash pop

误删分支

场景:删除了还有用的分支

bash
# 通过 reflog 找到分支最后的提交
git reflog | grep "feature/important"
# abc123 HEAD@{5}: checkout: moving from feature/important to main

# 重建分支
git branch feature/important abc123
git switch feature/important

推送了敏感信息

场景:误将密码、API Key 等敏感信息推送到远程仓库

立即行动(按优先级):

  1. 立即吊销泄露的凭证(最重要!不要等 Git 操作完成)
bash
# 吊销 API Key、更换密码等
# 这是第一优先级,不管是否能从 Git 历史中删除
  1. 从历史中删除敏感信息
bash
# 使用 git-filter-repo(推荐)
pip install git-filter-repo

# 替换敏感字符串
cat > replacements.txt << 'EOF'
my_secret_api_key==>REDACTED_API_KEY
my_db_password==>REDACTED_PASSWORD
EOF

git filter-repo --replace-text replacements.txt

# 或者删除包含敏感信息的文件
git filter-repo --path config/secrets.json --invert-paths
  1. 强制推送
bash
git push origin --force --all
git push origin --force --tags
  1. 通知所有协作者重新克隆
bash
# 历史已被重写,所有人必须重新克隆
rm -rf old-repo
git clone <url> new-repo
  1. 请求 GitHub/GitLab 清除缓存(联系支持团队)

预防措施:

bash
# 使用 pre-commit 钩子检测敏感信息
# 工具:git-secrets, gitleaks, trufflehog

# 安装 gitleaks
brew install gitleaks

# 扫描当前仓库
gitleaks detect

# 作为 pre-commit 钩子
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
gitleaks protect --staged --redact -v
EOF
chmod +x .git/hooks/pre-commit

仓库损坏的修复

场景:仓库出现损坏(如断电、磁盘错误)

bash
# 检查仓库完整性
git fsck
git fsck --full

# 常见错误及处理:
# "object file is empty":空对象文件
find .git/objects -size 0 -delete  # 删除空对象文件
git fsck  # 再次检查

# "loose object is corrupt":对象损坏
# 尝试从远程恢复
git fetch origin --all

# 极端情况:克隆到新目录
cd ..
git clone <remote-url> repo-recovered

# 如果有未推送的提交,尝试恢复
# 1. 找到未损坏的提交
git fsck --unreachable 2>&1 | grep "dangling commit"

# 2. 检出并恢复
git cat-file -p <commit-sha>

force push 导致同事代码丢失

场景:force push 覆盖了同事已推送但你还没拉取的提交

bash
# 从同事那里获取丢失的提交 SHA-1
# 让同事运行:git reflog | head -20

# 在你的仓库中获取那些 SHA-1
git fetch origin
# 如果已被覆盖,让同事推送到临时分支
git push origin def456:refs/heads/rescue-branch

# 你获取并合并
git fetch origin rescue-branch
git merge origin/rescue-branch

# 之后正确推送
git push --force-with-lease origin main

# 预防措施:
# 1. 始终使用 --force-with-lease 而不是 --force
# 2. 不要对共享分支(main、develop)进行 force push
# 3. 在 force push 前通知团队

误将大文件提交

场景:不小心提交了大文件(如视频、压缩包)

bash
# 如果刚刚提交,还没 push:
git reset --soft HEAD~1    # 撤销提交,保留改动
rm large-file.mp4           # 删除大文件
echo "*.mp4" >> .gitignore  # 加入 .gitignore
git add .gitignore
git commit -m "chore: 添加大文件到 .gitignore"

# 如果已经 push,需要清理历史(参考仓库瘦身章节)
git filter-repo --path large-file.mp4 --invert-paths
git push --force --all

紧急修复:无法访问某个提交

bash
# 通过各种方式找到提交
git log --all --oneline      # 所有分支的历史
git reflog --all             # 所有引用的操作历史
git fsck --unreachable       # 不可达对象(孤立提交)

# 找到后创建分支保存
git branch rescue abc123

# 如果需要合并到当前分支
git cherry-pick abc123

快速参考

事故解决命令
提交到错误分支git reset HEAD~1 + 在正确分支 cherry-pick
合并错误分支(未 push)git reset --hard ORIG_HEAD
合并错误分支(已 push)git revert -m 1 <merge-sha>
误 reset --hardgit reflog + git reset --hard HEAD@{n}
误删分支git branch <name> <sha>
推送敏感信息立即吊销凭证 + git-filter-repo + force push
仓库损坏git fsck + 从远程恢复
丢失提交git reflog + git branch rescue <sha>