Workflows

发布后热修失败,如何回滚与稳定主线

当发布后热修本身引入问题时,优先判断回滚粒度、共享影响和后续补丁路径,而不是立刻继续叠加修复。

适合谁看
  • 要把命令组合成稳定流程的团队成员
  • 需要处理协作顺序和分支边界的人
前置知识
  • 知道 fetch / pull / push / branch 的基本作用
  • 能理解一条分支为什么会分叉
常见风险
  • 照抄流程却没确认当前分支关系
  • 在共享分支上用错整合方式

热修失败时,最危险的往往不是 bug 本身,而是慌着继续往生产线上叠修复。
事故一旦进入“再补一刀看看”的状态,主线、发布线、标签和排查上下文都会一起变乱。

一条更稳的热修回滚路径先判断影响范围,再做最小回滚,最后重做修复并回补主线。事故处理的重点不是优雅,而是把分支重新带回可解释状态。
先判断什么
线上症状hotfix 提交范围发布标签主线 / 发布线状态
目标结果
服务恢复稳定主线清晰修复可回补
先恢复稳定,再追求历史整洁。
已经共享并部署的 hotfix,不要优先想着 reset

如果热修已经被别人拉取、已经进入发布线,或者已经对应线上部署记录,通常应优先考虑 git revert。共享历史里最怕的是“回滚手段本身又改写了上下文”。

第一步不是继续提交,而是判断事故边界

先把这几个问题问清楚:

  1. 问题是否只来自这次 hotfix
  2. 要回滚的是一个提交、一个 patch,还是整次发布范围
  3. 当前线上部署对应哪个 tag 或提交
  4. 哪条分支是现在最可信的真相来源

这些判断的价值在于:它能阻止你把“代码修复”问题,误做成“历史结构”问题。

为什么很多情况下优先考虑 revert

如果 hotfix 已经共享并部署,git revert <hotfix-commit> 通常比 reset 更稳,原因不是它更高级,而是它更容易保留事故轨迹:

  • 历史可追踪
  • 团队成员更容易同步
  • 发布线和主线都更好解释
  • 后续复盘时能看清“修复”和“撤销修复”各自发生了什么
git revert <hotfix-commit>

如果有多次相关提交,也应该先确认范围,再决定是单次 revert 还是多次 revert,而不是直接把分支倒回到某个旧位置。

一个更稳的事故顺序

1. 先标记当前线上状态

如果目前线上状态已经不稳定,第一件事不是继续改代码,而是先把当前状态标清楚。
这可以是:

  • 一个 tag
  • 一条 incident 记录
  • 一次 release note 备注

核心目的是让“问题版本”有名字,后面排查和沟通都更容易。

2. 回滚最小必要范围

如果只是 hotfix 本身出问题,就优先回滚 hotfix,而不是把整次发布一起推翻。
如果是整次发布边界本来就判断错了,再考虑更大粒度的撤回。

3. 在独立分支重做修复

不要在已经混乱的发布线里继续边试边修。
回滚让系统先稳定,新的修复应该在新的分支里完成、验证,再决定怎么回补。

4. 再决定如何回补主线和发布线

真正危险的地方往往在这里:
热修失败不只是“线上坏了”,还意味着主线和发布线可能已经分叉表达不同状态。后续必须明确:

  • 哪条分支先恢复正确修复
  • 是否需要 backport
  • 是否需要补新的 tag

什么时候 reset 反而更危险

以下几种情况里,直接 reset 往往会让问题更复杂:

  • 提交已经 push 到共享分支
  • 别人已经基于这次 hotfix 继续工作
  • 发布系统、CI、tag 或审计链已经记录了这次提交
  • 你还没完全确认要回滚的粒度

这类场景里,reset 的问题不是“命令不能用”,而是它会把协作上下文一起抹掉。

一个适合团队执行的最小事故协议

可以非常简单:

  1. 先确认线上对应提交和标签
  2. 优先使用可追踪的回滚方式
  3. 先稳定系统,再重做修复
  4. 修复完成后再统一回补主线和发布线
  5. 所有事故分支、标签和回滚动作都留下明确说明

这套协议的价值是让“事故处理”不只靠个人经验,而是能被团队复用。

常见误区

“线上坏了,就赶紧在当前分支继续修”

这通常会把第一层事故变成第二层事故。先稳住系统,再重做修复,更容易控制。

“回滚就是把分支退回去”

在共享历史里,回滚更常常意味着新增一条明确的“撤销历史”,而不是抹掉已有历史。

“只要 hotfix 修好了,主线自然也就对了”

不一定。你还需要确认发布线、主线、tag 和后续补丁路径是不是重新对齐了。

练习:模拟一次 hotfix 回滚决策

重点不是练命令本身,而是练习在共享历史里如何先稳定、再修复、再回补。

准备
git switch main
git switch -c hotfix/login-timeout
echo patch >> app.txt
git commit -am "hotfix: adjust login timeout"
# 假设这次提交已经发布并出现问题
步骤
  1. 先记录当前状态对应的 tag 或提交
  2. 判断是否只需回滚 hotfix 提交
  3. 用 `git revert <sha>` 设计一条可追踪回滚路径
  4. 新开分支重新实现修复并补验证
  5. 再决定主线和发布线各自如何回补
你应该观察到
  • 回滚动作和重做修复是两件不同的事
  • revert 能保留事故路径
  • 分支和 tag 会成为排查上下文的一部分
  • 主线和发布线需要被重新对齐
常见错误
  • 没判断范围就直接 reset
  • 在事故分支上连续叠修补丁
  • 回滚完不处理主线与发布线的后续一致性

一个最重要的原则

事故处理中,先把系统带回稳定状态,再追求历史优雅。
稳定优先,漂亮其次,可解释性贯穿始终。