经常使用Git,有时候就会有个疑问冒出来:Git是如何保存我提交的这些内容的呢?今天总算花点时间搜索了一番,有了个基本的概念,写下来保存。
Git 和其他版本控制系统的主要差别在于,Git 只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异。这类系统(CVS,Subversion,Perforce,Bazaar 等等)每次记录有哪些文件作了更新,以及都更新了哪些行的什么内容,请看下图。
Git 并不保存这些前后变化的差异数据。实际上,Git 更像是把变化的文件作快照后,记录在一个微型的文件系统中。每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件作一快照,然后保存一个指向这次快照的索引。为提高性能,若文件没有变化,Git 不会再次保存,而只对上次保存的快照作一链接。Git 的工作方式就像下图所示。
这么作的优点就是,Git的几乎所有操作都是本地操作,你可以即使不联网,你依然可以查看历史,比较变更,提交修改,等到有网络时一次性传上去,而这点对其它VCS是不可能的。
而本地操作的另一个优点就是快!尤其在网络条件差的时候,这个优点更明显。
那么具体是如何实现的呢?
Git为每个项目在根路径下建立一个.git目录,它是 Git 用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据。目录结构大致如下:
我们需要关注的是objects目录,里面存放的是git对象。结构大致如下:
这便是 Git 存储数据内容的方式──为每份内容生成一个文件,取得该内容与头信息的 SHA-1 校验和,创建以该校验和前两个字符为名称的子目录,并以 (校验和) 剩下 38 个字符为文件命名 (保存至子目录下)。
SHA-1校验和用于索引内容,前两位用于创建目录,这样一个目录下不会有太多文件,而后38位字符作文件命名保存在该目录下,其内容是用gzip格式压缩的,可以用命令查看,查看时需要完整的SHA-1校验和。
这里用的git命令是
git cat-file -p object-id
-p参数是pretty-print的意思
以上这种方式存储的对象叫做松散对象(loose object),是以空间换时间的方式获得对对象访问速度的提升。还有一种方式使用打包文件(pack file),通过git gc
来打包文件以节省空间。
作为基本情况的了解就是这么多,更多深入的研究这里就不作涉及了。