前两天开发完一个功能,习惯性 git commit、git push——刷新远程,提交直接落在了 master。CI 已经跑了一分钟,群里有人问谁合的。
一句话结论:已 push 到共享 master → git revert;只在本地、还没 push → git reset。
一、revert 和 reset 怎么选
git revert | git reset | |
|---|---|---|
| 做法 | 新增反向 commit,原 commit 仍在 | 移动分支指针,可抹掉 commit |
| 远程 | 安全,不改写公共历史 | 需 force push,影响他人 |
| 适用 | master / 共享分支 | 本地或未 push 的分支 |
| 情况 | 用什么 |
|---|---|
| 已 push 到 master | git revert |
| 本地 commit,未 push | git reset --soft/mixed |
| 刚 push、无人 pull、负责人确认 | 极少数 reset + --force-with-lease(非常规,慎用) |
| commit 含密钥 | revert 当前代码 + 必须 git-filter-repo 清历史 + 轮换密钥(revert 删不掉历史里的密钥) |
二、已 push:revert 撤回 + 处理 SOP
别慌,按顺序来:
| 步骤 | 做什么 |
|---|---|
| 1. 停 | 不要立刻 force push,先看 CI 和同事有没有 pull |
| 2. 查 | git log origin/master -5 |
| 3. 撤 | git revert HEAD(多个 commit 见下)→ git push origin master |
| 4. 告 | 通知团队 pull,别在误提交上继续开发 |
| 5. 补流程 | 需要 MR 记录时,走第三节 Case 2 |
| 6. 防 | 配分支保护 + pre-push hook |
git log --oneline -5
git revert HEAD # 单个 commit
git push origin master
# 多个误提交:一次性 revert 再提交(比逐个 revert 少冲突)
git revert --no-commit HEAD~2..HEAD
git commit -m "Revert mistaken commits on master"
git push origin master
冲突:手动改完冲突文件 → git add <file> → git revert --continue。
三、补走 MR 流程:三种 merge 场景
图例:A = 误推的功能 commit;B/C = 分支上后续 commit;R = revert;M = MR 合并。
| Case | 操作 | 结果 |
|---|---|---|
| 1 | 拉分支,master 不 revert,直接 merge | 无效——分支与 master 都有 A,提示 Already up to date |
| 2 | 拉分支 → master revert → MR merge | 标准做法,代码回到误推后状态,历史留 Review |
| 3a | master 仍含 A,分支上又有 B、C,直接 merge | 能,只合入 B、C 增量 |
| 3b | master 已 revert(有 R),分支含 A、B、C,再 merge | 能,A+B+C 一并合回 |
最常见走 Case 2;分支上若还继续提交了 B、C,按 3a 或 3b 处理。
顺序不能反:
正确:先拉分支(含 A)→ master revert(产生 R)→ MR 合回(产生 M)
错误:先 revert 再拉分支 → 分支没有功能 commit,合不回来
错误:只拉分支不 revert → Case 1,merge 无效
Case 2 完整流程(补规范标准做法,历史上会多 R 和 M,但比 force push 安心):
git checkout master && git pull
git checkout -b feature/order-fix # ① 从含误提交的 master 拉分支
git push -u origin feature/order-fix
git checkout master
git revert HEAD # ② 单个;多个见第二节
git push origin master
# ③ 提 MR:feature/order-fix → master
MR 合并后 master 代码与误推后一致。分支若还有 B、C(Case 3),合入后会比当初误推时更多:
Case 2:master: ... ── A ── R ── M feature: ... ── A
Case 3:master: ... ── A ── R ── M feature: ... ── A ── B ── C
四、还没 push:用 reset
git reset --soft HEAD~1 # 撤销 commit,改动留暂存区
git reset HEAD~1 # 改动留工作区
git reset --hard HEAD~1 # 彻底丢弃(慎用)
若还想补 MR 流程:
git branch feature/xxx 带走 commit → master 上 reset 回远程 → push feature → 提 MR。
五、预防:靠门禁,不靠口头约定
远程(团队必配):Gitee / GitHub 保护 master——禁止 direct push,必须 PR/MR + Review。配好后直推会被拒绝。
本地兜底:.githooks/pre-push 拦截 master 直推:
#!/bin/sh
for p in master main; do
b=$(git symbolic-ref HEAD 2>/dev/null | sed 's|.*/||')
t=$(git rev-parse --abbrev-ref @{u} 2>/dev/null | sed 's|.*/||')
[ "${t:-$b}" = "$p" ] && echo "禁止 push 到 $p,请走 feature + MR" && exit 1
done
git config core.hooksPath .githooks && chmod +x .githooks/pre-push
习惯:开功能先 git checkout -b feature/xxx;push 前看 git branch --show-current。CI 侧 master 只跑测试、不自动发布。
六、参考内容
- git-revert · git-reset · git-push
- Gitee 保护分支 · GitHub protected branches
- git-filter-repo(密钥误提交清历史)