从 Git 提交历史中「恢复」文件修改时间

简介: 几个月之前,我贪图部署方便,把博客的部署方式,从本地编译推送更新变成了借助 Github 和 Travis-CI 自动部署。 用了几个月一直相安无事,直到几天前我小小修改了一下主题,展示了文章的更新日期。 本地调试的时候一切正常,直到我把变更推到 Github,触发了自动部署。下图就是出乎意料的

几个月之前,我贪图部署方便,把博客的部署方式,从本地编译推送更新变成了借助 Github 和 Travis-CI 自动部署

用了几个月一直相安无事,直到几天前我小小修改了一下主题,展示了文章的更新日期。

本地调试的时候一切正常,直到我把变更推到 Github,触发了自动部署。下图就是出乎意料的上线效果。

逼死处女座

注意到所有的文章的更新日期都是同一天了吗?真是伤脑筋啊。没错,正如你猜测的那样,我的星座是人见人黑的处女座。

为什么会出现这种测试效果和上线效果不一致的情况呢?我们得从 Linux 系统中文件的几个时间属性说起。

在 POSIX 系统中,每个文件都有且仅有 3 个时间属性,最后访问时间,最后修改时间,最后状态变更时间。举个例子,这个文件的内容被访问了,比如用 cat 或者 less 读取内容,最后访问时间就会被更新;如果这个文件内容被修改了,比如用 vi 改了点东西然后保存,就会修改最后修改时间;如果用 chmod 改了权限什么的,更新的就是最后状态变更时间。

为什么没有「文件创建时间」?其实在一些现代的文件系统中,比如 ext4 或者 Btrfs 是保存了文件创建时间的,只不过由于默认的 POSIX 兼容性,我们一般都不去做特殊读取罢了,毕竟出于程序的可移植性考虑,我们不能把程序绑定在某几个文件系统上。

但是作为一个博客系统,Hexo 是需要知道文件的创建时间的。也许是出于上述原因,Hexo 没有依赖文件系统的特性去保存创建时间,而是直接把时间作为文章的元数据,放在文章开头的 YAML 区域里头了。

与此同时,Hexo 也提供了获取文章修改时间的 API,由于 POSIX 保证了能够问系统要到文件的最后修改时间,Hexo 就直接把这个功能交给系统代理,文件的最后修改时间就认为是文章的修改时间。

在博客的自动部署流程中,我们是把博客源码从 Github 上 clone 到 Travis-CI 的虚拟机里,然后使用 Hexo 编译出静态页面。显然这些 clone 出来的文件,它们的最后修改时间是这些文件在 Travis-CI 的虚拟机里的创建时间,而不是我当初修改并保存的时间。至于为什么 Git 不保存文件的修改时间,原因在这里

那么有没有什么办法恢复文件的修改时间呢?精确的恢复是不可能的,毕竟信息已经丢失了,丢失得很彻底。但是作为一个博客系统,对时间精确度的要求没那么高,近似一下,使用文件的 commit 时间作为修改时间,也是可以接受的。

Google 一下,神通广大的外国朋友已经给出了解决方案,当然我也不是啥都没做,我还是去掉了一些无用参数的!

git ls-files | while read file; do touch -d $(git log -1 --format="@%ct" "$file") "$file"; done

这个操作看起来也好理解,把当前 Git 仓库里正在跟踪的文件给列出来,然后依次「篡改」文件的最后修改时间。根据 git log 命令的文档%ct 是 committer date, UNIX timestamp 的占位符,代表提交时的时间戳,那么问题来了,为什么要在时间戳前面加上 @符号?

经过一番苦苦寻觅,我在 GNU Coreutils 的文档中找到了答案。原来从 Coreutils 5.3.0 开始,实用工具中只要是涉及时间的参数,都可以用 @+unix timestamp 的形式来替代,比如 touch -d 本来要带的参数是一个「人类可读」的时间描述,可以是 Sun, 29 Feb 2004 16:21:42 -0800 或者 2004-02-29 16:21:42,甚至 next Thursday 也行,但是我们还是可以任性地使用 @1078042902 作为时间输入。

于是最后的解决方案就是把上面这行代码添加到 .travis.yml中,在生成静态页面之前恢复一下文件的修改时间。

解救处女座

于是这个逼死处女座的问题总算解决了~

目录
相关文章
|
5月前
|
存储 前端开发 开发工具
Git Hooks实战:提交前检查修改文件中是否包含调试代码
Git Hooks实战:提交前检查修改文件中是否包含调试代码
93 0
|
5月前
|
缓存 Shell 开发工具
git 基本 使用和.gitignore文件不生效
git 基本 使用和.gitignore文件不生效
99 0
|
11天前
|
Shell 开发工具 git
git学习三:git使用:删除仓库,删除仓库内文件
通过GitHub的设置页面删除仓库,以及如何使用Git命令行删除仓库中的文件或文件夹。
42 1
git学习三:git使用:删除仓库,删除仓库内文件
|
11天前
|
开发工具 git 索引
git上面中新建gitignore文件,并且去除已经在仓库版本管理中的文件夹
git上面中新建gitignore文件,并且去除已经在仓库版本管理中的文件夹
28 4
|
17天前
|
开发工具 git
git显示开发日志+WinSW——将.exe文件注册为服务的一个工具+图床PicGo+kubeconfig 多个集群配置 如何切换
git显示开发日志+WinSW——将.exe文件注册为服务的一个工具+图床PicGo+kubeconfig 多个集群配置 如何切换
31 1
|
5月前
|
开发工具 git
如何使git提交的时候忽略一些特殊文件?
如何使git提交的时候忽略一些特殊文件?
|
2月前
|
缓存 开发工具 git
给Git仓库添加.gitignore:清理、删除、排除被Git误添加的临时文件
本文介绍了如何为Git仓库添加`.gitignore`文件来排除不需要跟踪的临时文件,并展示了如何删除已经被提交的临时文件缓存,以清理Git仓库中的不必要文件。
183 0
给Git仓库添加.gitignore:清理、删除、排除被Git误添加的临时文件
|
2月前
|
开发工具 git iOS开发
服务器配置Huggingface并git clone模型和文件
该博客提供了在服务器上配置Huggingface、安装必要的工具(如git-lfs和huggingface_hub库)、登录Huggingface以及使用git clone命令克隆模型和文件的详细步骤。
198 1
|
3月前
|
开发工具 git
git如何不上传指定的文件或文件夹
【7月更文挑战第9天】平时写代码的时候有很多测试文件或者静态文件,没必要上传到git上,不仅占资源更新还麻烦,以下介绍如何忽略不需要上传的文件或文件夹。
343 8
|
3月前
|
存储 安全 开发工具
Git学习笔记 三个区域、文件状态、分支、常用命令
理解并掌握这些概念和命令,对于有效地使用Git来管理项目源代码是至关重要的。Git的强大功能支持了复杂的开发工作流程,而良好的Git实践能够极大地提高团队的协作效率。随着实践的增多,对于Git更深层次的功能和最佳实践的理解也会随之增长,进一步加强你作为一个软件开发者的能力。
66 0

相关实验场景

更多