blog/_posts/2025-10-12-recover.md

29 lines
5.3 KiB
Markdown
Raw Normal View History

---
layout: post
title: 一次找回GitHub上被删除仓库的经历
tags: [GitHub, Git, 代码恢复, 软件存档]
---
在GitHub中寻找踪迹也许是非常简单的事情……<!--more-->
# 起因
前段时间,有人和我聊天的时候提到了[Brainfuck](https://esolangs.org/wiki/Brainfuck)语言,让我回想起了高中时写的[演讲稿](/%E6%BC%94%E8%AE%B2%E7%A8%BF/2018/06/20/Coding.html)。那时候我在演讲时也介绍了Brainfuck语言。对于Brainfuck的解释器[各种语言都可以实现](https://rosettacode.org/wiki/RCBF)不过我当时为了方便理解用了一个在GitHub Pages上的网站用可视化的方式演示了它的运行过程效果很不错。现在既然聊到了自然就想分享一下这个[演示的网站](https://fatiherikli.github.io/brainfuck-visualizer/)但我正想打开时发现网站已经404了😰。
在GitHub Pages上的网站都有对应的仓库现在不仅原仓库消失了连作者的[首页](https://github.com/fatiherikli)都打不开看样子是完全退出GitHub了……那么我想找到这个网站的想法就无法实现了吗不过GitHub有些有意思的特性也许能帮助我找回这个网站。
# GitHub的特性
在GitHub中一个普通的仓库可能没有什么特别的也许就是服务器上的一个文件夹。但是当仓库被其他人Fork的时候就不一样了在执行Fork时显然GitHub不会完整复制整个仓库。否则同一个仓库在服务器上会占用双倍空间这显然不合理。另外想想Git的结构它由提交对象和分支指针构成每次提交都有唯一的Hash值且不会冲突。因此可以推测GitHub在实现Fork时所有被Fork的仓库可能共享同一个对象库而每个用户仓库只保存指针这样所有仓库只会占用增量空间而不会存储重复内容。
但这样也会带来一个问题首先因为很多人可能要共用一部分对象所以也很难确认对象的所有权而且也因为这个原因所有的对象要能被所有人访问。因此在整个Fork网络中只要有一个仓库存在GitHub就必须保留所有的对象而且每个仓库都能访问这个网络中所有的对象。为了验证这一点我们可以用最知名的[Linux内核仓库](https://github.com/torvalds/linux)做个示例。
首先对Linux仓库进行Fork然后我们可以随便做一些改动比如在README中写“Linux已经被我占领了😆”之类的内容提交到自己的仓库并且记下提交的Hash值接下来就可以把自己的仓库删掉了。如果上面的猜想是正确的那么在这个Fork网络中的任何一个仓库查看我刚刚的提交应该都可以于是我直接在主仓库拼上了[提交的Hash值](https://github.com/torvalds/linux/tree/78e1d0446b94012da8639aa2b157d4f2dee481ce)(顺便一说只要值唯一,和其他的提交不冲突,[短的Hash值](https://github.com/torvalds/linux/tree/78e1d044)也可以果不其然能找到刚刚修改的内容这样一来只要GitHub和任意一个Linux仓库的Fork还存在这个提交就永远存在了😝。
# 找回仓库
那么接下来找回之前网站的方案就很简单了我只要找到网站仓库的任意一个Fork然后只要知道最新的提交Hash我就可以还原最新的仓库了。Fork倒是好找随便搜一下[就能找到一个](https://github.com/ashupk/brainfuck-visualizer)。这个Fork的最新提交是2016年但要想找到我当年演讲的版本至少到2018年之后。不过这个Hash值也不太好找虽然理论上爆破短Hash值也可以但是感觉太麻烦了没有那个必要所以我干脆直接去互联网档案馆看看能找到的[最新的仓库页面](https://web.archive.org/web/20201229125043/https://github.com/fatiherikli/brainfuck-visualizer/)吧这样我就能找到它的Hash值了然后我再把Fork仓库的地址和Hash拼到一起就看得到最新代码了。
当然仅仅看到代码还不够。我想Fork这个项目并在自己的GitHub Pages上部署一份。有没有什么好办法可以将我仓库的HEAD指针指向最新的提交呢其实很简单首先我要Fork这个Fork仓库然后Clone我的仓库到本地。不过此时Clone下来的仓库并不包含GitHub上完整的对象库因此直接checkout或reset是不行的。这时Hash值就派上用场了通过fetch拉取对应提交后就可以进行上述操作。具体命令如下
```bash
git fetch origin <commit-hash>
git reset --hard <commit-hash>
git push origin master
```
最终我就获得了包含[最新代码](https://github.com/Mabbs/brainfuck-visualizer)的[Brainfuck可视化演示](https://mabbs.github.io/brainfuck-visualizer/)了🎉。
# 结局
后来我才知道,原来有一个专门的组织[Software Heritage](https://archive.softwareheritage.org)会保存所有代码,根本没必要搞这些花里胡哨的操作😂,像这个仓库也是能很轻易在[上面](https://archive.softwareheritage.org/browse/origin/directory/?origin_url=https://github.com/fatiherikli/brainfuck-visualizer)找到这下以后知道了再遇到类似情况就可以直接去Software Heritage查找而不必在互联网档案馆上找线索瞎折腾了🤣。