用途:把改好的文件推上线,出问题时能退回去。核心不是「怎么推」,是先能退,再往前。
覆盖:静态站点、单机部署、有可写备份位或版本库的场景。
产出:一次可追溯的发布记录,加一个验证过的回退点。
拿到就能跑:第二步「建回退锚点」不能跳。跳过它,后面每一步都在赌。
推之前先看清现状。三处 = 本地源 / 镜像(或备份仓)/ 线上。
对每个要动的文件,取三样:大小、md5、修改时间。
三处一致 → 说明没人动过,可以推。 线上与本地不同 → 正常,这正是要推的原因。 线上已经是新的 → 停。有人先推过了,先查清是谁、推了什么,再决定。
⛔ 不要跳过这步直接推。「线上是什么」不能靠记忆,只能靠取回来比对。
输出:一张三处的 md5 对照表,以及一句结论:线上当前是旧版 / 已是新版 / 文件缺失。
把改动前的线上文件复制到备份位,并当场核对 md5。
唯一有效的判据是:备份件的 md5 等于改动前那一版的 md5。 不是时间,不是文件名。
这一步最容易错,两种典型错法:
如果备份丢了,还有一条路:从版本库历史里取改动前的那一版。这比任何备份都可靠,因为它不由操作者决定。
输出:备份路径 + 备份件 md5 + 「等于改动前」的核对结果。
自动化任务、定时脚本、另一个人的部署,都可能与这次撞车。
查三样:有没有相关进程在跑、有没有锁文件、目标目录最近有没有别的写入。
有任务在跑 → 等它结束。并发写入同一个索引文件,最后谁赢取决于毫秒,输的一方内容静默消失。
输出:一句确认——「无并发」或「已等 X 结束」。
两条规矩:
输出:落位后的 md5 清单,与本地逐条相同。
必须从线上取回来验证,不能看本地文件、不能看编辑器预览。 预览层会留旧件,它显示的「成功」不作数。
请求要带两样东西:
Cache-Control: no-cache —— 否则可能读到缓存,看到的是上一次的结果。验四样,缺一不可:
| 验什么 | 判据 |
|---|---|
| 状态码 | 200;403/404/无响应都要当失败处理 |
| 字节数 | 与本地文件一致(差几个字节通常是换行被改) |
| md5 | 与本地一致(最硬的一条) |
| 关键文字 | 这次改的那句话,在取回的内容里能搜到 |
输出:四样实测值,全部对上才算部署完成。
输出:三处一致(本地 / 镜像 / 线上)+ 一条版本记录。
按现象查,不要从头重来。
| 现象 | 最可能的原因 | 处理 |
|---|---|---|
| 双侧 md5 不一致 | 传的是旧文件 / 落位失败 | 核对源文件 md5,重传 |
| 200 但内容还是旧的 | 缓存 | 加 no-cache 重取;仍旧则查中间层缓存 |
| 无响应 / 连接被断 | UA 被安全规则拦了 | 换完整浏览器 UA 再试 |
| 403 / 404 | 路径写错,或落位后属主不对 | 查实际落位路径与权限 |
| 内容对,但页面样式不对 | 组件样式没重新合并 | 重新合并样式表再推一次 |
| 改了一个页面,别的页也变了 | 这页是从别处派生的 | 重跑派生,别手改结果 |
| 回滚后还是不对 | 备份件本身是新的 | 用版本库历史重建,不要相信备份目录 |
最后一条是最贵的:回滚失败通常不是回滚动作错了,是回退点本身就选错了。
发布:<改了哪些文件>
回退点:<备份路径> · md5 已核等于改动前
推前状态:线上旧版 / 已是新版 / 缺失
实测:状态码 200 · 字节数一致 · md5 一致 · 关键文字命中
收尾:镜像已补 / 派生已重建 / 已提交版本库
未做:<这次没覆盖什么>
files: [{path, md5_before, md5_after, bytes}]
anchor: {path, md5, verified_equal_before: true|false}
precheck: {online_state: old|new|missing, concurrent: false}
verify: {status, bytes_match, md5_match, probe_text_found}
derived: {sitemap: rebuilt|skipped, index: rebuilt|skipped}
vcs: {committed: true|false, ref}
anchor.verified_equal_before 为 false 时,后面全部不算通过——没有可靠的回退点,这次发布不成立。
已发布 N 个文件,回退点已核,线上四样全对。 或 发布未完成:卡在<哪一步>。
⛔ 不要说「大概好了」。四样里缺一样就是未完成。
2026-11-06。命令与工具因环境而异,但六步的顺序和四样实测判据通用。与所执行系统的既定标准冲突时,以那个系统的标准为准。