事情是这样的:想给博客改点东西,打开电脑才发现——本地的 Hexo 工程目录不见了。
一开始还比較淡定,想着”GitHub 上应该有吧”。结果一查,事情比想象的有意思。
先确认:源码到底还在不在
GitHub 仓库里只有”成品”
打开 ranshuo-ICer.github.io 这个仓库,里面是这些东西:
1 | index.html archives/ categories/ tags/ css/ js/ font/ image/ images/ pdf/ search.xml |
眼熟吗?这就是 hexo generate 出来的 public/ 目录,也就是部署产物,不是源码。
再看提交历史:一共 20 个提交,全部叫 Site updated: 2023-xx-xx xx:xx:xx,第一个提交的目录树里只有一个 placeholder 文件。分支也只有 master 一个。
结论很明确:源码从来没有进过这个仓库。当初用 hexo deploy 推上去的,只是生成好的静态页面。
本机也翻遍了
- 全盘(C/D/E/F/G)扫描
_posts目录、*hexo*文件:没有 - 回收站:没有
- OneDrive、Documents、Desktop、Downloads、Kuaipan:没有
- WSL、hexo 命令、npm 安装日志、编辑器”最近打开”:都没有痕迹
说明这个博客当初是在另一台机器(或者另一个环境)上搭的,本机从来没留下过源码。
原始工程文件确实找不回来了。
那就反过来:从成品推出源码
好在站点还在线,而静态站点里其实藏着几乎全部信息。关键是别急着”手抄”,要去找那些机器能读出来的痕迹。
1. 主题配置直接内嵌在页面里
Keep 主题(其它主题也类似)会把配置序列化到页面里,方便前端 JS 使用:
1 | <script id="hexo-configurations"> |
把这段 JSON 拿出来,主题配置基本就齐了:配色、logo/头像/favicon、首屏签名、菜单、本地搜索、PJAX、lazyload、评论(giscus 的 repo / category id 都在里面)。
2. 用”特征”把主题版本钉死
CDN 链接会暴露主题版本:
1 | <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.6.1/source/font/css/fontawesome.min.css"> |
但光有版本号不够——npm 上发布的 3.6.1 并不带 giscus 评论,而线上站点有。于是去比对主题仓库的历史:
- 线上
css/style.css里没有.page-numbers(说明还没加”页码”特性) - 页脚没有全站字数
- 但评论用的是 giscus
结果正好卡在主题仓库的 edbb953(2023-01-18)这个提交上:package.json 里还写着 3.6.1,但已经有 giscus 支持。把这一版导出成 themes/keep 就还原了。
找这类”版本特征”比猜版本号靠谱得多:少一个 CSS 类、多一段模板,都是硬证据。
3. 正文:代码块、图片都有自己的”写法指纹”
正文 HTML 里有不少线索:
- 代码块是
<figure class="highlight bash">→ 说明原文是bash ``` 围栏;highlight plaintext对应”没写语言的围栏”,而<pre><code>对应四个空格缩进的代码块——两者渲染结果不一样 - 图片被主题的 lazyload 过滤器改写过,会出现”两个 alt 属性”:
1 | <!-- 原文是  --> |
按这个规律就能分别还原成 Markdown 图片和原生 HTML 图片,连 alt 文本都不会丢。
(有意思的是,重复的 alt 属性在真正的 HTML 解析器里会被丢掉,所以这个判断得在原始文本上做,不能先解析再判断。)
4. 最麻烦的是公式
文章里的数学公式,页面中是 MathJax 渲染后的 SVG,原始 LaTeX 并没有保留。但 SVG 里有两个关键信息:
data-mml-node="mfrac" / "msqrt" / "msubsup" / "munderover" / "mtable"→ 完整的 MathML 结构- 每个字形
<path data-c="1D707">→ 字符编码(十六进制码点)
于是写个脚本按结构递归重建 LaTeX:mfrac → \frac{}{}、msqrt → \sqrt{}、msubsup → _{}^{}、mtable → \begin{matrix}…… 最后再把字形码点还原成字符。
踩过的几个坑,记录一下:
- MathJax 的
msubsup子节点顺序是[底, 上标, 下标],不是直觉上的”先下标后上标” - 重建出来的
\sum_{n}^{j=0}如果写成\sum _{n}(宏后面多一个空格),Markdown 会把_当成强调符号吃掉,公式直接渲染失败 - 矩阵的行分隔符
\\会被 Markdown 转义成\,得写成\\\\才能活着走到 MathJax - 大调色:
\log这种”正体”运算符和斜体的log在 SVG 里码点不同,能区分出来
5. 最后用”逐文件对比”验收
重建完不是靠眼睛看,而是把新构建的 public/ 和线上仓库逐文件 diff:
1 | npx hexo clean && npx hexo generate |
结果:96 个文件对 96 个文件,一个不多一个不少;其中 67 个逐字节相同或只有空白差异,剩下的差异都能解释(比如页脚年份取的是构建当年、几处字数统计、两个矩阵公式的行间距)。
现在的工程
1 | blog-source/ |
日常还是老几样:
1 | npx hexo server # 本地预览 |
教训
- 源码要单独放一个仓库(或者至少本地 + 云端各留一份)。
hexo deploy推上去的永远是public/的产物,产物里没有 Markdown、没有配置、没有主题改动 - 顺手搞清楚了:
hexo deploy底层是git push --force,会覆盖远端历史。想保留历史就 clone 下来、覆盖工作区、普通 push - 静态站点是”可反推”的:只要站点还在线,文章内容、图片、代码块、甚至公式都有办法救回来。但能救回来 ≠ 不用备份,反推一次的成本比备份高太多了
这次算是把博客从”尸体”里捞了回来,也顺手把还原过程写成工具留着了。
- 本文标题:博客源码丢了,我从线上站点把它反推了回来
- 创建时间:2026-09-20 23:17:00
- 本文链接:2026/09/20/hexo-source-recovery/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!