git 基本原理
什么是git ?
Git是一个分布式版本控制系统(DVCS),它与集中式版本控制系统,如SVN有很大的不同。它们最大的差别是如何处理数据:
- SVN等大多数版本控制系统存储的数据是一系列记录项目文件变更的文件
- Git存储的数据则更像是一个微型文件系统的一系列快照,即快照流
如下图:
(svn存储的是文件的变更)
(git在每个版本下,都会生成一个文件快照)
这样的好处在于,可以轻松的回溯到任意一个版本,而不是比较每次变更的地方。
git是如何管理文件的
1. 使用哈希,哈希是一个系列的加密算法(Git 底层采用的是 SHA-1),各个不同的哈希算法虽然加密强度不同,但是有以下几个共同点:
- 不管输入数据的数据量有多大,使用同一个哈希算法,得到的加密结果长度固定(git使用的是40位)
- 哈希算法确定,输入数据确定,输出结果保证不变
- 哈希算法确定,输入数据有变化,输出结果一定有变化,而且通常变化很大
- 哈希算法不可逆
- 哈希算法中不区分英文大小写
2. Git 把数据看作是小型文件系统的一组快照。每次提交更新时 Git 都会对当前的全部文件制作一个快照并保存这个快照的索引。为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。所以 Git 的工作方式可以称之为快照流。
git是如何存储文件的
- git 是一个内容寻址系统
- git 的核心部分是一个简单的键对数据库
一个栗子
git init
复制代码
首先初始化一个git仓库
可以使用vim编辑器新建一个内容为 11111 的 1.txt
ls -al
复制代码
这时查看文件,可以发现文件列表不仅有1.txt 还有一个 .git的文件
brew install tree
brew install watch
复制代码
安装第一个代表用树形结构展示你的文件,另一个是监视文件的变化
watch -n .5 tree .git
复制代码
可以新开一个窗口,执行此命令,表示0.5秒监听一次文件的变化
可以看到git的树形结构
rm -r .git/hooks
复制代码
为了方便看的清楚删除多余的hooks,重点关注objects和refs
这时当我们执行 git add 1.txt 之后可以发现git树也相应的发生了变化,objects里面多了一个结构 (此时文件在暂存区,还未保存版本)
- f7 (c6dd01 …)
git cat-file -t f7c6
git cat-file -p f7c6
复制代码
当使用第一个命令的时候可以看出 文件类型为 blob ,使用第二个命令可以看出文件存储的内容
接下里执行 git commit -m “first commit” 命令后又多出来两条存储信息
- 87 (d37b26)
- fa (d18af3)
通过 git cat-file -t / -p 可看出 对应的文件类型及存储信息
- 87 (d37b26) 文件类型是一个tree , 存储的是文件类型为 blob ,hash值为f7 (c6dd01 …) ,以及1.txt 这个名称
可以看出blob文件只存储具体内容,而文件名称是存储在tree类型里面的,这就说明假如我们有10份内容一模一样的文件 ,其实git 只帮我们存储了一份 ,但是tree中是要记录10次的
- fa (d18af3) 文件类型是一个commit , 存储的是这一次commit 提交的信息 — “first commit” , 并且会存储一个tree ,指向87 (d37b26),这样通过每个版本的提交信息就能找到相关信息 ,可以任意版本回溯
可以在新建一个文件 例如 2.txt ,存储的内容与1.txt 保持一致,当执行git add 2.txt 时 其实git是不会再次存储此文件的
此时在新建一个3.txt文件,3.txt ,存储的内容与前两次不一样 ,这次执行git add . 时git就会存储它,并又多了一个存储信息
- 5a (cf9b10)
用git cat-file -p 5acf 命令可查看此条信息内存储的正是刚才3.txt的信息,现在的文件状态是 2.txt 和 3.txt 都只在暂存区,还未执行commit 操作,现在执行
git commit -m "second commit"
复制代码
这时又会多出两条存储信息
- ed (cb4edd)
- c4 (884dd6)
我们执行查看命令的时候发现
-
ed (cb4edd) 这条信息 存储的信息有
- 有个tree 指向c4 (884dd6)
- 有个parent 指向 fa (d18af3),上次commit的信息
- 第二次提交的信息
-
c4 (884dd6) 存储的是具体内容
小总结
- 以上我们可以发现git的几个对象:
- blob:存储的是数据
- tree: 一个指向文件名、内容和其他tree的指针
- commit: 作者信息、提交信息、以及指向parent上一次提交的指针
- 过程如下:
上述图片是现有的一个文件系统,现在对1.txt做出了改变,即下图
此时git在代码库也备份了一个最新的1.txt, 此时当执行git commit 命令的时候,tree就会生成一个新的索引指向这个文件 ,而且代码库的tree 也会根据暂存区的一个tree 的索引新生成一个索引。commit也会新生成一个commit,有一个指向新的tree的指针,和上一次parent的指针,即下图。
git 分支的变化
分支合并冲突:
- fast-forward:
当没有冲突就直接修改指针指向即可
- no-fast-forward
当有冲突的时候,会创建一个新指针,同时指向master和dev