Git对象

现在已经明白Git的基本流程,但Git是怎么完成的呢?Git怎么区分文件是否发生变化?下面简单介绍一下Git的基本原理。

SHA-1 校验和

Git 是一套内容寻址文件系统。意思就是Git 从核心上来看不过是简单地存储键值对(key-value),value是文件的内容,而key是文件内容与文件头信息的 40个字符长度的 SHA-1 校验和,例如:5453545dccd33565a585ffe5f53fda3e067b84d8。Git使用该校验和不是为了加密,而是为了数据的完整性,它可以保证,在很多年后,你重新checkout某个commit时,一定是它多年前的当时的状态,完全一摸一样。当你对文件进行了哪怕一丁点儿的修改,也会计算出完全不同的 SHA-1 校验和,这种现象叫做“雪崩效应”(Avalanche effect)。

SHA-1 校验和因此就是上文提到的文件的指针,这和C语言中的指针很有些不同:C语言将数据在内存中的地址作为指针,Git将文件的 SHA-1 校验和作为指针,目的都是为了唯一区分不同的对象。但是当C语言指针指向的内存中的内容发生变化时,指针并不发生变化,但Git指针指向的文件内容发生变化时,指针也会发生变化。所以,Git中每一个版本的文件,都有一个唯一的指针指向它。

文件(blob)对象,树(tree)对象,提交(commit)对象

blob 对象保存的仅仅是文件的内容,tree 对象更像是操作系统中的目录,它可以保存blob对象和tree 对象。一个单独的 tree 对象包含一条或多条 tree 记录,每一条记录含有一个指向 blob 对象或子 tree 对象的 SHA-1 指针,并附有该对象的权限模式 (mode)、类型和文件名信息等:file当你对文件进行修改并提交时,变化的文件会生成一个新的blob对象,记录文件的完整内容(是全部内容,不是变化内容),然后针对该文件有一个唯一的 SHA-1 校验和,修改此次提交该文件的指针为该 SHA-1 校验和,而对于没有变化的文件,简单拷贝上一次版本的指针即 SHA-1 校验和,而不会生成一个全新的blob对象,这也解释了10M大小的项目进行10次提交总大小远远小于100M的原因。

另外,每次提交可能不仅仅只有一个 tree 对象,它们指明了项目的不同快照,但你必须记住所有对象的 SHA-1 校验和才能获得完整的快照,而且没有作者,何时,为什么保存这些快照的原因。commit对象就是问了解决这些问题诞生的,commit 对象的格式很简单:指明了该时间点项目快照的顶层tree对象、作者/提交者信息(从 Git 设置的 user.name 和 user.email中获得)以及当前时间戳、一个空行,上一次的提交对象的ID以及提交注释信息。你可以简单的运行git log来获取这新信息:

  1. $ git log
  2. commit 2cb0bb475c34a48957d18f67d0623e3304a26489
  3. Author: lufficc <luffy.lcc@gmail.com>
  4. Date: Sun Oct 2 17:29:30 2016 +0800
  5. fix some font size
  6. commit f0c8b4b31735b5e5e96e456f9b0c8d5fc7a3e68a
  7. Author: lufficc <luffy.lcc@gmail.com>
  8. Date: Sat Oct 1 02:55:48 2016 +0800
  9. fix post show css
  10. ***********省略***********

file上图的Test.txt是第一次提交之前生成的,第一次它的初始 SHA-1 校验和以3c4e9c开头。随后对它进行了修改,所以第二次提交时生成了一个全新blob对象,校验和以1f7a7a开头。而第三次提交时Test.txt并没有变化,所以只是保存最近版本的 SHA-1 校验和而不生成全新的blob对象。在项目开发过程中新增加的文件在提交后都会生成一个全新的blob对象来保存它。注意除了第一次每个提交对象都有一个指向上一次提交对象的指针

因此简单来说,blob对象保存文件的内容;tree对象类似文件夹,保存blob对象和其它tree对象;commit对象保存tree对象,提交信息,作者,邮箱以及上一次的提交对象的ID(第一次提交没有)。而Git就是通过组织和管理这些对象的状态以及复杂的关系实现的版本控制以及以及其他功能如分支。