Git 操作中,最危险的命令之一就是 git push --force。一旦用错,可能覆盖他人代码、丢失提交历史。本文总结 force push 的正确用法,以及误操作后的恢复方法。
force push 的风险 普通 push vs force push 1 2 3 4 5 6 7 git push origin feature git push --force origin feature git push -f origin feature
风险场景 1 2 3 4 5 6 7 8 9 10 11 12 13 远程分支状态: A - B - C (origin/feature) 你的本地分支(rebase 后): A - B - D - E (feature) 普通 push: ! [rejected] non-fast-forward 原因:C 和 D/E 没有共同的祖先 Force push: + A - B - D - E (origin/feature) - C 被删除
危险 :
如果其他人在 C 基础上开发,他们的提交会「丢失」
如果你 force push 到 main/develop 分支,影响更大
误操作后可能永久丢失代码
protected branch 设置 GitHub 设置 1 2 3 4 5 6 7 8 Settings -> Branches -> Branch protection rules 添加规则: - Branch name pattern: main - ✓ Require pull request reviews before merging - ✓ Require status checks to pass before merging - ✓ Do not allow bypassing the above rules - ✓ Do not allow force pushes <-- 关键
GitLab 设置 1 2 3 4 5 6 Settings -> Repository -> Protected branches 保护 main 和 develop: - Allowed to merge: Maintainers - Allowed to push: No one (或 Maintainers) - ✓ Prevent force push <-- 关键
命令行检查 1 2 3 4 git config branch.main.protected
reflog 恢复历史 reflog 是什么 reflog 记录了本地仓库所有 HEAD 的移动历史。即使提交被删除、rebase、reset,只要 reflog 还在,就能恢复。
1 2 3 4 5 6 7 8 9 10 git reflog git reflog show 550e8400 HEAD@{0}: reset: moving to HEAD~2 a1b2c3d4 HEAD@{1}: commit: 添加新功能 e5f6g7h8 HEAD@{2}: rebase: 应用补丁 1234567 HEAD@{3}: checkout: moving to main
恢复误删的提交 1 2 3 4 5 6 7 8 9 10 11 12 git reflog git checkout a1b2c3d4
恢复误 force push 的分支 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 git reflog origin/main git reset --hard abcdefg git push --force origin main
历史提交修改 修改最后一次提交 1 2 3 4 5 6 git commit --amend -m "新的提交信息" git add forgotten_file.txt git commit --amend --no-edit
修改历史提交 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 git rebase -i HEAD~5 pick abc1234 提交1 pick def5678 提交2 pick ghi9012 提交3 pick jkl3456 提交4 <-- 要修改 pick mno7890 提交5 pick jkl3456 提交4 -> edit jkl3456 提交4 git add . git commit --amend git rebase --continue
撤销历史提交(不修改历史) 如果需要撤销某个历史提交,但不改变历史,使用 git revert:
1 2 3 4 5 git revert <commit-hash> git revert HEAD~3
安全规范制定 1. 分支命名规范 1 2 3 4 5 6 7 main develop feature/<ticket>-xxx bugfix/<ticket>-xxx hotfix/<ticket>-xxx release/<version>
2. 提交规范 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <type >(<scope>): <subject> feat(user): 添加用户注册功能 fix(order): 修复订单支付bug docs(readme): 更新文档 style(ui): 调整样式 refactor(api): 重构API接口 test : 添加单元测试
3. Pre-push Hook 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #!/bin/sh branch=$(git symbolic-ref --short HEAD) if [ "$branch " = "main" ] || [ "$branch " = "develop" ]; then echo "不允许直接推送 $branch 分支,请使用 PR/MR" exit 1 fi echo "建议使用 'git push --force-with-lease' 代替 'git push --force'"
4. Alias 配置 1 2 3 4 5 6 7 8 9 10 11 [alias ] force-with-lease = push --force-with-lease lg = log --oneline --graph --decorate --all clean-branches = !git branch --merged | grep -v '\\*\\|main\\|develop' | xargs -r git branch -d
5. Git Config 保护 1 2 3 4 5 6 git config branch.main.protected true git config branch.develop.protected true git config --global push.useForceWithLease true
实战:误删分支恢复 场景1:删除了本地分支 1 2 3 4 5 6 7 8 9 10 git branch -D feature git reflog git checkout -b feature <commit-hash> git checkout feature
场景2:force push 后发现有问题 1 2 3 4 5 6 7 git reflog origin/feature git push --force origin def5678:feature
场景3:rebase 后提交丢失 1 2 3 4 5 6 7 8 9 10 11 git rebase --abort git reflog git reset --hard HEAD@{1} git reflog git checkout <之前的commit>
场景4:reset –hard 后恢复 1 2 3 4 5 6 7 8 9 10 git reset --hard HEAD~5 git reflog git reset --hard HEAD@{1}
最佳实践总结 应该做的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 git push --force-with-lease git status git log --oneline -5 git branch backup-before-rebase git push origin main
不应该做的 1 2 3 4 5 6 7 8 git push --force origin main git rebase origin/main
恢复能力检查清单 1 2 3 4 5 6 7 8 9 10 11 git config core.logAllRefUpdates git reflog | head -20 git branch backup-$(date +%Y%m%d%H%M%S)
总结 Git 安全操作要点:
操作
风险
安全做法
git push --force
覆盖他人代码
使用 --force-with-lease
git rebase
丢失提交
不要 rebase 公共分支
git reset --hard
丢失工作
先 git branch backup
git push 到 main
破坏主分支
使用 PR/MR
安全规范:
保护分支 :main/develop 禁止 force push
reflog :定期检查,确保历史完整
沟通 :force push 前必须通知团队
备份 :重要操作前创建备份分支
强制 :使用 --force-with-lease 代替 --force