git原理性概念
参考资料
近期学习git内部概念及原理,为了加强效果和日后回忆,还是写了这篇笔记进行输出。
参考资料如下:
- 这才是真正的Git——Git内部原理 - LZANE | 李泽帆(靓仔)
- 《Version Control with git》
实验环境:unbuntu 18
objects(blobs tree commits tag) & hash
git里的object 有四种:tree object 、 blob(Binary Large Object) object、 commit object、 tag object
git 会为每个object生成一个SHA1哈希码作为“身份证”。
在git仓库中添加一个文件,git将创建一个blob object对应这个文件。
git的blob object内容与文件内容相关,而与文件名无关。git将对文件的内容
使用SHA1哈希算法,产生一串哈希码。该哈希表唯一标识该文件与其对应的object
文件的路径名(pathname)等信息,由tree object 存储。其组织方式类似于Linux文件组织方式。
每一次commit都会产生一个commit object,同样有hash码对应每次commit
只要文件内容相同,blob object的hash就是相同的,因为hash码只对文件内容起作用。
文件内容相同,且文件组织方式也相同时,tree object的hash码才会相同。
commit object的hash码很难相同,因为除了文件内容,提交人的name、email、提交时间等信息都会参与到hash码的计算中
tag则用来引用(reference)一个commit,分为lighweight 和 annotated tag。前者不会创建object,后者则会创建object。许多git命令都只对后者有效。同样的,tag object也会对应一个hash码。
working directory、index、object store/repository(工作区、暂存区、仓库)
注意:在repository的内容不能被删除或修改,只能添加!
-
working directory 存放工作linux目录中的内容
-
index(暂存区)存放一种“索引”,记录目录之间的关系,通常git add 命令会修改该索引,git commit则会将“索引”转换为tree object存储在仓库中
-
object store(仓库)存放4中object,各个object都有hash码与其对应,git commit将会在这个区域中增加object,但不会修改\删除object
先新建一个文件夹,初始化为一个git仓库
ljc@ljc:~/gitlearning$ mkdir project
ljc@ljc:~/gitlearning$ cd project/
ljc@ljc:~/gitlearning/project$ git init
Initialized empty Git repository in /home/ljc/gitlearning/project/.git/
ljc@ljc:~/gitlearning/project$ ll
total 12
drwxrwxr-x 3 ljc ljc 4096 3月 1 19:37 ./
drwxrwxr-x 4 ljc ljc 4096 3月 1 19:37 ../
drwxrwxr-x 7 ljc ljc 4096 3月 1 19:37 .git/
初始化后添加文件、提交文件
在这之前,可以先了解一下git ls-file 和 git cat-file命令:
git-ls-files - Show information about files in the index and the working tree
git-ls-files -s #展示被staged的file对象的哈希值等信息
git cat-file -p <对象哈希码> # 可该哈希码对应的object的具体内容
git cat-file -t <对象哈希码> # 可查看
初始化
新建一个project文件,使用git init初始化git仓库
观察.git/objects文件夹, 目前发现只有info、pack这两文件夹
添加两个文件,分别写入内容“foo” “bar”,使用git status可知晓这两个文件目前还没有加入暂存区
目前,index区(暂存区)和object store中没有任何东西
git add 后
将两个文件staged,可以观察到,.git/objects目录下,多出两个blob object,分别对应file1和file2,说明此时两个file对应的object已经被存储
途中这一连串的十六进制码就是object对应的哈希码。且他们的内容存储于.git/objects目录下,为了防止文件数目过大,采用了层级式目录的方式存储object,取hash码的前两个字符先创建一个子目录,然后在子目录中存储对应的object:
且在index也已经创建出了某个index,指向了这两object。由于index没有对应的object,因此不能在.git/objects目录下看到对应的变化。但可以观察./git目录,发现多出来一个index文件
,猜想这个文件就对应着Index区。
那么目前三个区域的内容如下所示:
git commit 后
最后commit这两个file:
此时,./git/objects 中会出现两个新的object
使用git cat-file -p查看这两个object类型:
可以发现,其中一个是commit object和一个tree object。
因为git commit后会将index转化为tree object,然后生成一个commit object“指向”该tree object。此时,三个区的内容如下:
看看tree object 和 commit object的内容
tree object像一个文件目录一样把其中的文件关系记录下来。commit object则存储了提交者的信息以及提交信息。
分支
创建分支
创建分支不会产生任何object,分支名只是一种引用,指向了某个commit,它会在.git/refs/heads中。
下面创建分支branch1:
使用git cat-file -p 命令查看他们的内容
看到它们的输出与commit object的输出相同,但它们并不是object本身,因此可以将它们视作一种引用。而且,目前在branch1分支与master分支的指向是相同的,目前的存储区域内容如下所示,几乎没有任何影响。
分支中进行commit
此时object store中将加入三个新的object,分别为blob object、 tree object和commit object。
存储区域的内容变更如下图:
git不会修改object store中的内容
,因此即使在working directory中的某些文件被修改,git会直接创建另一个blob object并重新计算哈希值,且将index的引用至新的blob object上。
当commit时,先将index转化为新的tree object(对应hash值为abd31),然后创建一个commit object(对应hash值为d14e3)使其引用新的tree object。
合并分支
这里不考虑复杂的合并情况,也不考虑合并冲突的情况。但为了不至于太简单,首先切回master分支,然后在file2中新加上一行内容,最后commit该改动。
此时,两个分支中的4个文件内容分别为:
在master分支上,执行git merge合并branch1:
git会自动生成commit object来执行这次的merge:
存储区域的内容变更如下图: