本文章主要记录学习git底层原理时的一些知识点
文章参考
blob = array
tree = map
,(这里的blob实际上是一个哈希值)。永远存在一个root树。// 伪代码
type commit = struct {
parents: array
author: string
message: string
snapshot: tree
}
// 伪代码
type object = blob | tree | commit
objects = map
def store(object):
id = sha1(object)
objects[id] = object
def load(id):
return objects[id]
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)
git对一个目录的表示(最上层是root tree)
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
每次commit都会生成一个commit object,每个commit实际上会指向它之前的commit(parent),但每个commit的parent有可能有多个,参考之前的伪代码,这是因为当并行开发两个独立的功能,彼此独立,之后merge,新生成的commit就会指向多个parent。
经过多次提交后,commit组成的链就形成了,这个链上的每个commit都保留了那一次提交时的root tree的哈希值。如果某个文件改变了,那么那个文件对应的哈希值会改变,所以需要一个新的blob存储,同时存储它的tree由于需要保存指向它的哈希值,所以tree的内容也改变,那么tree作为object的哈希也改变。最后自底向上就体现在根目录的哈希发生改变。通过这样形成保存的snapshot。
branch实际上是一个可移动的指针,它指向的内容是当前commit的分支中最新的那一个,每次提交时,它都会移动到新提交的内容。 Git 将默认分支命名为 master。您当前加载的分支或提交由 HEAD 跟踪。 Head 始终指向您当时所在的提交或分支
可以把tag当成git history中的一个书签,它是一个不可移动的,指向一个确定的commit的书签。标签对于指定版本很有用。
Object的设计感觉有点像EXT文件系统,用指针(哈希)而不是直接存储blob的原因就是为了避免每次使用直接载入时需要载入全部的子节点的blob,但其实用不到而且内存开销太大。要用的时候再载入内存。