记一次严重的 Git 分支提交错误与修复复盘
1. 问题背景:错把 develop
的代码合入了 main
事故性质: 严重分支污染事故。
事故描述 : 在项目开发过程中,一系列(共10个)本应提交到 develop
开发分支的功能,被错误地直接提交并推送到了受保护的 main
主分支。由于 main
分支是生产环境的直接代码源,且其提交历史已被共享,这次事故对代码库的稳定性和版本历史的清晰度构成了严重威胁。
受影响的提交范围:
- 从
8ec3e9c
(refactor(test): 统一MinIO测试配置为环境变量驱动
) - 到
173fb2b
(refactor(api): 统一视频上传并修复服务初始化
)
2. 核心挑战:如何在不"炸毁"仓库的前提下修复?
主要挑战在于,这些错误的提交已经被推送到了远程共享分支 (origin/main
)。在这种情况下,严禁使用 git reset --hard
或 git rebase
等会重写历史的命令。因为这会强制改变已共享的提交历史,导致团队其他成员的本地仓库与远程仓库产生严重冲突和不一致,引发更大的混乱。
因此,我们必须采用一种既能修正错误,又不破坏现有提交历史的安全策略。
3. 解决方案:"撤销"与"移植"
我们采用了"撤销并移植" (Revert and Cherry-pick) 的两阶段策略,这是处理此类问题的行业标准最佳实践。
-
阶段一:修复
main
分支- 目标 : 将
main
分支的内容恢复到事故发生前的状态。 - 工具 :
git revert
。 - 原理 :
git revert
会创建一个新的提交 ,这个新提交的内容与目标提交的内容正好相反,从而"撤销"目标提交的更改。它不删除历史,而是在历史之上追加一次修复,是修改共享分支的安全方式。
- 目标 : 将
-
阶段二:更新
develop
分支- 目标 : 将被撤销的10个提交正确地应用到
develop
分支。 - 工具 :
git cherry-pick
。 - 原理 :
git cherry-pick
可以选择一个或多个现有的提交,并像"复制-粘贴"一样,将它们的更改在当前分支上重新应用一遍,创建出内容相同但哈希值不同的新提交。
- 目标 : 将被撤销的10个提交正确地应用到
4. 详细修复流程
阶段一:在 main
分支上执行 revert
此阶段的目标是安全地"撤销"main
分支上的10个错误提交。
flowchart TD
subgraph "阶段一:修复 Main 分支"
A[开始] --> B{识别出 main 分支上
10个错误提交}; B --> C[执行 git revert --no-commit
准备批量撤销]; C --> D[执行 git commit
将所有撤销合并为
一个修复提交]; D --> E[执行 git push origin main
将修复推送到远程]; E --> F[结束: main 分支内容恢复]; end
10个错误提交}; B --> C[执行 git revert --no-commit
准备批量撤销]; C --> D[执行 git commit
将所有撤销合并为
一个修复提交]; D --> E[执行 git push origin main
将修复推送到远程]; E --> F[结束: main 分支内容恢复]; end
具体执行的命令:
-
定位 revert 的范围:
bash# 找到系列提交的起点 (8ec3e9c) 的上一个提交 git rev-parse 8ec3e9c~1 # 输出: 59944d65b252f6603c856b861bdb2e02320e37ce
-
执行 revert 操作:
bash# 撤销从 59944d6 (不含) 到 173fb2b (含) 的所有提交 # --no-commit 标志让我们可以将所有 revert 合并为一次提交 git revert --no-commit 59944d6..173fb2b
-
创建清晰的修复提交:
bashgit commit -m "revert: Revert 10 commits to move to develop branch"
-
同步到远程:
bashgit push origin main
阶段二:在 develop
分支上执行 cherry-pick
此阶段的目标是将那10个功能提交正确地"移植"到 develop
分支。
flowchart TD
subgraph "阶段二:更新 Develop 分支"
A[开始] --> B[切换到 develop 分支];
B --> C[执行 git cherry-pick
将10个提交
应用到 develop 分支]; C --> D[执行 git push origin develop
将新提交推送到远程]; D --> E[结束: develop 分支更新完毕]; end
将10个提交
应用到 develop 分支]; C --> D[执行 git push origin develop
将新提交推送到远程]; D --> E[结束: develop 分支更新完毕]; end
具体执行的命令:
-
切换分支:
bashgit checkout develop
-
执行 cherry-pick 操作:
bash# 将 59944d6 (不含) 到 173fb2b (含) 的提交应用到当前分支 git cherry-pick 59944d6..173fb2b
-
同步到远程:
bashgit push origin develop
5. 经验总结
本次事故通过标准的 git revert
和 git cherry-pick
流程得到了安全、彻底的解决。
main
分支: 代码内容已恢复到事故前的状态,同时保留了清晰、不可篡改的提交历史,记录了从事故发生到修复的全过程。develop
分支: 成功接收了所有本应属于它的功能提交,开发工作可以正常继续。- 团队协作: 由于未使用任何重写历史的危险命令,本次修复对团队其他成员完全透明,不会造成任何协作冲突。
这次事故提醒我们,在执行 git commit
和 git push
操作前,务必通过 git status
和 git branch
确认当前所在分支,避免类似问题再次发生。