事情从一个很小的动作开始,我想确认本地仓库和 GitHub 上的提交对不对得上。
结果对不上。本地 main 停在 4f78766,GitHub 上已经走到 d0c55d3,中间隔着 5 个提交,工作区里还躺着 7 个没提交的改动。
那 7 个改动的内容很能说明问题,全是把文档里的 A:\work_zone 改成 E:\work_zone。这个仓库曾经在 A 盘,后来搬到了 E 盘,文档里的路径没跟着改。改的时候大概想的是「反正我自己知道在哪」,于是就这么放着了。
A 盘这个细节后面还会再出现一次。
#一、先把落差补上
拉齐没什么悬念。git pull --ff-only 快进 5 个提交,进来两篇新文章,外加一个 head.ejs 的修复。page 类型页面的 tags 是数组,没有 each 方法,关于页因此整页空白,改成 forEach 就好了。
工作区那 7 个盘符改动跟远端提交零文件重叠,没冲突。顺手把文档补齐推了上去,这条线结束。
#二、dependabot 的那颗补丁
远端还挂着一个 dependabot 开的 PR,把 sharp 从 0.35.3 升到 0.35.4。
打开只有两个文件,package.json 一行、package-lock.json 240 行。有意思的是那行 "sharp": "^0.35.3",caret 范围本来就允许 0.35.4,所以真正起作用的是 lock 文件把锁定版本从 0.35.3 提到 0.35.4。不合并的话,npm ci 装的还是老版本。
sharp 在这个仓库只有一处用途,就是 scripts/gen-thumbs.js 生成 360px 缩略图,挂在 npm run build 里,Cloudflare 每次构建都会跑它。补丁级升级,风险低。
合并方式选了 rebase。仓库历史是纯线性的(git log --merges 一条都没有),rebase 能让 dependabot 那一条原样落到 main 上,保留它的作者署名和 Signed-off-by 尾注,也不会多出一个以操作者署名的合并提交。
合完顺手开了仓库的 delete_branch_on_merge。这个开关之前是关着的,我想手动把 dependabot 那个残留分支删掉时,DELETE 请求返回 422 Reference does not exist,说明 GitHub 在合并那一刻就已经自己清掉了,白点一次。
#三、8 个漏洞里有 4 个是自己钉死的
npm 在装依赖时报了 8 个漏洞,既然要动依赖就一并处理。
先看依赖树的形状。13 个直接依赖,全树 252 个唯一包名。最重的是 hexo-renderer-marked,一个渲染器拖进 104 个包,因为它带了个 jsdom。19 个包存在多版本共存,entities 一个包有 5 个版本(2.2.0 / 3.0.1 / 4.5.0 / 6.0.1 / 7.0.1),hexo-util 有 3 个(2.7.0 / 3.3.0 / 4.0.0)——sitemap 要 2.x、marked 渲染器要 3.x、hexo 核心要 4.x,谁都不让。
npm audit fix 修掉 4 个,dompurify 从 3.4.12 到 3.4.15、js-yaml 从 4.3.0 到 4.3.2、morgan 从 1.11.0 到 1.12.1、nanoid 从 3.3.16 到 3.3.19。
剩下 4 个 high 全在 brace-expansion / minimatch 那条链上,npm audit fix 碰不动。查下来原因在仓库自己身上。
1 | |
这两个钉版是 8 月 1 日那次安全加固提交留下的,提交信息写着「overrides 锁定 brace-expansion/minimatch 漏洞版本」。2.0.1 和 9.0.5 在当时确实是补丁版。问题是上游后来又发了新公告,受影响区间分别是 >=2.0.0 <2.1.4 和 >=9.0.0 <9.0.7,这两个版本正好落在里面。
钉版本是为了修漏洞,顺带把下一个漏洞也钉住了。抬版到 2.1.7 / 9.0.9 之后,npm audit 归零。
顺手确认一下影响面这些包全在构建期执行,站点是 Cloudflare Pages 上的静态产物,运行时碰不到它们。唯一有安全实质的是 dompurify,它挂在 marked 渲染器上做 HTML 净化,属于正文 XSS 防线的一环。
#四、行尾判断错了两回
扫描行尾时发现 scripts/gen-thumbs.js(115 行)和 scripts/img-thumbs.js(17 行)是 CRLF。仓库的约定是 LF,.gitattributes 里写着 * text=auto eol=lf,CLAUDE.md 也声明「行尾 LF 已统一」。
第一次判断错了。我用 grep -c $'\r' 统计,报出 docs/ 下三个文件全是 CRLF。那是 Git Bash 下的假阳性,换成字节级统计,docs/ 是干净的 LF。
第二次判断也错了,方向相反。我以为这两个脚本在库里存的是 CRLF,把磁盘副本改成 LF 就能产出一笔「统一行尾」的提交。改完 git status 报脏,我一度以为验证通过,再查才发现库内和磁盘的字节数完全相同。
1 | |
库内本来就是 LF。磁盘上是 CRLF,git 的 clean filter 一直在替它们归一化,所以状态一直干净,提交历史里从来没有 CRLF。
这笔「统一行尾」对仓库来说什么都没改。归一只让本地磁盘和仓库存储终于一致,index 与工作区字节相同,进不了提交。要追问这两个脚本的 CRLF 从哪来,历史里也查不到。
#五、拍平那层多余的目录
我平时打开文章要走进 E:\work_zone\Blog\blog\source\_posts\。那层多出来的 blog\ 一直觉得别扭,这次正好想了一下,这结构是必须的吗?
不是。外层 Blog\ 里**只有** blog\ 一个子目录,它本身不是 git 仓库,也没有兄弟项目。GitHub 上仓库的根对应的就是内层那层目录。Hexo 的配置全是相对路径(source_dir: source),.git/config 里没有绝对路径,git 也不关心文件夹叫什么。
这层嵌套没有承担任何职责,它就是当年在 A 盘建目录时顺手多套的一层,和文档里那些 A:\work_zone 是同一批遗留。
决定拍平到 E:\work_zone\Blog,让仓库根就是这一层。执行时撞了个钉子。
改不了名外层
Blog\被占用了,某个进程的当前目录正好是它。Windows 不允许重命名进程 cwd 或其祖先目录,mv直接失败。
绕过去的方法是把内层的 22 个条目逐个上提到 Blog\,再删掉空壳 blog\。全程不碰那个被占用的外层目录,目标结构照样达成。同盘符改名是瞬时操作,node_modules、.git、内嵌的 .deploy_git 都跟着走,没有复制开销。
搬完验证了一遍。git 仓库根变成 E:/work_zone/Blog,HEAD 没变,npm run build 正常,244 个产物文件一个不少,sharp 的原生绑定照常加载(libvips 8.18.6)。
#六、收尾的 16 处路径引用
目录搬了,引用得跟着改。第一轮脚本只匹配绝对路径,漏掉 6 处相对写法(../../Blog/blog/docs/...),复查时才发现。
docs/ARCHITECTURE.md 里那个 ASCII 架构图,路径短了 5 个字符,右边框会歪,按字符数补了 5 个空格让 │ 重新对齐。
编辑器的最近文件列表里还留着 8 条旧路径,没改。点开提示文件不存在而已,不值得为它动编辑器的配置文件。
#七、尾声
回头看,这一整天处理的是三笔旧账加一层多余的目录,四件事性质完全一样。目录多套一层能用就行,文档里留着 A 盘路径自己能看懂就行,overrides 钉着当时最新的补丁版漏洞修了就行,脚本是 CRLF 但 Node 照跑就行。
每一个决策在当下都站得住,没有一个是错误,也没有一个需要立刻回头检查。它们只是被放在那里,直到某个不相干的任务把它们一次性串起来,这次只是想确认 commit 对不对得上。
这类遗留不会自己消失,也不会自己变坏,下一次动手时才结算。
评论