Git 是一套内容寻址文件系统,那么Git是怎么进行寻址呢?其实,寻址无非就是查找,而Git采用HashTable的方式进行查找,也就是说,Git只是通过简单的存储键值对(key-value pair)的方式来实现内容寻址的,而key就是文件(头+内容)的哈希值(采用sha-1的方式,40位),value就是经过压缩后的文件内容。因此,在接下来的实践中,会经常通过40位的hash值来进行底层命令操作,几乎每一个底层命令都需要通过key来指定所要操作的对象。
一.Git的三种对象类型
1.BLOB(binary large object)对象:BLOB对象可以存储几乎所有的文件类型
2.tree对象:用来组织BLOB对象的一种数据类型
3.commit对象:每一次的提交操作,由tree对象衍生,每一个commit对象表示一次提交,在创建的过程中可以指定该commit对象的父节点,这样所有的commit操作便可以连接在一起,而这些commit对象便组成了提交树,branch只不过是这个树中的某一个子树罢了。
二.Git对象存储方式为:key-value
key=sha1(file_header + file_content)
value=zlib(file_content)
Git 将文件头与原始数据内容拼接起来,并计算拼接后的新内容的 40位的sha-1校验和,将该校验和的前2位作为object目录中的子目录的名称,后38位作为子目录中的文件名;然后,Git 用zlib的方式对数据内容进行压缩,最后将用 zlib 压缩后的内容写入磁盘.
三.对象暂存(缓存)区---------->了解git add的背后.Git如何使用底层命令完成文件索引操作的!
git add命令在底层命令中被分为两步:
1.通过git hash-object命令将需要暂存的文件进行key-value化转换成Git对象,并进行存储,拿到这些文件的key
git hash-object #获取指定文件的key,如果带上-w选项,则会将该对象的value进行存储
2.通过git update-index命令将这些对象加入到暂存区进行暂存,这样便完成了Git文件的暂存操作
git update-index #将指定的object加入索引库,需要带 --add选项
查看key对应的文件信息:
git cat-file –p -t key #获取指定key的对象信息,-p打印详细信息,-t打印对象的类型
四.实验验证-------->使用底层命令实现git add的操作
1)mkdir test && cd test && git init
2)该目录下会生成.git目录,查看.git/objects 目录下面没有文件 --------->find .git/objects -type f
Git 初始化了 objects 目录,同时在该目录下自动创建 pack 和 info 子目录,但是该目录下没有其他常规文件
3)将文件转换成git对象:
将该文件转换为Git的对象并存储解释:
A) 参数w 指示hash-object命令存储数据,若不指定这个参数该命令仅仅返回键值
B)该命令输出长度为 40 个字符的校验和
C)objects 目录下会生成一个文件,这便是 Git 存储数据内容的方式
hash-objec命令会返回该Git对象的key值,这时到.git目录的objects目录下会发现,多一个19的目录,该目录下有一个0a18037c64c43e6b11489df4bf0b9eb6d2c9bf的文件
4).查看存储文件的内容和git对象的类型:
查看存储文件的内容和git对象的类型5)将git对象加入到暂存库中:
放入暂存区--cacheinfo 指定文件类型: 100644代表普通文件
此时.git目录下会多一个index文件,以后每次每次update-index命令执行之后,该index文件的内容都会发生变化.
以上git add操作的底层命令已经完成.
五.index文件简谈
index是一个索引文件,存放的是暂存区的整个目录树的信息,并且为目录树中的每个文件都保存了时间戳和长度
1.index文件的格式为:
Index魔数(DIRC) + 版本号 + 暂存的文件个数 + 每个文件的时间戳和长度
2. Index索引库记录从项目初始化到目前为止,项目仓库中所有文件最后一次修改时刻的时间戳以及对应的长度信息,因此随着加入仓库中的文件不断增多,index文件也会不断增大.
3.git status命令,则会把每一个文件的索引信息和上次提交的索引信息进行比较,如果发生了变化,就会显示出来.
4.暂存操作会对每一个文件计算校验和(即前面提到的 SHA-1 哈希字串),然后把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 类型的对象存储这些快照),并将校验和加入暂存区域.
六.创建树节点
1.在Git中,所有的内容以tree或者BLOB对象进行存储.,其中 tree 对象对应于 UNIX 中的目录,blob 对象则大致对应于 inodes 或文件内容.
一个单独的tree对象包含一条或多条tree记录,每一条记录含有一个指向BLOB对象或子tree对象的sha-1指针(也就是一个40位的key值),并附有该对象的权限模式 、类型和文件名信息.
创建tree对象只是add和commit中间的一个缓冲步骤,因为commit对象要根据tree对象来创建
2.创建tree对象:
创建tree对象解释: cat-file -t显示该对象的类型是tree,表明tree对象创建成功.至此,tree节点创建完成.
由于index暂存区包括了项目仓库中所有的文件,因此commit对象所对应的tree对象,永远都是工作目录的根tree对象。也就是说,每次commit,都是把工作目录的根目录所对应的tree对象,链接给此次的commit对象;而且,在Git中,每个子目录都对应一个tree对象,每个文件对应一个BLOB对象,因此整个工作目录对应一棵Git对象树,根节点就是commit对象所引用的tree节点,而每个子文件夹又分别对应一棵子树。所以任何一个文件的更改,都会导致其上层所有父对象的更改和重新存储
七.commit对象
在Git中,每一次commit都对应一个commit对象,而一个commit对象对应一个tree对象,该tree对象是工作目录的根tree对象.
1.创建commit对象
创建tree对象解释:
1)由于这是第一次提交,因此不需要带上-p选项来指明父节点
2)该commit对象中包含了与之关联的tree对象的key值,以及author和committer的信息
3)git log --stat key命令,该命令会打印指定commit对象之前的所有提交记录
2.目前为止blob对象,tree对象,commit对象之间的关系:
第一次提交后Git对象关系图八.提交树---->commit tree
操作要求:在第一次提交的基础上完成第二次提交和第三次提交,
第二次提交会提交ceshi的第二个版本和增加一个新的文件并提交;
第三次提交会演示在tree对象中构造子tree对象并提交.
在下面的每一次提交中,还需要指定每一次提交的前继提交对象,这样commit对象便连接在一起,形成一棵提交树.
第二次提交操作:
1.修改ceshi文件和创建newfile文件并将文件转变成git对象:
转换成git对象此时.git/object目录下会存在a5和1e目录.
2.将git对象添加早暂存区中:
放入暂存区中3.利用暂存区创建tree对象
创建tree对象4.根据该tree对象创建commit对象
指定前继tree对象5.查看commit对象的内容:
查看commit对象的内容6.目前位置git对象关系:
第二次提交后Git对象关系图第三次提交(演示在tree对象中构造子tree对象并提交):
1.将版本1中的tree对象读取到暂存区中:
git read-tree --prefix=bak 39314df08ba40c2cd50eb0276fc80129189efaa2
解释:在读取的过程中,需要加--prefix选项,否则无法成功读取,这是因为在index中相同路径的文件只能出现一次,由于ceshi已经存在于index索引库了,因此如果想把第一个版本的tree对象读取进来,需要将该版本的ceshi放在文件夹bak中
2.创建tree对象并提交commit对象:
创建tree对象并提交commit对象3.查看tree对象包含的内容:
.查看tree对象包含的内容4.git对象之间的关系:
第三次提交后Git对象关系图
网友评论