用 Shell 脚本结合 Git 检测子目录里的文件是否有更改
注意:git diff --quiet HEAD master -- ./source 不知道为什么失效了,因此谷月姐采用了 Plan B 并修改了这篇文章。
S. 场景
谷月姐的博客(以下简称本博)是用 hexo-abbrlink 插件来生成每篇文章的文件名。这个插件会在执行 hexo g 渲染静态页面时,在每个 MarkDown 源文件的 front-matter 区域生成一个唯一不变的编号,并把这个编号作为对应的 HTML 网页文件的主文件名。
本博是用 GitHub Actions 自动部署的,这样就意味着,在 GitHub Actions 的虚拟机中执行 hexo g 以后,还要执行一次 git push,将修改过的 MarkDown 源文件推送回 GitHub 仓库。
C. 冲突
在实践中,谷月姐发现两个问题:
- 
如果没有任何文件发生更改,执行
git commit -m "something" -a的返回值是1,这会导致 GitHub Actions 判定这个 step 执行状态为 fail,进而终止执行[1]。 - 
由于未知的原因,在 GitHub Actions 的虚拟机中每次执行过
hexo g命令后,node_modules目录中总有一些与 markdown-it 和 katex 有关的文件被更改,这种情况在本地渲染从来没有发生过,而且,这样的更改是万万不能推送回 GitHub 仓库的。

 
Q. 问题
谷月姐发现,hexo-abbrlink 插件只会修改 source/_posts 目录中的 MarkDown 源文件。
那么,有没有办法只检测这个子目录中文件的更改,并且只推送这个更改呢?
参考文献[1]给出的办法,仅能检测仓库中全部文件的更改,无法把检测范围局限在某个子目录。
经过摸索,谷月姐找到了只检测某个子目录中的文件更改的方法。
A. 答案
Git 命令 git diff --quiet HEAD master -- ./source 可以检测当前仓库的 master 分支中 source 子目录中已暂存的文件内容的更改[2]。如果文件内容没有改动,上述命令的返回值(exit code)是 0;如果文件内容有改动,返回值是 1。
Git 命令 git status --porcelain ./source 可以检测当前工作区的 source 子目录中的更改,包括新增文件、删除文件、修改文件内容。如果无更改,上述命令的结果为空;如果有更改,上述命令会列出发生更改的文件名[3]。
为什么是检测 source 子目录而不是 source/_posts 子目录呢?一是为了省事儿,二是为了防备日后有其他的插件修改 source 子目录中其他的文件。
如果检测到 source 子目录中的文件内容没有改动,就略过;如果检测有改动,就提交并推送回 GitHub 仓库。具体代码如下:
1  |  | 
把以上代码写入到 GitHub Actions 自动部署脚本 deploy.yml 中即可。完整的脚本见这里。
N. 注意事项
- 在工作区里,无论是新建文件、删除文件,还是修改已有文件的内容,Git 命令 
git status --porcelain ./source都能检测到。 - Git 命令 
git status --porcelain ./source默认是递归的,可以检测source子目录中所有的文件,及其每一级子目录中的文件。 
T. Two More Things …
- 
在推送到 GitHub 之前,先在本地运行一遍
hexo s,也会在每个 MarkDown 源文件的 front-matter 区域生成一个唯一不变的编号,并把这个编号作为对应的 HTML 网页文件的主文件名。这样就省去了在 GitHub Actions 虚拟机中将修改过的 MarkDown 源文件推送回 GitHub 仓库的麻烦。 - 
新版 Hexo (应该是从 6.0 开始),Hexo 本身也可以生成唯一的编号,用它充当每篇文章对应的 HTML 网页文件的主文件名[4]。它是在执行
hexo g渲染时自动生成,不会修改 MarkDown 源文件。这样彻底省去了在 GitHub Actions 虚拟机中将修改过的 MarkDown 源文件推送回 GitHub 仓库的麻烦。但是我无法放弃 hexo-abbrlink 改用这个方案,因为这个方案会更改本博所有博文的 URL,影响搜索引擎的索引,也影响读者通过搜索引擎访问本博。 
R. 参考文献
图片版权
头图:Banner Image by milkusmaximus from Pixabay

	