14.4 Git管家:git-gc
前面两节介绍的是比较极端的情况,实际操作中会很少用到git prune命令来清理版本库,而是会使用一个更为常用的命令git gc。命令git gc就好比Git版本库的管家,会对版本库进行一系列的优化动作:
(1)对分散在.git/refs下的文件进行打包,打包到文件.git/packed-refs中。
如果没有将配置gc.packrefs关闭,就会执行命令:git pack-refs—all—prune实现对引用的打包。
(2)丢弃90天前的reflog记录。
会运行reflog过期命令:git reflog expire—all。因为采用了默认参数调用,因此只会清空reflog中90天前的记录。
(3)对松散对象进行打包。
运行git repack命令,凡是有引用关联的对象都被打在包里,未被关联的对象仍旧以松散对象的形式保存。
(4)清除未被关联的对象。默认只清除2周以前的未被关联的对象。
可以向git gc提供—prune=<date>参数,其中的时间参数传递给git prune—expire<date>,实现对指定日期之前的未被关联的松散对象进行清理。
(5)其他清理。
如运行git rerere gc对合并冲突的历史记录进行过期操作。
从上面的描述中可见命令git gc完成了相当多的优化和清理工作,并且最大限度地照顾了安全性的需要。例如像暂存区操作引入的没有关联的临时对象会最少保留2个星期,而因为重置而丢弃的提交和文件则会保留最少3个月。
下面就把前面的例子用git gc再执行一遍,不过这一次添加的两个大文件要稍有不同,以便看到git gc打包所实现的对象增量存储的效果。
复制两个大文件到工作区。
$cp/tmp/bigfile bigfile
$cp/tmp/bigfile bigfile.dup
在文件bigfile.dup后面追加些内容,以造成bigfile和bigfile.dup内容不同。
$echo "hello world">>bigfile.dup
将这两个稍有不同的文件提交到版本库。
$git add bigfile bigfile.dup
$git commit-m "add bigfiles."
[master c62fa4d]add bigfiles.
2 files changed,0 insertions(+),0 deletions(-)
create mode 100644 bigfile
create mode 100644 bigfile.dup
可以看到版本库中提交进来的两个不同的大文件是不同的对象。
$git ls-tree HEAD|grep bigfile
100644 blob 2ebcd92d0dda2bad50c775dc662c6cb700477aff bigfile
100644 blob 9e35f946a30c11c47ba1df351ca22866bc351e7b bigfile.dup
做版本库重置,抛弃最新的提交,即抛弃添加两个大文件的提交。
$git reset—hard HEAD^
HEAD is now at 6652a0d Add Images for git treeview.
此时的版本库有多大呢,还是像之前添加两个相同的大文件时占用11MB的空间么?
$du-sh.git/
22M.git/
版本库空间占用居然扩大了一倍!这显然是因为两个大文件分开存储造成的。可以用下面的命令在对象库中查看对象的大小。
$find.git/objects-type f-printf "%-20p\t%s\n"
.git/objects/0c/844d2a072fd69e71638558216b69ebc57ddb64 233
.git/objects/2e/bcd92d0dda2bad50c775dc662c6cb700477aff 11184682
.git/objects/9e/35f946a30c11c47ba1df351ca22866bc351e7b 11184694
.git/objects/c6/2fa4d6cb4c082fadfa45920b5149a23fd7272e 162
.git/objects/info/packs 54
.git/objects/pack/pack-969329578b95057b7ea1208379a22c250c3b992a.idx 2892
.git/objects/pack/pack-969329578b95057b7ea1208379a22c250c3b992a.pack 80015
输出的每一行用空白分隔,前面是文件名,后面是以字节为单位的文件大小。从上面的输出中可以看出来,打包文件很小,但是有两个大的文件各自占用了11MB左右的空间。
执行git gc并不会删除任何对象,因为这些对象都还没有过期。但是会发现版本库的占用空间变小了。
执行git gc对版本库进行整理。
$git gc
Counting objects:69,done.
Delta compression using up to 2 threads.
Compressing objects:100%(49/49),done.
Writing objects:100%(69/69),done.
Total 69(delta 11),reused 63(delta 8)
版本库空间占用小了一半!
$du-sh.git/
11M.git/
原来是因为对象库重新打包,两个大文件采用了增量存储使得版本库变小。
$find.git/objects-type f-printf "%-20p\t%s\n"|sort
.git/objects/info/packs 54
.git/objects/pack/pack-7cae010c1b064406cd6c16d5a6ab2f446de4076c.idx 3004
.git/objects/pack/pack-7cae010c1b064406cd6c16d5a6ab2f446de4076c.pack 11263033
如果想将抛弃的历史数据彻底丢弃,进行如下操作。
(1)不再保留90天的reflog,而是将所有reflog全部即时过期。
$git reflog expire—expire=now—all
(2)通过git fsck可以看到有提交成为了未被关联的提交。
$git fsck
dangling commit c62fa4d6cb4c082fadfa45920b5149a23fd7272e
(3)这个未被关联的提交就是添加大文件的提交。
$git show c62fa4d6cb4c082fadfa45920b5149a23fd7272e
commit c62fa4d6cb4c082fadfa45920b5149a23fd7272e
Author:Jiang Xin<jiangxin@ossxp.com>
Date:Thu Dec 16 20:18:38 2010+0800
add bigfiles.
diff—git a/bigfile b/bigfile
new file mode 100644
index 0000000..2ebcd92
Binary files/dev/null and b/bigfile differ
diff—git a/bigfile.dup b/bigfile.dup
new file mode 100644
index 0000000..9e35f94
Binary files/dev/null and b/bigfile.dup differ
(4)不带参数调用git gc虽然不会清除尚未过期(未到2周)的大文件,但是会将未被关联的对象从打包文件中移出,成为松散文件。
$git gc
Counting objects:65,done.
Delta compression using up to 2 threads.
Compressing objects:100%(45/45),done.
Writing objects:100%(65/65),done.
Total 65(delta 8),reused 63(delta 8)
(5)未被关联的对象重新成为松散文件,所以.git版本库的空间占用又反弹了。
$du-sh.git/
22M.git/
$find.git/objects-type f-printf "%-20p\t%s\n"|sort
.git/objects/0c/844d2a072fd69e71638558216b69ebc57ddb64 233
.git/objects/2e/bcd92d0dda2bad50c775dc662c6cb700477aff 11184682
.git/objects/9e/35f946a30c11c47ba1df351ca22866bc351e7b 11184694
.git/objects/c6/2fa4d6cb4c082fadfa45920b5149a23fd7272e 162
.git/objects/info/packs 54
.git/objects/pack/pack-969329578b95057b7ea1208379a22c250c3b992a.idx 2892
.git/objects/pack/pack-969329578b95057b7ea1208379a22c250c3b992a.pack 80015
(6)实际上如果使用立即过期参数—prune=now调用git gc,就不用再等2周了,直接就可以完成对未关联的对象的清理。
$git gc—prune=now
Counting objects:65,done.
Delta compression using up to 2 threads.
Compressing objects:100%(45/45),done.
Writing objects:100%(65/65),done.
Total 65(delta 8),reused 65(delta 8)
(7)清理过后,版本库的空间占用降了下来。
$du-sh.git/
240K.git/