• Study Git - Data Model


    前言

    本文章主要记录学习git底层原理时的一些知识点
    文章参考


    Git的数据模型

    • blob: 压缩并保存所有文件内容的数据结构 blob = array
    • tree: 存储目录的结构,保存所属文件和tree(子目录)tree = map,(这里的blob实际上是一个哈希值)。永远存在一个root树。
    • commit: commit是指向tree的数据结构,每个提交都有自己的哈希值,它还会保存您的消息、时间戳和您的信息。当改变了追踪的文件blob1并且提交后,git会将更改文件后的内容存储在新的blob2中,这时commit会创建一个新的tree,tree也会通过一个新的hash值指向blob2,(但指向其它没有更改的blob的哈希是没有变化的,也就是依然指向原blob) 这样,通过两次commit,git repo中就保存了两个不同版本的的snapshot。
    // 伪代码
    type commit = struct {
        parents: array
        author: string
        message: string
        snapshot: tree
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • Objects : 一个Object是tree/blob/commit中的统一表达。很显然,如果要让tree中直接包含blob/tree本身或者一个副本,commit包含tree的全部内容(递归地继续包含blob和tree),磁盘开销会很大。所以实际上,当tree指向blob/tree时或者commit指向tree时,实际上是通过引用它们的hash值(SHA-1)实现的。也就是说一个commit指向的tree和一个tree中包含的内容实际上都是哈希值,这些哈希值指向blob或者tree,并且这个tree本身通过哈希可以被commit或者tree引用。所有Object通过SHA-1哈希寻址。
    // 伪代码
    type object = blob | tree | commit
    objects = map
    
    def store(object):
        id = sha1(object)
        objects[id] = object
    
    def load(id):
        return objects[id]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • references: 实际上是指向commit的指针,因为用哈希值去让人交互非常难记,所以用reference代替.例如,master reference 通常指向开发主分支中的最新提交。在 Git 中,“我们当前所在的位置”是一个名为“HEAD”的特殊引用。
    references = map
    
    def update_reference(name, id):
        references[name] = id
    
    def read_reference(name):
        return references[name]
    
    def load_reference(name_or_id):
        if name_or_id in references:
            return load(references[name_or_id])
        else:
            return load(name_or_id) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    git对一个目录的表示(最上层是root tree)
    在这里插入图片描述


    working directory | Stage OR Index

    working directory:工作目录。当用checkout切换commit时,工作目录也会切换到commit对应的snapshot

    This the git directory in which you are working. When you check-out a commit, your whole directory with all files is changed/replaced to match that commit (except ignored files)

    Stage OR Index:暂存区。在git commit保存到git repo前的缓存区,可以在这个缓存区中决定哪些改动过的文件需要被添加进repo中。试想一下如果没有Stage,那么每次提交就直接是工作目录到git repo的提交,有一些还没有完成的工作也会不得不添加进去。
    详情参考:The anatomy of a Git commit


    History

    每次commit都会生成一个commit object,每个commit实际上会指向它之前的commit(parent),但每个commit的parent有可能有多个,参考之前的伪代码,这是因为当并行开发两个独立的功能,彼此独立,之后merge,新生成的commit就会指向多个parent。
    在这里插入图片描述

    经过多次提交后,commit组成的链就形成了,这个链上的每个commit都保留了那一次提交时的root tree的哈希值。如果某个文件改变了,那么那个文件对应的哈希值会改变,所以需要一个新的blob存储,同时存储它的tree由于需要保存指向它的哈希值,所以tree的内容也改变,那么tree作为object的哈希也改变。最后自底向上就体现在根目录的哈希发生改变。通过这样形成保存的snapshot。

    在这里插入图片描述


    branch

    branch实际上是一个可移动的指针,它指向的内容是当前commit的分支中最新的那一个,每次提交时,它都会移动到新提交的内容。 Git 将默认分支命名为 master。您当前加载的分支或提交由 HEAD 跟踪。 Head 始终指向您当时所在的提交或分支
    在这里插入图片描述


    Tag

    可以把tag当成git history中的一个书签,它是一个不可移动的,指向一个确定的commit的书签。标签对于指定版本很有用。
    在这里插入图片描述


    一些个人理解

    Object的设计感觉有点像EXT文件系统,用指针(哈希)而不是直接存储blob的原因就是为了避免每次使用直接载入时需要载入全部的子节点的blob,但其实用不到而且内存开销太大。要用的时候再载入内存。

  • 相关阅读:
    如何配置多个ssh
    rpm2rpm 打包步骤
    dpdk 将arp和icmp数据包交给kni 实现ping
    软件课程设计--仓库管理系统
    Android组件通信——Service(二十七)
    Word 文档转换 PDF、图片
    wireshark——解密加密报文
    SpringCloud中的Eureka的集群配置
    PLC数据采集案例
    img标签如何将<svg></svg>数据渲染出来
  • 原文地址:https://blog.csdn.net/good_jiojio/article/details/127895935