在开发过程中往往会使用第三方库,可有时候会因为某种原因不使用了,我们可以使用 reset
或是直接删除的方式将库删除,比如开发中用到了某度的SDK,可是发现crash的概率太大需要舍弃之。可是,这些库的影子还在版本库的历史中可以找到,会增大库的体积。这时候我们就需要完全清楚这些“残留”文件。
本篇内容可在Git权威指南第14章中找到。
松散对象
Git中对于以SHA1值作为目录名和文件保存的对象称之为松散对象。因此 add,commit等操作都会产生一系列的松散对象。大量的松散对象会造成访问效率低下,占用过多存储空间。在Git中可以通过打包的方式提高访问效率,同时也方便通过增量更新的方式节省存储空间。
暂存区中的临时对象
添加到暂存区的文件发现不需要了,我们应该怎么办?
首先我们复制2个稍大的(10M)的文件到工作区,然后添加到暂存区:
$ cp /Users/xxxxx/Desktop/test.zip bigfile
$ cp /Users/xxxxx/Desktop/test.zip bigfile.dup
$ git add bigfile bigfile.dup
此时查看工作区和版本库的大小:
$ du -sh
31M .
$ du -sh .git/
10M .git/
因为添加到库中的文件Git是以blob对象的形式存储,对于相同文件只存储一份,所以库的大小是10M。
查看添加后的对象:
$ find .git/objects/ -type f
.git/objects//5d/1150e0e75f30161bba7799170c887d8f1104a0
然后我们把添加的文件撤出暂存区:
$ git reset HEAD
$ git status -s
?? bigfile
?? bigfile.dup
这时再查看大小:
$ du -sh
31M .
$ du -sh .git/
10M .git/
发现大小并没有变化,这说明版本库中还保存着那个对象,可以通过fsck
命令查看:
$ git fsck
Checking object directories: 100% (256/256), done.
Checking objects: 100% (65/65), done.
dangling blob 5d1150e0e75f30161bba7799170c887d8f1104a0
要将这个文件从版本库彻底删除那么需要执行prune
:
$ git prune
$ du -sh .git/
172K .git/
此时可以看出版本库已经不包含文件对象
重置操作引入的对象
对于已经添加到版本库中的文件,又该如何清除相应的文件对象呢?我们还是添加2个文件并commit:
$ git add bigfile bigfile.dup
$ git commit -m "add bigfiles"
[master a5feb75] add bigfiles
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 bigfile
create mode 100644 bigfile.dup
此时查看文件对象:
$ find .git/objects/ -type f
.git/objects//5d/1150e0e75f30161bba7799170c887d8f1104a0
.git/objects//6b/ff29c5d2ae75913cb00de0887f6bc1e4dfbc31
.git/objects//a5/feb75fd7255b0085b92448a14d2fae62ea75be
使用 git cat-file -t 5d11
可以查看文件对象的类型,上述3个对象的类型分别为:树,提交,和blob。
接下来使用reset
:
$ git reset --hard HEAD^
HEAD is now at 6652a0d Add Images for git treeview.
现在查看文件对象会发现还是存在上述的3个对象:
$ find .git/objects/ -type f
.git/objects//5d/1150e0e75f30161bba7799170c887d8f1104a0
.git/objects//6b/ff29c5d2ae75913cb00de0887f6bc1e4dfbc31
.git/objects//a5/feb75fd7255b0085b92448a14d2fae62ea75be
这时执行fsck
会发现没有找到未关联的对象:
$ git fsck
Checking object directories: 100% (256/256), done.
Checking objects: 100% (65/65), done.
很显然,所有的对象都是关联的,因为我们还可以撤销该次提交。要找到该文件对象,需要使用:
$ git fsck --no-reflogs
Checking object directories: 100% (256/256), done.
Checking objects: 100% (65/65), done.
dangling commit a5feb75fd7255b0085b92448a14d2fae62ea75be
由于所有的记录都存储在reflog
中,所以我们需要清除reflog
,在这里需要注意:
$git reflog expire --all
$ git reflog
6652a0d HEAD@{0}: reset: moving to HEAD^
a5feb75 HEAD@{1}: commit: add bigfiles
这只会清空90天前的数据,可以带date
参数来指定日期:
$ git reflog expire --expire=now --all
$ git reflog
此时记录全部清除,可以使用fsck
和prune
了:
$ git fsck
Checking object directories: 100% (256/256), done.
Checking objects: 100% (65/65), done.
dangling commit a5feb75fd7255b0085b92448a14d2fae62ea75be
$ git prune
$ du -sh .git/
164K .git/
git gc
实际使用中很少使用prune
,而是使用git gc
,该命令可以执行一系列的优化:主要是打包文件,丢弃过期的历史,清除未关联的对象等等。
$git gc
//将未关联的对象从打包文件中移出,成为松散文件
$git gc --expire=now
//直接清除未关联的文件,释放存储空间
实际中,git的某些操作会自动执行gc
命令,因此不需要手动执行gc
进行版本库的清理了。
Comments