维护
之前在包文件的章节中提到过,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内部原理,维护与数据回复章节。