先说结论,git使用了Delta增量压缩算法,git-lfs实测没有进行任何压缩,这个结论让我很震惊。
测试过程如下:
测试git仓库自身的压缩
准备一个包含许多杂项文件的文件夹,大概几百M,要保证有一个txt文本文件,做修改用,我们就叫这个文件夹为[数据包]。
将[数据包]压缩为TestFile.zip,我这里压缩结果大小为115M,然后放进本地仓库里。
步骤1、将TestFile.zip进行add、commit然后push到远程仓库:
步骤2、对[数据包]中的一个txt文件稍做修改,依旧是压缩为TestFile.zip,然后替换掉本地git仓库的同名文件,从而模拟修改,再次执行步骤1。
将步骤1、2这一过程循环执行3遍,这样的话,就意味着有三个115M大小的文件被push到远程仓库中,我们来验证一下远程中央仓库大小:
如上所示,远程仓库的大小确实是3*115=345M,也就是说,无论你连续几次提交之间的差异有多小,在提交并推送时,远程仓库并不会[立即]进行Delta压缩。
我们再看一下咱们的本地仓库大小:
本地仓库显示459M,实际也是合理的,因为本地仓库是[工作空间]+[仓库]:
好了,下面就是见证git的压缩技术的时候了,我们先在远程中央仓库执行压缩命令git gc:
压缩完之后,我们再看一下远程仓库的空间大小:
远程仓库的空间变成了115M,和只传了一个TestFile.zip占用的空间一样,但确实是包含了三次commit的全部历史版本数据,对每次的TestFile.zip文件的修改都能追溯。这说明了如果直接用git管理大文件,在历次对大文件的修改不大的前提下,git的Delta压缩会极大的节约空间,因为只保留历次文件修改之间的区别。
我们再对本地仓库进行下git gc看看:
结果和远程仓库一样,除了工作空间不受影响以外,仓库空间被极大的压缩,但同样在小体积的同时保留了所有对于TestFile.zip文件的历次修改。
好了,以上就是git自身对于仓库文件的压缩,下面,咱们再看git-lfs,我原本以为git-lfs作为专为管理大文件而生的git扩展,自然有对空间管理这方面的牛b之处,没想到一番测试下来大跌眼镜。
首先分别给远程中央仓库与本地仓库进行lfs的初始化:
命令:git lfs install
远程中央仓库:
本地仓库:
然后再从本地仓库执行以下命令git lfs track "*.mp4",让git-lfs负责管理.mp4格式的文件:
接下来将上面命令所生成的git-lfs配置文件.gitattributes推送到远程仓库:
上面都妥当之后,就该咱们的老朋友TestFile.zip登场了,他将继续作为测试git-lfs大文件夹存储压缩方面的关键人。
TestFile.zip:唉?不对啊,你上面不是配置了只让git-lfs管理mp4文件吗?怎么还是我?
作者:你改下后缀名不就是个mp4了嘛!
TestFile.mp4:哦~,也是哦,真有你的……
好了,那就把[数据包]中的txt文件内容稍作修改,依旧压缩为TestFile.zip,然后改后缀名为TestFile.mp4,复制到本地仓库中:
步骤1、将TestFile.mp4进行add、commit然后push到远程仓库:
步骤2、对[数据包]中的一个txt文件稍做修改,依旧是压缩为TestFile.zip,然后依旧是改后缀名为TestFile.mp4,然后替换掉本地git仓库的同名文件,从而模拟修改,再次执行步骤1。
将步骤1、2这一过程循环执行3遍,这样的话,就意味着有三个115M大小的文件被push到远程仓库中,我们来验证一下远程中央仓库大小:
我们发现远程仓库的大小还是115m,那是因为远程仓库上的git仓库与存储大文件的lfs仓库路径是不同的,在部署gitea托管平台的时候会设置lfs的路径,所以我们到lfs路径下去看看:
然后我们看一下本地仓库,之前的三个TestFile.zip文件还是存放在objects文件夹下,还是占用114m,而lfs管理的mp4文件是存放到了新增加的lfs文件夹下了,三个mp4文件,3*115=345m:
好了,我们现在照猫画虎,依旧是分别在远程仓库与本地仓库执行git gc命令进行仓库压缩,看看会发生什么:
远程仓库:
本地仓库:
同样是没有任何变化:
不是,合着你git lfs是一点压缩也不干啊?哪怕三个文件就只有一个字节的区别,你也是存三份?
git lfs:是啊,那我不就省下了压缩与解压缩的时间,不就更快了嘛!
我:我特么……
结论
就跟上面的实验一样,如果你的大文件会经常性的修改,你还是别用git lfs了,哪怕你一次只做一个字节的修改,git lfs也会完整的给你存一份,压缩空间?只存增量?不存在的。你要是经常改动某些大文件,git lfs仓库所在的服务器容量分分钟给你挤爆了。当然,你要说你服务器容量管够,当我没说。
那么哪些文件适合放进git lfs,我觉得要同时满足这两点:
1、很大
2、永远不会修改
如果不能同时满足这两点,劝你还是老老实实用git吧,或者用svn。
另外可能有些对git lfs有所了解的朋友会说:唉,不对啊,我记得git lfs有一个命令[git lfs prune]可以压缩空间啊。
那我只能遗憾的告诉你,这条语句不会对远程中央仓库产生半点影响,它只是暂时的将你本地用不到的lfs缓存文件给删除掉,中央服务器中依然是存储着过去的commit对于lfs缓存文件历史版本的引用。也就是说你本地删除掉的东西,随时都能从中央仓库down回来。
再次追加
我这两天又在外网上查阅了不少关于git-lfs的相关资料,最后得出结论:
不要用git-lfs
不要用git-lfs
不要用git-lfs
实际上git-lfs的唯一好处就是可以让使用人员在clone仓库时不用下载所有文件,仅此而已,但随着这两年git的更新,也可以实现clone时不下载所有内容了,所以git-lfs的唯一优势也基本没有了。
在stack上上有关于这点的详细讨论,有兴趣的可以去看看:
git lfs - How does git LFS track and store binary data more efficiently than git? - Stack Overflow
git lfs - Do I need Git LFS for local repos? - Stack Overflow