GitHub+Hexo(9)-双入口博客的本地安全同步

上一篇把 Hexo 的发布方式改成了:源码统一放在私有的 blog-source,GitHub Actions 负责生成网页,再自动更新 guohuahu.github.io。这样以后,电脑、手机或者 ChatGPT 都可以作为博客的编辑入口。

新的问题也随之出现了。以前博客源码只在自己的电脑上,本地就是唯一版本;现在 ChatGPT 也可能直接修改 GitHub 上的源码,手机端以后也可能这么做。那么下一次回到电脑继续写的时候,本地仓库就不一定是最新的。正常的 Git 使用习惯当然是先 pull 再 push,但对于博客源码,我觉得还应该多考虑一步:假如远程仓库本身出了问题,直接 pull 回来,会不会把本地正常的文章一起弄坏?

为什么不能直接pull

最常见的做法大概是:

1
2
3
4
5
git pull
# 修改文章
git add .
git commit
git push

如果远程只是正常多了一篇文章,这样没有问题。但现在远程仓库已经不只由本地电脑修改。比如我可以直接让 ChatGPT 在 GitHub 上新增文章、改配置,GitHub Actions 也会根据这些源码继续发布。下一次打开本地仓库的时候,本地和远程出现差异已经是正常情况,而不是异常情况。

问题在于,pull 会直接影响当前工作区。假如远程因为误操作删掉了大量文章,或者某次提交把整个 source/ 弄坏了,本地一上来就 pull,相当于把最后一份正常副本也跟着改掉。Git 虽然有历史可以恢复,但真到了出问题的时候,再去翻 reflog、找 commit,还是比较麻烦。

所以我给本地增加了一层比较保守的同步流程:

1
2
3
4
5
6
7
8
9
10
11
12
本地准备 push
↓
先备份 source/
↓
git fetch,只看远程,不改本地
↓
检查远程 source/ 改了什么
↓
正常 → 合并远程变化
异常 → 停止
↓
普通 git push

先给source留一份副本

每次同步以前,先把整个 source/ 复制到 Git 仓库之外,而且包括还没有加入 Git 的文件。备份目录按时间保存,例如:

1
../.hexo-source-backups/Blog/20261002-032500/source/

这样做有点笨,但是很有用。Markdown 本身并不大,几十篇文章和一些页面占不了多少空间,完全没有必要为了省这一点空间承担风险。即使后面的 Git 操作真的出了问题,至少文章原文件还在。

这里特意把备份放到仓库外面,也是为了避免备份文件自己又被 Git 跟踪,最后跟着源码一起上传。

fetch以后先看看远程发生了什么

第二步也不是 git pull,而是先:

1
git fetch --prune

fetch 只更新远程分支信息,不会改当前工作区。这样就可以先比较本地和远程共同祖先到远程最新版本之间,source/ 到底发生了哪些变化。

脚本会统计:

1
2
3
新增了多少文件
修改了多少文件
删除了多少文件

如果只是 ChatGPT 在远程增加了一篇文章,或者改了少量 Markdown,就继续。如果发现远程的 source/ 整个消失,或者删除数量明显异常,就直接停止,不往本地合并。

我现在设的判断比较保守:远程一次删除至少 10 个文件,并且超过原有 source/ 文件数的 30%,就认为值得人工检查。这个数字当然不是通用标准,只是给自己的博客加一道保险。真正重要的不是 10 或 30%,而是 pull 以前先判断“远程这次改动像不像正常编辑”。

本地和远程都有修改怎么办

检查通过以后,再根据两边的状态决定怎么同步。如果本地没有新提交,只是落后于远程,就使用 fast-forward;如果本地和远程都各自有新提交,就 rebase 到远程最新版本。假如 rebase 出现冲突,脚本会自动 abort,恢复到操作以前的状态,再让人自己处理。

整个过程不使用:

1
2
3
git reset --hard
git clean
git push --force

最后仍然只是普通的 git push。因为对于博客源码来说,我更愿意让同步失败,也不愿意为了“自动成功”去强行覆盖某一边。

另外,如果本地已经有修改但还没有 commit,也不会继续自动同步。先把自己正在写的东西处理清楚,再碰远程版本,比较容易知道冲突到底来自哪里。

和自动发布接起来

这样以后,博客大概有两条入口:

1
2
3
4
5
6
7
8
9
10
11
电脑本地
↓
source 备份
↓
fetch + 检查 + 同步
↓
blog-source
↓
GitHub Actions
↓
guohuahu.github.io

另一条则是:

1
2
3
4
5
6
7
手机 / ChatGPT
↓
blog-source
↓
GitHub Actions
↓
guohuahu.github.io

云端修改以后,本地下一次提交会先看到这些变化;本地修改以后,推送到源码仓库又会自动触发网站构建。这样两个入口可以同时存在,但都围绕同一个 blog-source 工作。

以前本地电脑既保存源码,又负责生成和部署,所以很少考虑“远程版本会不会比本地新”。现在发布已经搬到云端,ChatGPT 也可以直接修改源码,这个问题反而必须认真处理。自动化越多,越不能简单地把所有命令连起来就算完成,最好给重要的数据留一个可以停下来的地方。

对我来说,博客最重要的还是 source/ 里面这些 Markdown。网页坏了可以重新生成,Actions 配错了可以再改,甚至整个 guohuahu.github.io 都可以重新部署,但是文章本身没必要跟着这些自动化一起冒险。先备份,再检查,再同步,虽然多了几步,反而更适合现在这种本地和云端都可能修改源码的方式。