Git的存储原理

目录

  • [Git 设计原理](#Git 设计原理)
    • [Git vs SVN](#Git vs SVN)
    • [Git 存储模型](#Git 存储模型)
      • [.git 目录结构](#.git 目录结构)
      • [Git 基本数据对象](#Git 基本数据对象)
      • [Git 包文件](#Git 包文件)
      • [Git 引用](#Git 引用)

Git 设计原理

概括的讲,Git 就是一个基于快照的内容寻址文件系统。 往下慢慢看。

Git vs SVN

Git 出现前,主流版本控制系统(SVN...)一般为基于增量(delta-based)的系统,如下图:

Git 则是基于快照(snapshot),即针对每一个被修改的文件生成一个快照,没被修改的则不再重新生成快照,如下图:

直觉上讲,似乎基于增量的方式要更好些?

毕竟针对被修改的文件,Git 生成的是完全的快照,而其他系统只是生成增量文件。没错,但是当需要回滚版本或者比对多个版本间的差异时,Git 只需要取出对应版本的快照文件进行对比即可,而基于增量的系统则需要从头开始一步步应用增量文件来回溯,Git 的速度优势就很明显了。

Git 存储模型

.git 目录结构

当用git init 或者 git clone 获取一个 git 仓库时,可以发现目录下有一个隐藏目录。git,它的基本结构类似如下:

Shell 复制代码
├── COMMIT_EDITMSG 仓库最后一次commit的message
├── FETCH_HEAD  每个分支的最后一次commit的SHA1值
├── HEAD 记录了HEAD指针的指向位置
├── ORIG_HEAD 针对某些危险操作,该指针记录了上一次安全版本的HEAD指针的位置,方便回退
├── config git的相关配置
├── index 暂存区,索引文件
├── packed-refs 已经压缩的分支,记录了每个分支的最后一次commit的SHA1
├── logs/ 操作日志,包括本地远程的
├── objects/ 对象存储文件夹
|   ├── ... 文件夹名称根据object的SHA1值的前2个字符确定
|   ├── ...
|   ├── info/
|   ├── pack/ 压缩后的数据
└── refs/ 记录本地和远程的最后一次commit的SHA1值
    ├── heads/ 分支引用
    ├── remotes/ 远程地址
    └── tags/ 标签引用

这个目录下包含了 Git 所有信息,且都是用文件的形式存储,所以说 Git 是一个文件系统。

Git 基本数据对象

  • blob(二进制大对象):也就是前面说的基于快照存储的文件
  • tree:目录,代表了 blob 对象的集合
  • commit:提交,包含了 blob、tree 的集合
  • tag:标签对象(指 annotation 标签),还有一种轻量标签不记录创建标签人等额外信息,不需要再单独创建标签对象

上述 4 种数据对象均存储在。git/object/目录下,git 会对每一种数据对象计算哈希值来确定具体的存储路径,下面来举个例子。

Shell 复制代码
> echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4 
//40位的SHA-1哈希值,前2位位目录名,其他38位为文件名,存储路径即.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
> git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
 test content 
> git cat-file -p master^{tree} // 输出master最新提交包含内容
100644 blob a906cb2a4a904a152e80877d4088654daad0c859      README  
100644 blob 8f94139338f9404f26296befa88755fc2598c289      Rakefile  
040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0      lib
// 包含了2个文件的修改和1个目录的修改
> git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0
100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b      simplegit.rb    

git hash-object 命令可以用于计算文件的哈希值

-w 表示把将对象写入到 git 数据库中

--stdin 表示从标准输入读取内容

git cat-file 命令可以根据传入哈希值取出 git 存储的对象

-p 自动判断内容的类型

一次提交的数据结构可以用下图来概括:

Git 包文件

可能有的小伙伴通过上述方式在自己项目中尝试时,发现在。git/objects/下找不到对应文件,这是什么原因呢?

可能真的不是操作出了问题,而是 Git 进行了压缩操作。

Git 最初存储对象时使用的时"松散(loose)"对象格式,即保存在。git/objects/下。

但是,Git 会时不时(或者当你手动执行git gc 命令后)地将这些对象打包成一个称为"包文件(packfile)"的二进制文件(存储在。git/objects/pack),以节省空间和提高效率。

Git 引用

引用类似于指针,除了 HEAD 存储在。git/HEAD 以外,其他指针存储在。git/refs 目录下

  • 分支
  • HEAD:一种特殊的指针,用于指向目前所在的 commit,。git/HEAD 文件里存储的就是引用的 commit 的哈希值
  • 标签(轻量标签)

可以看出,所谓的引用只是一个记录了 commit 哈希值的文件,非常的轻量,这也是为什么分支/标签的创建、删除速度能这么快的原因。

相关推荐
iofomo12 天前
【CoCollider】让系统和应用适配如此简单
开发工具·移动端
不能只会打代码18 天前
IDE(集成开发环境)
ide·开发工具
Stark、21 天前
QT界面开发--我的第一个windows窗体【菜单栏、工具栏、状态栏、铆接部件、文本编辑器、按钮、主界面】
开发语言·c++·后端·qt·开发工具·1024程序节
Stark、1 个月前
输入输出--I/O流【C++提升】
c语言·开发语言·c++·后端·开发工具
iofomo1 个月前
【Wing】背后的插件们
开发工具
风的姿态2 个月前
我是如何开发一款支持IDEA、PyCharm、Android Sutdio 等JB全家桶的摸鱼插件的
程序员·开发工具·jetbrains·idea插件
iofomo2 个月前
【Abyss】Android 平台应用级系统调用拦截框架
android·开发工具·移动端
高兴的才哥2 个月前
kubevpn 教程
kubernetes·go·开发工具·telepresence·bridge to k8s
yinmaisoft2 个月前
谁破防了?低代码能代替程序员吗
低代码·开发工具·开发效率
七夜zippoe2 个月前
哪个编程工具让你的工作效率翻倍?
开发工具