1
1
=== 维护与数据恢复
2
2
3
- 偶尔,你需要做一些清理 - 使一个仓库体积更小、清理一个导入的仓库、或者恢复丢失的工作。
3
+ 偶尔,你必须要做一些清理 - 使一个仓库体积更小、清理一个导入的仓库、或者恢复丢失的工作。
4
4
这个小节将会介绍这些情况中的一些。
5
5
6
6
[[_git_gc]]
11
11
然而,如果有太多松散对象(不在打包文件中的对象)或者太多打包文件,Git 运行一个完全的 `git gc` 命令。
12
12
``gc'' 代表垃圾回收,命令做几件事情:它收集所有松散对象并将它们放置到打包文件中,它将多个打包文件合并为一个大的打包文件,然后移除无法从任何提交追踪到的几个月的对象。
13
13
14
- 你可以像下面一样手动执行自动垃圾回收 :
14
+ 可以像下面一样手动执行自动垃圾回收 :
15
15
16
16
[source,console]
17
17
----
18
18
$ git gc --auto
19
19
----
20
20
21
21
再一次,这通常什么事都不做。
22
- 你必须有大约 7000 个松散对象或超过 50 个打包文件 Git 才会启动一次真正的 gc 命令。
23
- 你可以通过 `gc.auto` 与 `gc.autopacklimit` 配置选项分别修改这些限制。
22
+ 必须有大约 7000 个松散对象或超过 50 个打包文件 Git 才会启动一次真正的 gc 命令。
23
+ 可以通过 `gc.auto` 与 `gc.autopacklimit` 配置选项分别修改这些限制。
24
24
25
25
`gc` 将会做的另一件事是打包你的引用到一个单独的文件。
26
26
假设你的仓库包含以下分支与标签:
@@ -34,7 +34,7 @@ $ find .git/refs -type f
34
34
.git/refs/tags/v1.1
35
35
----
36
36
37
- 如果你运行 `git gc`,在 `refs` 目录中你不会再有这些文件 。
37
+ 如果运行 `git gc`,在 `refs` 目录中不会再有这些文件 。
38
38
Git 会因为缺乏效率而移动进入它们到一个名为 `.git/packed-refs` 的文件,看起来像这样:
39
39
40
40
[source,console]
@@ -48,19 +48,19 @@ cac0cab538b970a37ea1e769cbbde608743bc96d refs/tags/v1.0
48
48
^1a410efbd13591db07496601ebc7a059dd55cfe9
49
49
----
50
50
51
- 如果你更新一个引用 ,Git 不会编辑这个文件但是会写一个新文件到 `refs/heads`。
51
+ 如果更新一个引用 ,Git 不会编辑这个文件但是会写一个新文件到 `refs/heads`。
52
52
为了获得给一个给定引用的正确的 SHA,Git 首先在 `refs` 目录中检查那个引用然后作为后备再到 `packed-refs` 文件中检查。
53
- 然而,如果你不能在 `refs` 目录中找到一个引用,它有可能在你的 `packed-refs` 文件中。
53
+ 然而,如果不能在 `refs` 目录中找到一个引用,它有可能在你的 `packed-refs` 文件中。
54
54
55
55
注意文件的最后一行,以 `^` 开头的行。
56
56
这意味着上面的是一个注释标签,那行是注释标签指向的那个提交。
57
57
58
58
[[_data_recovery]]
59
59
==== 数据恢复
60
60
61
- 在你 Git 旅程的某个时候,你可能会意外丢失一次提交。
62
- 通常,这是因为你强制删除了还有工作的分支,原来最终你还想要那个分支;或者你硬重置了一个分支 ,这样放弃了你想要的提交。
63
- 假设这已经发生,你如何才得得回你的提交呢 ?
61
+ 在你的 Git 旅程的某个时候,你可能会意外丢失一次提交。
62
+ 通常,这是因为强制删除了还有工作的分支,原来最终还想要那个分支;或者硬重置了一个分支 ,这样放弃了你想要的提交。
63
+ 假设这已经发生,如何才能得回你的提交呢 ?
64
64
65
65
这是一个例子关于在你的 test 仓库中硬重置了 master 分支到一个更老的提交然后恢复丢失的提交。
66
66
首先,让我们看看你的仓库现在在什么地方:
@@ -87,15 +87,15 @@ cac0cab538b970a37ea1e769cbbde608743bc96d second commit
87
87
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
88
88
----
89
89
90
- 你事实上已经丢失了顶部的两次提交 - 你没有那些提交可到达的分支 。
91
- 你需要找出最后一次提交的 SHA 然后增加一个指向它的分支。
90
+ 事实上已经丢失了顶部的两次提交 - 没有那些提交可到达的分支 。
91
+ 需要找出最后一次提交的 SHA 然后增加一个指向它的分支。
92
92
技巧是找到最后一次提交的 SHA - 看起来不像是你还记得它,是吗?
93
93
94
94
经常,最快捷的方式是使用一个称作 `git reflog` 的工具。
95
95
在你正在工作时,Git 默默地记录每一次你改变 HEAD 时的值。
96
96
每一次你提交或改变分支,引用日志都会被更新。
97
- 引用日志(reflog)也可以通过 `git update-ref` 命令更新,这是另一个原因使用它代替仅仅写入 SHA 值到你的 ref 文件中 ,如 <<_git_refs>> 我们介绍的。
98
- 你可以在任何时间通过运行 git reflog` 来查看你到过哪里:
97
+ 引用日志(reflog)也可以通过 `git update-ref` 命令更新,这是使用它代替仅仅写入 SHA 值到你的 ref 文件中的另一个原因 ,如 <<_git_refs>> 我们介绍的。
98
+ 可以在任何时间通过运行 git reflog` 来查看你到过哪里:
99
99
100
100
[source,console]
101
101
----
@@ -128,8 +128,8 @@ Date: Fri May 22 18:15:24 2009 -0700
128
128
modified repo.rb a bit
129
129
----
130
130
131
- 看起来底部的提交是你丢失的那个,所以你可以通过在那次提交上创建一个新分支来恢复它 。
132
- 例如,你可以在那次提交 (ab1afef)上开始一个名为 `recover-branch` 的新分支:
131
+ 看起来底部的提交是你丢失的那个,所以可以通过在那次提交上创建一个新分支来恢复它 。
132
+ 例如,可以在那次提交 (ab1afef)上开始一个名为 `recover-branch` 的新分支:
133
133
134
134
[source,console]
135
135
----
@@ -142,8 +142,8 @@ cac0cab538b970a37ea1e769cbbde608743bc96d second commit
142
142
fdf4fc3344e67ab068f836878b6c4951e3b15f3d first commit
143
143
----
144
144
145
- 酷,现在你有一个名为 `recover-branch` 的分支是你的 `master` 分支曾经指向的地方,再一次使得前两次提交可到达了。
146
- 接下来,假设你的损失因为某些原因不再引用日志中 - 你可以通过移除 `recover-branch` 并删除引用日志来模拟。
145
+ 酷,现在有一个名为 `recover-branch` 的分支是你的 `master` 分支曾经指向的地方,再一次使得前两次提交可到达了。
146
+ 接下来,假设你的损失因为某些原因不再引用日志中 - 可以通过移除 `recover-branch` 并删除引用日志来模拟。
147
147
现在前两次提交不再可被任何东西到达了:
148
148
149
149
[source,console]
@@ -152,10 +152,10 @@ $ git branch -D recover-branch
152
152
$ rm -Rf .git/logs/
153
153
----
154
154
155
- 因为引用日志数据存放在 `.git/logs/` 目录中,你事实上已经没有引用日志了 。
156
- 这时你该如何恢复那次提交 ?
157
- 一种方式是使用 `git fsck` 实用工具,将会检查你的数据库的完整性 。
158
- 如果你使用一个 `--full` 选项运行它,它会向你显示出所有没有被其他对象指向的对象:
155
+ 因为引用日志数据存放在 `.git/logs/` 目录中,事实上已经没有引用日志了 。
156
+ 这时该如何恢复那次提交 ?
157
+ 一种方式是使用 `git fsck` 实用工具,将会检查数据库的完整性 。
158
+ 如果使用一个 `--full` 选项运行它,它会向你显示出所有没有被其他对象指向的对象:
159
159
160
160
[source,console]
161
161
----
@@ -168,8 +168,8 @@ dangling tree aea790b9a58f6cf6f2804eeac9f0abbe9631e4c9
168
168
dangling blob 7108f7ecb345ee9d0084193f147cdad4d2998293
169
169
----
170
170
171
- 在本例中,你可以在字符串 ``dangling commit'' 看到你丢失的提交。
172
- 你可以使用同样的方式恢复它 ,通过添加一个指向那个 SHA 的分支。
171
+ 在本例中,可以在字符串 ``dangling commit'' 看到你丢失的提交。
172
+ 可以使用同样的方式恢复它 ,通过添加一个指向那个 SHA 的分支。
173
173
174
174
[[_removing_objects]]
175
175
==== 移除对象
@@ -179,15 +179,15 @@ dangling blob 7108f7ecb345ee9d0084193f147cdad4d2998293
179
179
希而,如果某个人在项目历史的任意时间点添加了一个单独的巨大的文件,永久性地每次克隆都被强制下载那个大文件,即使那个文件在接下来的提交中从项目中移除。
180
180
因为它是可以从历史到达的,它会永远在那里。
181
181
182
- 当你转换 Subversion 或 Perforce 仓库到 Git 时这会是一个巨大的问题。
183
- 因为在那些系统中你并不下载整个历史 ,这种类型的添加带来的问题较少。
184
- 如果你从另一个系统中导入或因为其他原因发现你的仓库比预期的要大很多,这里就是你如何找到并移除巨大对象的地方 。
182
+ 当转换 Subversion 或 Perforce 仓库到 Git 时这会是一个巨大的问题。
183
+ 因为在那些系统中并不下载整个历史 ,这种类型的添加带来的问题较少。
184
+ 如果从另一个系统中导入或因为其他原因发现仓库比预期的要大很多,这里就是如何找到并移除巨大对象的地方 。
185
185
186
- *警告:这个技巧对你的提交历史是具有毁灭性的 。*
187
- 它会从你必须修改或移除一个大文件引用最早的 tree 对象开始重写每一次提交。
188
- 如果你在导入后马上做这个,在任何人开始基于这些提交工作前,那么很好 - 否则,你必须通知所有贡献者他们必须变基他们的工作到你新的提交上 。
186
+ *警告:这个技巧对提交历史是具有毁灭性的 。*
187
+ 它会从必须修改或移除一个大文件引用最早的 tree 对象开始重写每一次提交。
188
+ 如果你在导入后马上做这个,在任何人开始基于这些提交工作前,那么很好 - 否则,你必须通知所有贡献者他们必须变基他们的工作到新的提交上 。
189
189
190
- 为了演示,你将会加添一个大文件到你的 test 仓库,在下一次提交中移除它,找到它,然后从仓库中永久性地移除它。
190
+ 为了演示,将会加添一个大文件到你的 test 仓库,在下一次提交中移除它,找到它,然后从仓库中永久性地移除它。
191
191
首先,添加一个大文件到你的历史中:
192
192
193
193
[source,console]
@@ -213,7 +213,7 @@ $ git commit -m 'oops - removed large tarball'
213
213
delete mode 100644 git.tgz
214
214
----
215
215
216
- 现在,`gc` 你的数据库然后看看你使用了多少空间 :
216
+ 现在,`gc` 数据库然后看看使用了多少空间 :
217
217
218
218
[source,console]
219
219
----
@@ -225,7 +225,7 @@ Writing objects: 100% (17/17), done.
225
225
Total 17 (delta 1), reused 10 (delta 0)
226
226
----
227
227
228
- 你可以运行 `count-objects` 命令来快速查看你使用了多少空间 :
228
+ 可以运行 `count-objects` 命令来快速查看使用了多少空间 :
229
229
230
230
[source,console]
231
231
----
@@ -240,16 +240,16 @@ garbage: 0
240
240
size-garbage: 0
241
241
----
242
242
243
- `size-pack` 记录是你的打包文件的千字节大小,所以你使用了大概 5MB。
244
- 在最后一次提交前,你使用了不到 2K - 显然,从之前的提交中移除文件并没有从历史中移除它。
243
+ `size-pack` 记录是你的打包文件的千字节大小,所以使用了大概 5MB。
244
+ 在最后一次提交前,使用了不到 2K - 显然,从之前的提交中移除文件并没有从历史中移除它。
245
245
每一次有人克隆这个仓库时,他们将必须克隆所有的 5MB 来获得这个微型项目,因为你意外地添加了一个大文件。
246
246
让我们移除它。
247
247
248
248
首先你必须找到它。
249
249
在本例中,你已经知道是哪个文件了。
250
- 但是假设你不知道;你该如何找出哪个文件或哪些文件占用了如何多的空间 ?
251
- 如果你运行 `git gc`,所有的对象都在一个打包文件中;你可以通过运行另一个称作 `git verify-pack` 的底层命令然后对输出的第三列(即文件大小)进行排序,从而找出体积大的对象。
252
- 你也可以通过管道传送给 `tail` 命令,因为你最关心最后的几个最大的文件:
250
+ 但是假设你不知道;该如何找出哪个文件或哪些文件占用了如何多的空间 ?
251
+ 如果运行 `git gc`,所有的对象都在一个打包文件中;可以通过运行另一个称作 `git verify-pack` 的底层命令然后对输出的第三列(即文件大小)进行排序,从而找出体积大的对象。
252
+ 也可以通过管道传送给 `tail` 命令,因为你最关心最后的几个最大的文件:
253
253
254
254
[source,console]
255
255
----
@@ -262,18 +262,18 @@ dadf7258d699da2c8d89b09ef6670edb7d5f91b4 commit 229 159 12
262
262
----
263
263
264
264
大对象在底部:5MB。
265
- 为了找出是哪个文件,你可以使用 `rev-list` 命令,你在 <<_enforcing_commit_message_format>> 中短暂使用过。
266
- 如果你传递 `--objects` 给 `rev-list`,它会列出所有的提交 SHA 、 blob SHA 与关联到它们的文件路径。
267
- 你可以使用这个来找出你的 blob 的名字:
265
+ 为了找出是哪个文件,可以使用 `rev-list` 命令,你在 <<_enforcing_commit_message_format>> 中短暂使用过。
266
+ 如果传递 `--objects` 给 `rev-list`,它会列出所有的提交 SHA 、 blob SHA 与关联到它们的文件路径。
267
+ 可以使用这个来找出你的 blob 的名字:
268
268
269
269
[source,console]
270
270
----
271
271
$ git rev-list --objects --all | grep 82c99a3
272
272
82c99a3e86bb1267b236a4b6eff7868d97489af1 git.tgz
273
273
----
274
274
275
- 现在,你需要从你的过去所有的 tree 中移除这个文件。
276
- 你可以轻松地查看哪些提交修改了这个文件 :
275
+ 现在,需要从你的过去所有的 tree 中移除这个文件。
276
+ 可以轻松地查看哪些提交修改了这个文件 :
277
277
278
278
[source,console]
279
279
----
@@ -282,8 +282,8 @@ dadf725 oops - removed large tarball
282
282
7b30847 add git tarball
283
283
----
284
284
285
- 你必须重写 `7b30847` 下流的所有提交来从你的 Git 历史中完全移除这个文件。
286
- 为了这样做,你使用 `filter-branch`,你在 <<_rewriting_history>> 中用过的:
285
+ 必须重写 `7b30847` 下流的所有提交来从你的 Git 历史中完全移除这个文件。
286
+ 为了这样做,使用 `filter-branch`,你在 <<_rewriting_history>> 中用过的:
287
287
288
288
[source,console]
289
289
----
@@ -294,18 +294,18 @@ Rewrite dadf7258d699da2c8d89b09ef6670edb7d5f91b4 (2/2)
294
294
Ref 'refs/heads/master' was rewritten
295
295
----
296
296
297
- `--index-filter` 选项类似于在 <<_rewriting_history>> 中使用的 `--tree-filter` 选项,除了会传递一个命令会修改检出到磁盘上的文件,你每次都在修改你的暂存区或索引 。
297
+ `--index-filter` 选项类似于在 <<_rewriting_history>> 中使用的 `--tree-filter` 选项,除了会传递一个命令会修改检出到磁盘上的文件,每次都在修改你的暂存区或索引 。
298
298
299
- 你必须使用 `git rm --cached` 来移除一个特定文件,而不是通过类似 `rm file` 的东西来移除它 - 你必须从索引中移除它 ,而不是磁盘中。
299
+ 必须使用 `git rm --cached` 来移除一个特定文件,而不是通过类似 `rm file` 的东西来移除它 - 必须从索引中移除它 ,而不是磁盘中。
300
300
这样做的原因是速度 - 因为 Git 不必在运行你的过滤器前检出每一个版本到磁盘中,这个过程可以非常、非常快。
301
- 如果你愿意的话你也可以通过 `--tree-filter` 来完成同样的任务。
302
- `git rm` 的 `--ignore-unmatch` 选项告诉它如果你想要尝试删除的模式不在那儿的话不要发生错误 。
303
- 最终,你请求 `filter-branch` 来重写自 `6df7640` 以来的历史,因为你知道那是问题开始的地方。
301
+ 如果愿意的话也可以通过 `--tree-filter` 来完成同样的任务。
302
+ `git rm` 的 `--ignore-unmatch` 选项告诉它如果想要尝试删除的模式不在那儿的话不要发生错误 。
303
+ 最终,请求 `filter-branch` 来重写自 `6df7640` 以来的历史,因为你知道那是问题开始的地方。
304
304
否则,它会从头开始然后会不必要的花费更长的时间。
305
305
306
306
你的历史不再包含对那个文件的引用。
307
307
然而,你的引用日志与你在 `.git/refs/original` 下做的 `filter-branch` 添加的一个新引用还引用着,所以你必须移除它们然后重新打包数据库。
308
- 在你重新打包前你需要移除任何包含指向那些旧提交的指针的东西 :
308
+ 在重新打包前需要移除任何包含指向那些旧提交的指针的东西 :
309
309
310
310
[source,console]
311
311
----
@@ -335,8 +335,8 @@ size-garbage: 0
335
335
----
336
336
337
337
打包的仓库大小下降到了 8K,比 5MB 好很多。
338
- 你可以从 size 值看到大对象还在你的松散对象中,它并没有消失;但是它不会通过一个推送或接下来的克隆传送,而这是重要的。
339
- 如果你真的想要删除它,你可以通过有 `--expire` 选项的 `git prune` 命令来完全地移除那个对象:
338
+ 可以从 size 值看到大对象还在你的松散对象中,它并没有消失;但是它不会通过一个推送或接下来的克隆传送,而这是重要的。
339
+ 如果真的想要删除它,可以通过有 `--expire` 选项的 `git prune` 命令来完全地移除那个对象:
340
340
341
341
[source,console]
342
342
----
0 commit comments