近期在倒腾老数据的时候想到一些以前写过的工具,但不知为何在本地并没有找到相关资料的备份。寻思着可能是因为数据搬迁时出现了些许佚失,加之从 GitLab (当时使用的是 17.0.1 版本)部分迁移到 Forgejo 时挪动的资料并不完整,便萌生了从备份资料中翻找相关仓库的念头。
从磁带中翻出旧资料的过程一切顺利,解开压缩包提取文件的过程也毫无阻碍。因为备份文件相对较大,加上我只需要提取其中的部分内容,就没想着重新拉起一整套环境执行全量恢复后再取得,直接在目录里翻翻就好。但当我打开字面意义上存放仓库使用的 repositories 的目录时却傻了眼:这些乱七八糟的东西都是些什么?

从 hashed 这个路径中,不难猜出这些应该是使用了一定的数据摘要算法对仓库进行 hash 处理后的结果。搜索关键词 gitlab hash path ,我找到了一个 Stack Overflow 上的回答:这些 hash 是对仓库的整数 id 执行的 SHA256 数据摘要,可以根据整数 id 计算出对应的 hash 值。因此,可以将定位仓库所在的 hash 目录的操作,转换为获取仓库对应的 id 值。
至于去哪里寻找对应的 id 值呢,答案也非常简单: GitLab 将这些数据都存储在数据库里,所以从备份文件中的 db 目录下的 database.sql.gz 压缩包中提取原始的 database.sql 文件,就一定可以在其中找到相对应的内容了。
抱着这个想法,我想当然地提取出 sql 文件,想着把它送到一个 postgres 数据库里去分析。虽然看着压缩包大小隐隐有些不安desu,但当把数据完全提取出来的时候我还是不禁倒吸了一口凉气——这恐怖的文件体积,大概是已经无法在合理时间内加载到数据库中去了。

幸好这个是 sql 文件,意味着它是纯文本编码的,也意味着只要数据存在,那么就可以通过简单的文本搜索寻找到相关的内容。顺着肌肉记忆往 VS Code 里一扔,想都没想按下「仍然打开」,当感觉不妙的时候已经来不及了——噔噔咚,第一次见到 VS Code 出现 OOM 错误崩溃。

既然如此,就还是请好兄弟(HxD)帮忙吧。打开文件,开始搜索,输入仓库原始的名称。因为这个仓库在创建之后实际上执行过一次改名,所以和原始名称相关的内容并不多,很轻松就能定位到仓库相关的日志:

更好的消息是,它的旁边就有一个看起来像是 ID 一样的数字。抱着死马当活马医的心态试一试:
1 | echo -n 82 | sha256sum |
我得到的是(去掉了末尾的 - )
1 | a46e37632fa6ca51a13fe39a567b3c23b28c2f47d8af6be9bd63e030e214ba38 |
也就是说,如果这个 a46e 东西存在的话,它就有可能是我想要的仓库。
打开备份文件中的 repositories/default/@hashed/ 目录,根据第一第二位打开一级目录,再根据第三第四位分组打开二级目录,很不错,我们看到了两个正好匹配这一串东西的目录。根据后缀不难猜出:
.git 就是打包的仓库 .wiki.git 是把 Wiki 打包的仓库,如果不需要提取其中的数据,可以不管它。
打开对应的 .git 目录,目录里有一个以备份事件标识符命名的目录,里面是一堆 001.bundle 类似的东西。虽然从来没见过这是啥,但大概率和我想要的数据相关。把它们提取到工作区备用。

经过简单的搜索,我又一次来到了 Stack Overflow 。可以得知这个大概率是 git bundle 相关的内容,可以直接使用 git 来处理。
但直接在工作区执行对应的命令会提示 error: need a repository to verify a bundle ,提示我们需要在一个 git 仓库里才能执行校验操作。在工作区其他位置创建一个子目录作为 git 工作区,用 git init . 初始化,我们就可以验证打包的备份文件了:
1 | $ git bundle verify ../1717516823_2024_06_04_17.0.1/001.bundle |
看起来一切顺利,接下来就可以从打包的备份文件中使用 git clone 提取仓库了:
1 | $ git clone ../1717516823_2024_06_04_17.0.1/001.bundle |
功夫不负有心人,这个提取的仓库就是具有完整文件和历史记录的仓库,也就是我们熟悉的 git 仓库了。简单修改一下远端配置,把它推送到新的托管服务器吧。