就在刚刚,我一个误操作,在没有本地备份的前提下,force push 了一个 GitHub 上的仓库。万幸最后恢复成功,数据拿回来了。惊魂未定之余,在此记录我的抢救过程以供参考。

前景提要

在闲逛 GitHub 的时候,发现了一个叫 snk的项目,可以在我的 profile readme 里面放个贪吃蛇,遂照着它的 example with cron job抄了一个 workflow 过来。但是这里我自作聪明地想着把东西全放在 master 上,就把 38 行的 target_branch 改成了 master。结果一运行吓一跳,我的 README.md 没了,仓库只剩下 snk 生成的 svg 文件。这不行啊,我花了好久的时间才整出来的东西,不能说没就没啊!于是赶紧开始网上冲浪,看怎么抢救被 force push 的 repo。

恢复过程

首先我要大力感谢这个 Gist,我就是参考这里的做法成功恢复了这个 repo 的。

找到上一次 commit 的记录

首先,要通过 https://api.github.com/repos/:owner/:repo/events 这个 API 找到上次提交的 sha。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
-> % curl -u boris1993 https://api.github.com/repos/boris1993/boris1993/events
Enter host password for user 'boris1993':
[
{
"id": "24633558565",
"type": "PushEvent",
"actor": {
"id": 4367313,
"login": "boris1993",
"display_login": "boris1993",
"gravatar_id": "",
"url": "https://api.github.com/users/boris1993",
"avatar_url": "https://avatars.githubusercontent.com/u/4367313?"
},
"repo": {
"id": 297097347,
"name": "boris1993/boris1993",
"url": "https://api.github.com/repos/boris1993/boris1993"
},
"payload": {
"push_id": 11349267173,
"size": 1,
"distinct_size": 1,
"ref": "refs/heads/master",
"head": "98364ce80ec5bbcdb6dc6f8d2239de2256ede487",
"before": "32276fc643c6b34fee48f46363cfb6a44327cbe4",
"commits": [
{
"sha": "98364ce80ec5bbcdb6dc6f8d2239de2256ede487",
"author": {
"email": "41898282+github-actions[bot]@users.noreply.github.com",
"name": "github-actions[bot]"
},
"message": "Deploy to GitHub pages",
"distinct": true,
"url": "https://api.github.com/repos/boris1993/boris1993/commits/98364ce80ec5bbcdb6dc6f8d2239de2256ede487"
}
]
},
"public": true,
"created_at": "2022-10-17T05:44:11Z"
},
{
"id": "24633554175",
"type": "PushEvent",
"actor": {
"id": 4367313,
"login": "boris1993",
"display_login": "boris1993",
"gravatar_id": "",
"url": "https://api.github.com/users/boris1993",
"avatar_url": "https://avatars.githubusercontent.com/u/4367313?"
},
"repo": {
"id": 297097347,
"name": "boris1993/boris1993",
"url": "https://api.github.com/repos/boris1993/boris1993"
},
"payload": {
"push_id": 11349264873,
"size": 1,
"distinct_size": 1,
"ref": "refs/heads/master",
"head": "32276fc643c6b34fee48f46363cfb6a44327cbe4",
"before": "b0ab0263c0693122ae8069c95526e13b7336483f",
"commits": [
{
"sha": "32276fc643c6b34fee48f46363cfb6a44327cbe4",
"author": {
"email": "boris1993@live.cn",
"name": "Boris Zhao"
},
"message": "Create generate_snake_animation.yml",
"distinct": true,
"url": "https://api.github.com/repos/boris1993/boris1993/commits/32276fc643c6b34fee48f46363cfb6a44327cbe4"
}
]
},
"public": true,
"created_at": "2022-10-17T05:43:50Z"
},
{
"id": "23024411036",
"type": "PushEvent",
"actor": {
"id": 4367313,
"login": "boris1993",
"display_login": "boris1993",
"gravatar_id": "",
"url": "https://api.github.com/users/boris1993",
"avatar_url": "https://avatars.githubusercontent.com/u/4367313?"
},
"repo": {
"id": 297097347,
"name": "boris1993/boris1993",
"url": "https://api.github.com/repos/boris1993/boris1993"
},
"payload": {
"push_id": 10514884570,
"size": 1,
"distinct_size": 1,
"ref": "refs/heads/master",
"head": "b0ab0263c0693122ae8069c95526e13b7336483f",
"before": "660e4d3896eb523d703464f89112a2eea07ee309",
"commits": [
{
"sha": "b0ab0263c0693122ae8069c95526e13b7336483f",
"author": {
"email": "boris1993@live.cn",
"name": "Boris Zhao"
},
"message": "Update README.md",
"distinct": true,
"url": "https://api.github.com/repos/boris1993/boris1993/commits/b0ab0263c0693122ae8069c95526e13b7336483f"
}
]
},
"public": true,
"created_at": "2022-07-22T08:05:06Z"
}
]

从接口的返回可以看到,截至现在一共有三次 push,仓库被覆盖就是发生在最上面的一次 push 中,而接下来的一个就是我添加 workflow 的那一次 push。

从上一次的 push 记录中找回数据

找到 push 记录,那么就好办了。点击添加 workflow 那次 push 中的 url,在浏览器中会打开一个新页面,返回的 JSON 中是这次 push 的详细信息。这里我们要找的是 html_url 这个字段。点开这个字段里面的链接,会进入 GitHub 里面,看到这次 commit 的 diff。然后点击 Browse files 按钮,就能看到当时的文件了。这时候还等什么?赶紧下载啊!文件少的话复制内容回来就行,文件多的话,点开 Code 按钮,Download ZIP 就好啦。

后记

一定要做备份啊!