Git学习笔记 004 Git内部原理 part5 Git的维护与数据恢复-part文件

Git学习笔记 004 Git内部原理 part5 Git的维护与数据恢复-part文件

维护

之前在包文件的章节中提到过,Git会按照一定规则不定时执行git gc命令。在存在有特别多的松散对象或者包文件过多的时候,它会去将这些对象或者包文件合并为一个包。

然后移除被打包的文件。也就是说refs 目录中将不会再有这些文件。 为了保证效率 Git 会将它们的引用移动到名为.git/packed-refs 的文件中

大约需要 7000 个以上的松散对象或超过 50 个的包文件才能让 Git 启动一次真正的 gc 命令。

确认一下执行gc命令前的refs文件夹

$ find .git/refs -type f

.git/refs/heads/master

.git/refs/heads/testing

.git/refs/original/refs/heads/testing

.git/refs/remotes/origin/HEAD

.git/refs/remotes/origin/master

.git/refs/remotes/origin/master2

.git/refs/remotes/origin/testing

.git/refs/remotes/origin/testing2

执行gc命令

$ git gc

Enumerating objects: 22, done.

Counting objects: 10022/22), done.

Delta compression using up to 4 threads

Compressing objects: 10019/19), done.

Writing objects: 10022/22), done.

Total 22 (delta 7), reused 0 (delta 0)

再次确认refs文件夹

$ find .git/refs -type f

.git/refs/remotes/origin/HEAD

看一下.git/packed-refs的内容

$ cat .git/packed-refs

# pack-refs with: peeled fully-peeled sorted

f28e02247cff4fba48760261b6105814c50a5477 refs/heads/master

8242b7851ba042a417e9654a0140adb2251b0427 refs/heads/testing

c22bd51211a0f96b13b60eeed40ef9d421c0c581 refs/original/refs/heads/testing

f28e02247cff4fba48760261b6105814c50a5477 refs/remotes/origin/master

f28e02247cff4fba48760261b6105814c50a5477 refs/remotes/origin/master2

8242b7851ba042a417e9654a0140adb2251b0427 refs/remotes/origin/testing

8242b7851ba042a417e9654a0140adb2251b0427 refs/remotes/origin/testing2

数据回复

有时会发生误删了某个分支,或者错误的硬重置了某个提交的情况,这个时候涉及到数据回复的问题。

比如有如下的提交记录

$ git log --pretty=oneline

a08b9dc260f73d98ece1bd30af95f7c54dfc17a8 (HEAD -> master) 5th commit

20b3399f51a8df88989c550081e836db93819368 4th commit

275d71bae6a6a2682bbde45755147c0d6f650c6b 3rd commit

86f4c3bf86176258117ccfaa910fc7a9da19a004 2nd commit

8547f1700b8fe2c7feececf1770409e311c39081 1st commit

现在将这个分支重置到第三次提交

$ git reset --hard 275d71bae6a6a2682bbde45755147c0d6f650c6b

HEAD is now at 275d71b 3rd commit

$ git log --pretty=oneline

275d71bae6a6a2682bbde45755147c0d6f650c6b (HEAD -> master) 3rd commit

86f4c3bf86176258117ccfaa910fc7a9da19a004 2nd commit

8547f1700b8fe2c7feececf1770409e311c39081 1st commit

现在,第四和第五次提交已经丢失了。但是它们还是存在于Git的数据库中。因为它们是历史的一部分。

那么找回这两个提交就需要它们的SHA-1。但是SHA-1可能没有人为记录下来。可以使用如下的命令来取得它们的SHA-1

这个命令显示的列表基于.git/log的内容

$ git reflog

275d71b (HEAD -> master)HEAD@{0}: reset: moving to 275d71bae6a6a2682bbde45755147c0d6f650c6b

a08b9dc HEAD@{1}: commit: 5th commit

20b3399 HEAD@{2}: commit: 4th commit

275d71b (HEAD -> master) HEAD@{3}: commit: 3rd commit

86f4c3bHEAD@{4}: reset: moving to 86f4c3bf86176258117ccfaa910fc7a9da19a004

376f50d HEAD@{5}: commit: 6th commit

86f4c3b HEAD@{6}: reset: moving to 86f4c3b

edd4d1d HEAD@{7}: commit: 5th commit

f2b1159 HEAD@{8}: commit: 4th commit

0606a6e HEAD@{9}: reset: moving to 0606a6e

86f4c3b HEAD@{10}: reset: moving to HEAD~

0606a6e HEAD@{11}: reset: moving to 0606a6e

86f4c3b HEAD@{12}: reset: moving to HEAD~

0606a6e HEAD@{13}: reset: moving to 0606a6e

86f4c3b HEAD@{14}: reset: moving to HEAD~

0606a6e HEAD@{15}: commit: 3rd commit

86f4c3b HEAD@{16}: commit: 2nd commit

8547f17 HEAD@{17}: commit (initial): 1st commit

可以看到HEAD@{1}和HEAD@{2}就是被丢弃的两个提交。我们可以通过创建一个新分支来取得这两个提交

$ git branch recover a08b9dc

$ git log --pretty=oneline recover

a08b9dc260f73d98ece1bd30af95f7c54dfc17a8 (recover) 5th commit

20b3399f51a8df88989c550081e836db93819368 4th commit

275d71bae6a6a2682bbde45755147c0d6f650c6b (HEAD -> master) 3rd commit

86f4c3bf86176258117ccfaa910fc7a9da19a004 2nd commit

8547f1700b8fe2c7feececf1770409e311c39081 1st commit

好了, 被丢弃的两个提交通过一个新的分支借尸还魂了。

为了介绍另外一个方法,先把刚生成的recover分支删除

$ git branch -D recover

Deleted branch recover (was a08b9dc).

并且删除.git/log

$ rm -Rf .git/logs

现在确认一下reflog

$ git reflog

发现结果列表已经空了。这个时候去使用下面的命令可以查找所有没有被分支引用的提交

$ git fsck --full

Checking object directories: 100256/256), done.

dangling commit a08b9dc260f73d98ece1bd30af95f7c54dfc17a8

dangling commit edd4d1d429c4753c4939d7b22c69ee5acf63c3a2

dangling commit 376f50dec9706ce1c66b8c552e95ffcf51be0d66

被丢弃的两个分支存在在这个列表中。这时就可以通过上述新建分支的方法来讲提交找回来。

移除对象

我们知道,Git是会保留所有操作历史的。如果某一个提交提交一个非常大的文件,而后续发现这个提交没有用处将其删除。

在其他用户clone这个工程的时候,这个非常大的文件也会以对象的形式下载。这会严重拖慢下载速度,而且占用磁盘控件。

Git提供了一劳永逸的解决办法来删除这样的文件。但是因为会破坏历史,笔者不建议使用。 感兴趣可以参照Progit的Git内部原理,维护与数据回复章节。

推荐阅读