用途:确认线上跑的就是你以为的那一版。不是「应该推上去了」,是「取回来比对过」。
覆盖:静态站点、文件同步、多副本环境(本地源 / 备份镜像 / 线上)。
产出:一致 / 不一致,差在哪个文件、差在哪一类,以及下一步该动哪边。
拿到就能跑:先取指纹再比,不要用修改时间判断——时间在传输、解压、复制之后都会变。
核对的前提是知道要比哪几个地方。常见三处:
本地源 你正在改的那份
镜像/备份 另一台机器或版本库里的那份
线上 真正对外提供访问的那份
两处也要比(本地 ↔ 线上),三处更稳——镜像存在的意义是:当本地和线上都不一样时,它能告诉你是谁先偏离的。
⛔ 不要只比「我印象里推过的那几个文件」。按目录整体比,才会发现「多了」和「少了」——单个文件比得再准,也发现不了没人提过的漏项。
输出:两端(或三端)的目录清单,含每个文件的相对路径。
对每个文件取四样,缺一样都可能误判:
| 指纹 | 能发现什么 |
|---|---|
| 字节数 | 内容变了、换行被改、编码变化 |
| md5 | 内容是否逐字节相同(最硬) |
| 换行格式 | CRLF / LF 混用——两边内容看着一样,字节不同 |
| BOM | 文件头多了三个不可见字节,某些解析器会读错 |
四项里字节数和 md5 是必取,换行与 BOM 在 md5 不同而肉眼看着一样时才需要查。
⚠️ 时间戳不是指纹。 复制、传输、解压都会重写修改时间,它只说明「这个文件什么时候被动过」,不说明内容是什么。
输出:一份「路径 → 四项指纹」的清单,每端一份。
逐文件比对,只会落在三种情况之一:
| 状态 | 含义 | 通常该动哪边 |
|---|---|---|
| 相同 | 四项全对 | 不动 |
| 内容不同 | 两边都有,但 md5 不同 | 先判断哪边是新的(见第四步),再决定方向 |
| 只在一处存在 | 一方有、一方没有 | 可能是漏推,也可能是漏删——先确认这个文件该不该在 |
⛔ 关键判断:「只在一处存在」不能默认「补上就好」。多出来的那一份可能是旧版残留,补上去等于把废弃文件又推了一遍。
输出:三类的文件数与清单。
不要一上来就重传。按这个顺序:
Cache-Control: no-cache、带完整浏览器 UA —— 缓存和拦截会返回另一个东西,看起来像内容不对。输出:每个不一致文件的处理动作与处理后指纹。
| 现象 | 最可能的原因 | 处理 |
|---|---|---|
| md5 不同,字节数相同 | 内容真的变了 | 按第四步第 3 条判断哪边对 |
| 字节数差 1–3 | 换行或 BOM | 统一换行格式后重传 |
| 字节数差很多 | 不同版本 | 查版本记录,别猜 |
| 线上取回的 md5 与文件不同,但字节数相同 | 传输层压缩或头信息差异 | 加「不压缩」请求头再取一次 |
| 比对工具说「一致」,但页面明显不对 | 工具只比了文件名或数量 | 换成逐个 md5 比 |
| 某些文件总是「多」出来 | 没排除临时文件、系统生成文件 | 加排除规则,不是每次都人工忽略 |
| 全部不一致 | 比的目录层级错了(多套了一层或少了) | 先确认路径基准,再比 |
最后一条最容易发生:两端的目录基准不同,比出来会全线不同,看着像严重问题,其实只是路径少了一层。
核对范围:<哪两端或三端> · <目录>
结果:一致 / 不一致(N 个文件)
差异:
内容不同 x 个:<路径>(本地 md5 … / 线上 md5 …)
只在一处 y 个:<路径>(在哪端)
判断:<哪边是对的,依据是什么>
处理:<动了什么>
复核:<处理后再取一次的结果>
scope: {ends: [local, mirror, online], root}
summary: {same: n, differ: n, only_local: n, only_online: n}
differ: [{path, md5_each: {...}, bytes_each: {...}, verdict}]
only: [{path, present_in: [...]}]
authoritative: local|mirror|online
action: none|push|pull|delete
recheck: pass|fail
authoritative 不能空——说不清哪边对,就不该动任何一边。
两端一致,N 个文件全对。 或 不一致:X 个内容不同、Y 个单边存在,判断以 <哪端> 为准。
⛔ 不要说「差不多一致」。一致是二值的。
2026-11-06。比对工具与环境无关;四项指纹与三种状态判据通用。与所执行系统的既定标准冲突时,以那个系统的标准为准。