Study Git - Data Model

4 篇文章 0 订阅

前言

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


Git的数据模型

  • blob: 压缩并保存所有文件内容的数据结构 blob = array<byte>
  • tree: 存储目录的结构,保存所属文件和tree(子目录)tree = map<string, tree | blob>,(这里的blob实际上是一个哈希值)。永远存在一个root树。
  • commit: commit是指向tree的数据结构,每个提交都有自己的哈希值,它还会保存您的消息、时间戳和您的信息。当改变了追踪的文件blob1并且提交后,git会将更改文件后的内容存储在新的blob2中,这时commit会创建一个新的tree,tree也会通过一个新的hash值指向blob2,(但指向其它没有更改的blob的哈希是没有变化的,也就是依然指向原blob) 这样,通过两次commit,git repo中就保存了两个不同版本的的snapshot。
// 伪代码
type commit = struct {
    parents: array<commit>
    author: string
    message: string
    snapshot: tree
}
  • 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<string, object>

def store(object):
    id = sha1(object)
    objects[id] = object

def load(id):
    return objects[id]
  • references: 实际上是指向commit的指针,因为用哈希值去让人交互非常难记,所以用reference代替.例如,master reference 通常指向开发主分支中的最新提交。在 Git 中,“我们当前所在的位置”是一个名为“HEAD”的特殊引用。
references = map<string, string>

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) 

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,但其实用不到而且内存开销太大。要用的时候再载入内存。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值