头图
Git 拥有一个经过精心设计的模型,这使其能够支持版本控制所需的所有特性,例如维护历史记录、支持分支和促进协作。然而,通过自顶向下的方式(从命令行接口开始)学习 Git 可能会让人感到非常困惑。一旦出现问题,就只能将当前工作保存下来,然后重新复制一份工作,继续进行处理了。如果我们能够先对其底层的数据结构有所了解,在接触命令行接口时,就会更加得心应手。

快照

Git 将顶级目录中的文件和文件夹作为集合,并通过一系列快照来管理其历史记录。在 Git 的术语里,文件被称作 Blob对象(数据对象),也就是一组数据。目录则被称之为“树”,它将名字与 Blob 对象或树对象进行映射(使得目录中可以包含其他目录)。快照则是被追踪的最顶层的树,快照也被称为提交(commit)。

历史记录建模:关联快照

在 Git 中,历史记录是一个由快照组成的有向无环图。每个快照都有一系列的“父辈”,也就是其之前的一系列快照。注意,快照可能同时有多个“父辈”,例如,经过合并后的两条分支。

o <-- o <-- o <-- o <---- o
            ^            /
             \          v
              --- o <-- o

数据模型及其伪代码表示

通过伪代码的表示,能够更加清晰的了解 Git 的数据模型。

// 文件就是一组数据
type blob = array<byte>

// 一个包含文件和目录的目录
type tree = map<string, tree | blob>

// 每个提交都包含一个父辈,元数据和顶层树
type commit = struct {
    parent: array<commit>
    author: string
    message: string
    snapshot: tree
}

对象和内存寻址

Git 中的对象可以是 blob、树或提交:type object = blob | tree | commit,所有的对象都会通过 SHA-1 哈希进行寻址。

objects = map<string, object>

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

def load(id):
    return objects[id]

引用

给这些哈希值赋予人类可读的名字,也就是引用(reference)。引用是指向提交的指针。与对象不同的是,它是可变的(引用可以被更新,指向新的提交)。例如,master 引用通常会指向主分支的最新一次提交。

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 中,我们当前的位置有一个特殊的索引,它就是 “HEAD”。


Chris
17 声望0 粉丝