Git工作原理
一、简介
Git是目前世界上最先进的分布式版本控制系统,流程如下:
Workspace
:工作区Index / Stage
:暂存区Repository
:仓库区(或本地仓库)Remote
:远程仓库
二、SVN与Git的最主要的核心区别
区别1 (重要)
- 1.SVN是集中式版本控制系统。版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,只有一台服务器来维护和控制代码,所以首先要从中央服务器那里得到最新的版本,干完活后需要把自己做完的活推送到中央服务器,
- 2.Git是分布式版本控制系统。没有中央服务器,每一台主机都当成一台服务器
区别2
-
Git和其他版本控制系统的主要差别在于,Git 只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异。Git 并不保存这些前后变化的差异数据。实际上,Git 更像是把变化的文件作快照后,记录在一个微型的文件系统中。每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件作一快照,然后保存一个指向这次快照 的索引。为提高性能,若文件没有变化,Git不会再次保存,而只对上次保存的快照作一链接。
-
近乎所有操作都是本地执行,在 Git 中的绝大多数操作都只需要访问本地文件和资源,不用连网。但如果用 CVCS 的话,差不多所有操作都需要连接网络。因为 Git 在本地磁盘上就保存着所有当前项目的历史更新,所以处理起来速度飞快。
二、Git目录
- hooks:
钩子目录,存放执行指定git命令前或者后触发的脚本,可以看到默认会有几个sample文件,如果要开启某个钩子脚本,就把脚本文件名的后缀simple去掉就可以,
这些钩子会在特定的时机被触发执行,比如post-commit在整个提交过程完成后执行,
可以用于发送提交通知等,另外也有服务端钩子,在推送前或者后执行,
比如post-receive在推送结束后会被执行,可以用于通知打包平台启动打包任务
- info:保存git的相关信息
- objects:存放真实数据,以Git对象形式存放
- refs
- heads:存放所有本地分支最新的commit哈希值
- stash:存放stash对应的哈希值
- tags:存放tags相关的
- config:配置文件
- HEAD:当前分支,并不存放SHA1值,类似/refs/heads/master,这个指向的文件里会有最新commit的SHA1值
- index:二进制文件,暂存区
三、Git对象存储
数据对象、树对象、提交对象都存储在.git/objects
目录下。Git对象在存储前,Git对象的40位hash值分为两部分:头两位作为文件夹,后38位作为对象文件名。
- Git对象的40位hash值是使用sha1算法计算,
- 使用zlib的deflate算法压缩Git对象存储到 后38位作为对象生成的文件名中
- Git对象存储的算法步骤(了解)
1.计算`content`长度,构造`header`;
2.将`header`添加到`content`前面,构造Git对象;
3.使用sha1算法计算Git对象的40位hash码;
4.使用zlib的deflate算法压缩Git对象;
5.将压缩后的Git对象存储到`.git/objects/hash[0, 2]/hash[2, 40]`路径下;
- 为什么Git要这么设计目录结构,而不直接用Git对象的40位hash作为文件名?原因是有两点: 1.有些文件系统对目录下的文件数量有限制。例如,FAT32限制单目录下的最大文件数量是65535个,如果使用U盘拷贝Git文件就可能出现问题。 2.有些文件系统访问文件是一个线性查找的过程,目录下的文件越多,访问越慢。
三、Git对象
- Git是根据文件内容的hash码来定位文件,这意味着同样内容的文件,在这个文件系统中会指向同一个位置,不会重复存储。
- Git对象包含三种:数据对象、树对象、提交对象。Git文件系统的设计思路与linux文件系统相似,即将文件的内容与文件的属性分开存储,文件内容以“装满字节的袋子”存储在文件系统中,文件名、所有者、权限等文件属性信息则另外开辟区域进行存储。在Git中,数据对象相当于文件内容,树对象相当于文件目录树,提交对象则是对文件系统的快照。
1. 数据对象
数据对象是文件的内容,不包括文件名、权限等信息。Git会根据文件内容计算出一个hash值,以hash值作为文件索引存储在Git文件系统中。由于相同的文件内容的hash值是一样的,因此Git将同样内容的文件只会存储一次。git hash-object
可以用来计算文件内容的hash值,并将生成的数据对象存储到Git文件系统中,
1.计算出一个hash值 ,以hash值作为文件索引存储在Git文件系统中
git hash-object -w ./gittest.txt
------>6ef4b68add7afe38c244974d1e08d9a40d442528
// -w 表示将数据对象写入到Git文件系统中,不加这个选项,那么只计算文件的hash值而不写入
2.Git对象的读取
`-p`表示查看Git对象的内容,`-t`表示查看Git对象的类型
git cat-file -p 6ef4b68add7afe38c244974d1e08d9a40d442528
数据对象只是解决了文件内容存储的问题,而文件名的存储则需要通过树对象来解决
2. 树对象
树对象是文件目录树,记录了文件获取目录的名称、类型、模式信息。使用git update-index
可以为数据对象指定名称和模式,然后使用git write-tree
将树对象写入到Git文件系统中
//写入 index区 或者 称为暂存区
1. git update-index --add --cacheinfo 100644 6ef4b68add7afe38c244974d1e08d9a40d442528 gittest.txt
然后写入 git系统 必须得加上,否则只在 暂存区
2. git write-tree
//树对象仍然可以使用`git cat-file`查看
\
树对象解决了文件名的问题,而且,由于我们是分阶段提交树对象的,树对象可以看做是开发阶段源代码目录树的一次次快照,因此我们可以是用树对象作为源代码版本管理. 在源代码版本控制中,我们还需要知道谁提交了代码、什么时候提交的、提交的说明信息等,这就需要提交对象。
3. 提交对象
提交对象是用来保存提交的作者、时间、说明这些信息的,可以使用git commit-tree
来将提交对象写入到Git文件系统中
总结:Git中的数据对象解决了数据存储的问题,树对象解决了文件名存储问题,提交对象解决了提交信息的存储问题。从Git设计中可以看出,Linus对一个源代码版本控制系统做了很好的抽象和解耦,每种对象解决的问题都很明确,相比于使用一种数据结构,无疑更灵活和更易维护。
四、Git引用
Git引用相当于给40位hash值取一个别名,便于识别和读取。Git引用对象都存储在.git/refs
目录下,该目录下有3个子文件夹heads
、tags
和remotes
,分别对应于HEAD引用、标签引用和远程引用。
1. HEAD引用
- HEAD引用是用来指向每个分支的最后一次提交对象,这样切换到一个分支之后,才能知道分支的“尾巴”在哪里。
- HEAD引用存储在
.git/refs/heads
目录下,有多少个分支,就有相应的同名HEAD引用对象 - HEAD引用的内容就是提交对象的hash值 2. 标签引用
- 就是给Git对象打标签,标签引用都存储在
.git/refs/tags
里面 3. 远程引用 - 远程引用也是Git引用对象,所以理论上也可以使用
git update-ref
来手动维护。但是,我们需要先把代码与远程仓库进行同步,在远程仓库中找到对应分支的HEAD,然后使用git update-ref
进行更新,过程比较麻烦。而我们在执行git pull
或git push
这样的高层命令的时候,远程引用会自动更新 总结: - 三种Git引用都已分析完毕。总的来说,三种Git引用都统一存储到
.git/refs
目录下,Git引用中的内容都是40位的hash值,指向某个Git对象,这个对象可以是任意的Git对象,可以是数据对象、树对象、提交对象。三种Git引用都可以使用git update-ref
来手动维护。 - 三种Git引用对象所不同的是,分别存储于
.git/refs/heads
、.git/refs/tags
、.git/refs/remotes
,存储的文件夹不同,赋予了引用对象不同的功能。HEAD引用用来记录本地分支的最后一次提交,标签引用用来给任意Git对象打标签,远程引用正式用来记录远程分支的最后一次提交。
转载自:https://juejin.cn/post/7015014883322232869