Git的过人之处
Git为什么是现在互联网最流行的版本控制工具,在我看来Git有其与众不同的地方,也就是过人之处:
首先它是分布式版本工具,每台电脑都有一个完整的版本库,我们可以随时在本地提交我们的代码,集中式版本工具,都要将代码提交到代码库的服务器中,如果没有网络,你想先把本地代码提交了,再进行下一个版本的开发就会变得非常不方便。
C语言的指针在Git内部实现中运用得淋漓尽致,无论是分支创建,分支的合并本质上都是对指针的调整,所以Git非常推荐我们使用分支,哪怕是修复一个小小的bug,都可以创建一个分支进行修复,因为Git分支创建和销毁的代价都是非常小的,仅仅是删除和移动指针而已。而在SVN等工具中想要创建和销毁分支的代价是非常大的,显得非常笨重不方便。
Git是保存的是快照流,而SVN保存的是版本之间的差异。这也是为什么Git进行版本回溯比其他工具要快。
简述了下Git的过人之处,那么就来看看其内部的实现原理。
快照和差异比较的区别
SVN的存储方式如下图:
其实就是保存每个文件不同版本之间的差异。那么回溯版本的时候是不是需要拼接或者剔除差异后,才能得到最终的内容,如果一个文件版本的迭代次数越多,版本的回溯就会越慢。
那么,Git的快照是怎么存储的呢?
就是同个文件,每个版本都会保存一份快照,如果下个版本文件没有修改,就会延用原先的快照,用一个指针指向原先的快照即可。
那么Git怎么知道文件没有被修改呢?其实Git会用一种SHA-1的散列算法基于文件的内容计算出一个Hash值,Git可以通过Hash值来索引文件,文件内容一致,Hash值必然一致,这也是为什么你只修改了文件名但内容并没有修改,Git却能感知到该文件已经换了一个文件名,而不是一个新增的文件,因为它基于内容计算出的Hash值是相同的。
Git原理
假设我们在新建的一个git项目,执行git add 1.txt
,就会看到文件.git/object
下多了个文件夹和文件,如图:
友情提示,查看目录树的命令watch -n 1 tree .git/
下面我们通过Hash值便可以索引到文件内容。
这个时候我们执行git commit -m 'first commit' 1.txt
,发现.git/object
下又产生几个新的文件夹,很显然黄色框部分就是新产生的。
再来看下其中里面存储的是什么内容
看到这里,你是不是明白了什么,原来6dc2这个Hash对应的文本里面包含了1.txt文件的Hash值
和文件名称
。
再往下看,你是不是明白了彻底懂了,原来这个98de这个Hash对应的文本里面包含了6dc2
和提交注释和作者信息
。
查看git log
发现,原来98de
就是commitID
也就是我们通过98de
这个commitID
找到6dc2
对应的tree,再通过tree里面保存的5711
这个Hash值就可以索引到文本快照。
如果再提交一个新的文件呢?就会生成新的commitID,我们查看最新的commitID,就会看到多了一个parent节点,很显然它指向上次提交的commitID
再看下master分支,保存也是这个最新的commitID,这样我们就知道,分支不过就是存储一个指针,指针的表现形式就是Hash值。
通过下面的图我们可以直观的感受下,他们之间的关联关系:
Git回溯版本直接就通过版本对应的CommitID,层层索引下去就能将当前版本下的所有文件给找出来,因为每个文件版本都有一个快照,并不像SVN一样需要对比文件差异,所以速度是非常快的。
最后
理解Git原理对于我们使用Git本身也会有非常大的好处,当然,笔者没有讲分支的合并,其实你懂了这篇文章,你去看Git分支合并原理相关的文章,你也会更加理解git merge
的几种合并方式以及git merge
和git rebase
本质上有什么不同,以及他们各自的实现原理。
最近一段时间都在看Netty源码,核心的逻辑过了下,有些地方设计得确实比较巧妙,不知道有没有机会给读者来一篇,不过很建议大家去把TCP/IP深入了解下,这样对于整个网络编程的理解也会更加深刻,因为分布式系统之间的通信都逃不过网络协议,这样对于你理解各种中间件应相关的协议,比如Redis,MQ的上层协议也会非常快。