洋仔的博客 洋仔的博客
首页
  • 个人心法总结

    • 价值心法
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • iOS基础知识
  • 前端
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 投资体系
  • 毛选
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

洋仔

奋斗的小青年
首页
  • 个人心法总结

    • 价值心法
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • iOS基础知识
  • 前端
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 投资体系
  • 毛选
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术文档

  • GitHub技巧

  • Nodejs

  • 博客搭建

  • iOS基础知识

    • iOS底层相关

    • Runloop系列

    • Runtime系列

    • 内存管理系列

    • Block系列

    • 线程系列

    • KVC跟KVO系列以及通知中心

    • UI系列

    • 离屏渲染系列

    • 组件化系列跟架构

    • OC跟webview交互系列

    • 持久化系列

    • APP编译系列

    • APP性能优化系列

    • cocoapods系列

    • swift系列

    • Git系列

      • git原理
        • git 原理
        • Git 到底是如何工作呢 ?
          • 对象数据库
          • 对象模型
          • Blob Object
          • Tree Object
          • Commit Object
          • 每个commit都是一个git仓库的快照
      • git pull 和 git fetch的区别
      • git merge和git rebase的区别
    • 网络相关

    • 三方库系列

    • 系统原理

    • 总结系列

    • 算法系列

    • 数据结构系列

  • 前端

  • 技术
  • iOS基础知识
  • Git系列
洋仔
2023-09-21
目录

git原理

# git 原理

Git 是一个内容寻址文件系统。这意味着,Git 的核心部分是一个简单的键值对数据库(key-value data store)。 你可以向该数据库插入任意类型的内容,它会返回一个键值,通过该键值可以在任意时刻再次检索该内容。而这些数据全部是存储在objects目录里。key是一个hash, hash前两个字符用于命名子目录,余下的 38 个字符则用作文件名。如果了解tree 树的朋友应该会想明白之所以这样处理是因为检索优化策略,提高文件系统效率(如果把太多的文件放入同一个目录中,一些文件系统会变慢)。而这个hash的内容(即hash对应的Value)有四种对象类型,commit(提交),tree(目录树),,blob(块),tag(标签)。

从根本上来讲,Git是一个内容寻址的文件系统,其次才是一个版本控制系统。记住这点,对于理解Git的内部原理及其重要。所谓“内容寻址的文件系统”,意思是根据文件内容的hash码来定位文件。这就意味着同样内容的文件,在这个文件系统中会指向同一个位置,不会重复存储。

# Git 到底是如何工作呢 ?

在 Git 的设计中,有两种命令:分别是底层命令(Plumbing commands)和高层命令(Porcelain commands)。一开始,Linus 就设计了这些给开源社区黑客使用的符合 Unix KISS 原则的命令,因为黑客们本身就是动手高手,水管坏了就撸起袖子去修理,因此这些命令被称为 plumbing commands. 后来接手 Git 的 Junio Hamano 觉得这些命令对于普通的用户可不太友好,因此在此之上,封装了更易于使用、接口更精美的高层命令,也就是我们今天每天使用的 git add, git commit 之类。

我们知道最简单的git flow主要有三步

  • 在工作目录中修改文件。
  • 暂存文件,将文件的快照放入暂存区域。
  • 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。

在我们看这三个命令到底做了什么之前,先来了解一下几个概念

以为git是一个分布式版本控制系统,因此git的操作大部分都是在本地,除非明确说明下面的原理或者命令都是在本地操作。

每个git项目的根目录下都有一个.git目录,它是git默默进行版本控制时读写的“数据库”。有几个概念需要提一下:

--工作区:代码所在的目录,就是自己电脑中能够看到的目录

--暂存区:英文叫stage或者index。一般存放在.git/index文件中,所以我们把暂存区也叫作索引index

--版本库:工作区有一个隐藏的.git,这个不算工作区,而是Git的本地版本库,你的所有版本信息都会存在这里

# 对象数据库

Git 最核心、最底层 的部分则是其所实现的一套 对象数据库(Object Database),其本质是一个基于 Key-Value 的内容寻址文件系统(Content-addressable File System)。笔者认为其设计理念与传统的文件系统的设计理念极其相似,为了方便理解和对照,因此在下文中,我们将以 Git 文件系统 作为简称。

Git 文件系统中存储了所有文件的所有历史快照,通过索引不同的历史快照,Git 才能够实现版本控制。下面,我们来介绍一下 Git 文件系统。

# 对象模型

通过对比 Git 文件系统和 Ext 文件系统,我们基本了解了 Git 是如何存储和索引文件及目录的。接下来,我们来深入了解 Git 的对象模型,即数据存储的基本单元及类型。

Git 对象模型主要包括以下 4 种对象

  • 二进制对象(Blob Object)

  • 树对象(Tree Object)

  • 提交对象(Commit Object)

  • 标签对象(Tag Object) 所有对象均存储在 .git/objects/ 目录下,并采用相同格式进行表示,其可以分为两部分:

  • 头部信息:类型 + 空格 + 内容字节数 + \0

  • 存储内容 Git 使用两部分内容的 40 位 SHA-1 值(前 2 为作为子目录,后 38 位作为文件名)作为快照文件的唯一标识,并对它们进行 zlib 压缩,然后将压缩后的结果作为快照文件的实际内容进行存储。

下面,我们来主要介绍一下其中前三种对象类型。

# Blob Object

Block Object 用于存储普通文件的内容数据,其头部信息为 "blob" + 空格 + 内容字节数 + \0,存储内容为对应文件的内容快照。

# Tree Object

Tree Object 用于存储目录文件的内容数据,其头部信息为 "tree" + 空格 + 内容字节数 + \0,存储内容为 一个或多个树对象记录(Tree Entry)。

其中,树对象记录的结构(Git v2.0.0)为:文件模式 + 空格 + 树对象记录的字节数 + 文件路径 + \0 + SHA-1。

注意,当我们执行 git add(进入暂存区)时,Git 会为暂存文件创建 Blob Object,为暂存目录创建 Tree Object ,结合未修改文件和目录的 Object,建立一个整体的索引关系,从而形成一个版本快照。

# Commit Object

Tree Object 和 Blob Object 用于表示版本快照,Commit Object 则不同,它 用于表示版本索引和版本关系。

此外,Tree Object 和 Blob Object 的 SHA-1 值是根据内容计算得到的,只要内容相同,SHA-1 值相同;而 Commit Object 会结合内容、时间、作者等数据,因此 SHA-1 值很难出现冲突。

Commit Object 的头部信息为 "commit" + 空格 + 内容字节数 + \0,存储内容包含多个部分(Git v2.0.0),具体如下图所示。

  • 对应的根 Tree Object 对应的 SHA-1

  • 一个或多个父级 Commit Object 对应的 SHA-1。当进行分支合并时就会出现多个父级 Commit Object。

  • 提交相关内容,包括:作者信息、提交者信息、编码、提交描述等

# 每个commit都是一个git仓库的快照

要理清objects“筐”中各object间的关系,就必须要把握住一个关键概念:“每个commit都是git仓库的一个快照” – 以一个commit为入口,我们能将当时objects下面的所有object联系在一起。因此,上面5个object中的那个commit对象就是我们分析各object关系的入口。我们根据上述5个object的内容将这5个object的关系组织为下面这幅示意图:

通过上图我们看到:

注意

  • commit是对象关系图的入口;

  • tree对象用于描述目录结构,每个目录节点都会用一个tree对象表示。目录间、目录文件间的层次关系会在tree对象的内容中体现;

  • 每个commit都会有一个root tree对象;

  • blob对象为tree的叶子节点,它的内容即为文件的内容。

  • 和subversion这样的集中式版本管理工具最大的不同就是每个程序员节点都是git仓库,拥有全部开发、协作所需的全部信息,完全可以脱离“中心节点”;

  • 如果说git聚焦于数据平面的功能,那么github则是一个基于git网络协作的控制平面的实现;

  • objects是个筐,什么都往里面装。git仓库的核心数据都存在.git/objects下面,主要类型包括:blob、tree和commit;

  • 每个commit都是一个git仓库的快照,记住commit对象是分析对象关系的入口;

  • git是基于数据内容的hash值做等值判定的,object是不可变的,默克尔树(Merkle Tree)用来快速判断变化。

  • branch和tag因为是“指针”,因此创建、销毁和切换都非常轻量。

每个commit对象都是一个git仓库的快照,切换到(git checkout xxx)某个branch或tag,就是将本地工作拷贝切换到commit对象所代表的仓库快照的状态。当然也会将commit对象组成的单向链表的head指向该commit对象,这个head即.git/HEAD文件的内容。

编辑 (opens new window)
上次更新: 2024/10/23, 23:26:17
swiftUI面试
git pull 和 git fetch的区别

← swiftUI面试 git pull 和 git fetch的区别→

最近更新
01
数组
10-25
02
数组双指针系列之对撞指针
10-25
03
数组双指针系列之快慢指针
10-25
更多文章>
Theme by Vdoing | Copyright © 2019-2024 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式