自底向上理解git

git已经很是普及,几乎是程序员的标配。在熟练使用git命令的同时,你是否对底层的实现机制有所好奇?是否想更深刻的了解git原理?本文将从较底层的视角逐步揭开git的秘密。
文章主要参考git book 第十章,根据笔者的理解提炼,简化没必要要的细节,但愿能帮助你们更好的理解git。linux

解决文件存储(blob)

版本控制系统本质就是一个特殊的文件系统,可以对不一样的文件、不一样的版本进行存储和恢复。git最基本的存储是一个key-value的数据库。git提供了一些命令来操做该数据库,hash-object命令可以将工做区文件插入数据库,返回一个hash,cat-file命令可以根据hash查询内容。数据文件存储于.git/objects目录。
下面的例子建立一个test.txt文件并将其内容存储到git数据库。
clipboard.pnggit

当文件内容改变时,从新存储便可,此时会返回新的hash。只要记录了的hash和文件名,就能进行简单的版本管理了。下面的例子演示了一个简单的版本恢复:首先修改test.txt,并将新值存入数据库,而后又经过hash将工做区的文件内容回退到上一个版本。
clipboard.png程序员

这种最基本的存储单元git称为blob。数据库

支持文件名和集合(tree)

普通blob没法支持文件名和集合的存储,git引入tree来解决此问题。tree相似目录的概念,包含一系列blob/tree的hash和文件名。
建立tree的方法是,先用update-index命令将内容添加到暂存区,而后经过write-tree命令生成tree。服务器

以下,先添加一项内容的暂存区。
clipboard.png
ps:100644是linux文件类型常量。ssh

以下,再添加一项内容到暂存区(update-index命令可直接传入文件名,此时git会作两个步骤,读取文件内容存至数据库和添加至暂存区)。
clipboard.pngspa

此时暂存区有两个项目,经过write-tree命令将暂存区内容生成tree,返回值一个hash。能够看到tree也是以一个blob来存储的。
clipboard.png设计

支持备注信息(commit-tree)

前面已经解决了存储的问题(包括内容和文件名),但想添加一些额外的备注信息(好比做者、时间、说明等)还不行,commit-tree可以解决此问题。
如下代码对一个tree添加commit。commit-tree主要包含三部分:指向另外一个tree的hash、做者信息、commit message。
clipboard.png版本控制

commit-tree还有另一个字段parent,指向前一个commit-tree的hash。如下代码建立了一个新的commit-tree并指向前者。
clipboard.pngip

有了commit-tree以后,不只能添加备注信息,还能造成一条commit链,经过这条链,可以更加方便地进行版本的切换和回退。

分支的本质(branch)

理论上commit-tree可以知足任意复杂的版本管理,只不过你须要记住一些关键节点的commit hash。hash对人是不友好的,git引入分支来记录关键节点的commit hash。分支就是一个普通的文本文件,文件名即分支名,内容即commit hash。
如下代码建立了一个master分支。
clipboard.png

经过git log就能查看分支对应的commit链。
clipboard.png

分支存储于.git/refs目录,本地分支位于heads目录,远程分支位于remotes目录,.git/HEAD指向当前所在的本地分支。从这里能够看到git分支模型确实是很是轻量,就是一个简单的文本文件而已。

远程同步(remotes)

仅仅本地进行版本管理还不够,要和远程仓库通讯才能多人协做。git的远程仓库设计也很简单,本地和远程互为拷贝。在同步信息时,只需同步.git/objects和.git/refs/remotes目录的文件便可。数据传输可采用http/ssh协议,服务器交互地址配置于.git/config。

总结

git命令很是多,概念也相对较多。但git的底层逻辑设计是很是简单的。一个好的设计必定是简单的,一个好的设计必定是具备美感的。

相关文章
相关标签/搜索