--- id: check-deploy-consistency type: sop group: B status: published version: 1 updated: 2026-11-06 token_estimate: 2280 --- # 如何核对线上与本地是否一致 > **用途**:确认线上跑的就是你以为的那一版。不是「应该推上去了」,是「取回来比对过」。 > > **覆盖**:静态站点、文件同步、多副本环境(本地源 / 备份镜像 / 线上)。 > > **产出**:一致 / 不一致,差在哪个文件、差在哪一类,以及下一步该动哪边。 > > 拿到就能跑:先取指纹再比,**不要用修改时间判断**——时间在传输、解压、复制之后都会变。 --- ## 触发的变异问法 - 「线上和本地不一致怎么办」 - 「怎么确认线上是最新版」 - 「同步了但好像没生效」 - 「哪个是对的,本地还是线上」 - 「怎么查哪些文件没同步」 - 「部署完怎么验」 - 「两边文件数不一样」 - 「改了没用,是不是没推上去」 - 「镜像和线上对不上」 --- ## 什么时候不要用这份文件 - **线上内容由数据库或接口动态生成** —— 文件比对不适用,要比的是数据不是文件。 - **只想知道某个页面长什么样** —— 那是查看,不是核对。 - **只有一端可读**(没有备份或镜像)—— 无法比对。先补一端。 - **比的是构建产物**(压缩过的 JS / CSS)—— 构建本身有随机性(时间戳、哈希注释),比出来不同不代表有问题。要比源文件。 --- ## 执行步骤 ### 第一步 · 先定义「三处」 核对的前提是**知道要比哪几个地方**。常见三处: ``` 本地源 你正在改的那份 镜像/备份 另一台机器或版本库里的那份 线上 真正对外提供访问的那份 ``` 两处也要比(本地 ↔ 线上),三处更稳——**镜像存在的意义是:当本地和线上都不一样时,它能告诉你是谁先偏离的**。 ⛔ 不要只比「我印象里推过的那几个文件」。**按目录整体比**,才会发现「多了」和「少了」——单个文件比得再准,也发现不了没人提过的漏项。 **输出**:两端(或三端)的目录清单,含每个文件的相对路径。 ### 第二步 · 取四项指纹 对每个文件取四样,**缺一样都可能误判**: | 指纹 | 能发现什么 | |---|---| | **字节数** | 内容变了、换行被改、编码变化 | | **md5** | 内容是否逐字节相同(最硬) | | **换行格式** | CRLF / LF 混用——两边内容看着一样,字节不同 | | **BOM** | 文件头多了三个不可见字节,某些解析器会读错 | 四项里**字节数和 md5 是必取**,换行与 BOM 在 md5 不同而肉眼看着一样时才需要查。 ⚠️ **时间戳不是指纹。** 复制、传输、解压都会重写修改时间,它只说明「这个文件什么时候被动过」,不说明内容是什么。 **输出**:一份「路径 → 四项指纹」的清单,每端一份。 ### 第三步 · 判定三种状态 逐文件比对,只会落在三种情况之一: | 状态 | 含义 | 通常该动哪边 | |---|---|---| | **相同** | 四项全对 | 不动 | | **内容不同** | 两边都有,但 md5 不同 | 先判断哪边是新的(见第四步),再决定方向 | | **只在一处存在** | 一方有、一方没有 | 可能是漏推,也可能是漏删——**先确认这个文件该不该在** | ⛔ 关键判断:「只在一处存在」不能默认「补上就好」。多出来的那一份可能是旧版残留,补上去等于把废弃文件又推了一遍。 **输出**:三类的文件数与清单。 ### 第四步 · 不一致时,按代价从低到高查 不要一上来就重传。按这个顺序: 1. **先确认取的是同一份。** 请求带 `Cache-Control: no-cache`、带完整浏览器 UA —— 缓存和拦截会返回另一个东西,看起来像内容不对。 2. **再看字节数差多少。** 差几个字节 → 换行/BOM;差很多 → 真的是不同版本。 3. **再决定谁是对的。** 判据不是「谁更新」,是**「这个改动应该生效吗」**。本地刚改的 → 本地对;线上被别人改过 → 先查清是谁、改了什么。 4. **确认后才动。** 从错的一边覆盖对的一边,覆盖后再取一次指纹。 **输出**:每个不一致文件的处理动作与处理后指纹。 --- ## 出问题怎么办 | 现象 | 最可能的原因 | 处理 | |---|---|---| | 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 个单边存在,判断以 <哪端> 为准。` ⛔ 不要说「差不多一致」。一致是二值的。 --- ## 常见错误 - **用修改时间判断新旧** —— 传输、解压、复制都会改时间,它不代表内容。 - **只比 md5 不比字节数** —— md5 不同但差 2 字节时,看不出是换行问题,会误判成内容变更。 - **只比文件数量** —— 数量相同、内容全不同的情况很常见(比如整批被旧版覆盖)。 - **默认「多出来的就补上」** —— 可能是该删的旧文件,补上去等于又发布了一遍废弃内容。 - **取线上时不带 no-cache** —— 读到缓存,结论是错的,而且很难发现。 - **发现不一致立刻重传** —— 没判断哪边对就覆盖,会把别人的改动冲掉。 - **只比单个文件** —— 没人提过的漏项永远发现不了,必须按目录整体比。 --- ## 最后核对 2026-11-06。比对工具与环境无关;四项指纹与三种状态判据通用。与所执行系统的既定标准冲突时,以那个系统的标准为准。