Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency
上面这句话摘自git官网,意思是说git是一个分布式版本控制系统,快速高效的处理大小型项目的所有事情。那么git如何做到快速高效的呢。我们先写一个demo来演示一下效果,然后我们深入讲一下原理.
(操作这之前,需要自己在电脑上安装git,这里不再讲怎么安装git和命令的使用)
首先我们进到D盘,创建gitDemo这个文件夹,在这个文件夹下打开git,使用git init命令初始化一个git版本库
git init
我们在当前目录下创建一个以“hunter”命名的txt文件
在hunter.txt中写上点字
现在将他提交到git版本库
git add .
git commit -m "第一次提交"
这个时候我们创建一个新的分支,并切换到新的分支
git checkout -b dev
dev这个分支没有指定分支,则是在master(刚刚创建的)分支上创建的,则这时hunter.txt文件的内容还是"11111"
我们在dev分支上对hunter.txt文件进行改动然提交
将hunter.txt 内容由"11111"改为"22222"
git add .
git commit -m "第二次改动"
下面我们在切回到master分支
git checkout master
发现hunter.txt文件只有一个,但是该文件在每个分支的内容是不同的,那git内部是怎么存储的呢,接下来我们来分析git的原理。
在git内部维护了四个对象,分别是blob对象,commit对象,tree对象,tag对象。
我先来画一下这几个对象的关系,稍后我们代码演示一遍
当我们从工作空间 add 到暂存区之后,这时候每个被更改的文件会形成一个blob对象,blob对象里面的内容是原始内容的压缩,blob文件的文件名是根据原始内容的hash值。
当我们commit的时候,会形成一个或者多个tree对象和一个commit对象,到底形成几个是根据我们的文件目录层级来定的(一会我们演示的时候注意一下)。
最终这些对象将会保存在.git文件夹的objects里面
好了,上面这些暂时缓存在自己的大脑里,下面我们带着这些抽象的东西去coding印证一下
先介绍几个命令
find .git/objects/ -type f 查看git里所有的object
git cat-file -t #{对象名} 查看对象的类型
git cat-file -p #{对象名} 查看对象的内容
我们重置一下git版本库,进入到d盘,新建gitDemo文件夹,在gitDemo文件夹下打开git命令窗口
git init
新建一个hunter.txt文件,在文件里随便写一些东西
add之前先用find .git/objects/ -type f 命令看一下目前有多少对象
没有对象,我们add一下
git add .
形成一个对象,我们看一下这个对象的类型
git cat-file -t 8b25206ff90e9432f6f1a8600f87a7bd695a24af
看一下文件里面存的什么,我们先直接进到这个文件里面打开,然后再用命令打开
打开文件之后,并不能看到里面到底存的是什么,这里印证了前面所说的blob里面存的是原始文件的压缩内容
再用命令打开看一下
git cat-file -p 8b25206ff90e9432f6f1a8600f87a7bd695a24af
确实是这个blob对象里面存储了我们的内容,下面我们commit到本地仓库,并且查看新形成的git对象
git commit -m "第一次提交"
find ./git/objects/ -type f
此时多了两个对象,我们分别用命令查看一下他们分别是什么类型的对象,以及对象里的内容
到这里你是不是发现了点什么,对着这张图屡一下
8b25206ff90e9432f6f1a8600f87a7bd695a24af 是blob对象,里面存储了具体的消息内容
a8d587b2fc8e2067f6f6a080268d8412332a2875 是tree对象,里面存储了blob的hash值,以及blob对象真实的文件名字
55b9744d8955c92dc57f3bf63bf6aaf284dfd47e 是commit对象,里面存储了tree的hash值,提交时备注内容,作者等信息
到此我们实现了一个简单的git结构,git每一次选择分支,回滚都是怎么做的呢,继续来看
在.git文件夹下还有一个refs文件夹,这里面存储了所有的分支信息和tag信息
为了更好的区分我有新建了一个dev分支
git checkout -b dev
这些分支里面存的其实就是commit的值
明白了,其实git里每一个分支是一个文件,这个文件里保存着commit的值,根据commit的值找到tree,然后拿出来tree中所有的子tree和blob来展现所有的内容,问题又来了,那我肯定有一个地方标记当前工作空间所在的分支吧。莫急,这个标记的地方就在git文件夹下的head文件。
到这我们完全明白 工作空间根据git文件加下的head文件找到当前的分支,然后拿到分支进一步拿到commit从而得到所有的内容。
但是还有点什么东西需要注意一下小编直接告诉你这些,你可以自己coding验证。
①blob的形成是根据内容来形成的,换句话说如果我有两个文件A和B,A中的内容为“master”,B中的内容也是"master",提交时则只会形成一个blob对象。反过来,如果内容不一样则会形成两个blob对象。
②每一次add的时候,只有变化的文件才会形成新的blob并且原来的blob依然存在。
③每一次commit都会绘制一个新的tree,并且不复用之前的子tree,但是blob不会变。