Git的常见命令虽然不多,但是,想要更加深刻理解这些命令,掌控每条命令的具体操作,了解Git的内部原理还是很有必要。
首先,我想说两个便于理解我后续解释的概念:
- 同一个事物有不同表现形式,但其实质是一个东西,它们可以相互转换。比如,原始文件和压缩后的文件。
- 两个事物有确定的映射关系,便可以从一个事物找到另一个事物。比如,通过文件名找文件内容,通过一段数据的hash值来找这段数据。
然后,从功能上来看,Git最核心的就是将文件夹存入仓库中,并且可以随时取出。此处的文件夹指文件夹下所有文件(文件内容、名称、权限等)和子文件夹。只不过存入Git仓库中的文件夹不可以再改变,相当于一个快照。接下来,我们来看Git库中的文件和文件夹的表现形式。
数据对象和树对象
对于Git而言,存入库中的文件和文件夹的另一种形式就是数据对象(blob)和树对象(tree)。数据对象就是将文件压缩,同时计算它的hash摘要,从而对它进行索引。树对象就是文件名、权限等和文件(hash摘要)之间的对应关系。
<<树对象和数据对象.vsdx>>
因此,每一个tree对象,都可以转换为一个文件夹。唯一的不同就是tree对象对应的文件夹保存后不再变化,工作空间的文件夹可以动态改变。
提交对象
Git每次commit一个版本,其实最主要就是保存了这样一个tree对象,下次便可以直接通过tree复原保存时的文件夹。除此之外,Git还保存了commit时的一些额外信息,比如作者、时间、备注等,由此,便设计出了第三种Git对象:提交对象(commit)。
<<提交对象.vsdx>>
暂存区
暂存区(index)其实就是一个临时tree对象。每次commit的时候可以直接把当前工作空间的文件夹直接转换为一个tree对象,并使commit对象指向它。但是,有时候我们的一些临时改动不想存入库中,因此,便加入一个暂存区。
所以,这里有三个文件夹,暂存区和仓库中是tree对象的形式存在。刚开始,三个文件夹内容一致,我们可以直接修改工作空间的文件夹。然后通过命令(git add/rm)可以将想要提交的修改合并到暂存区中的tree对象上。最后通过命令(git commit)才会将暂存区中的tree保存到库中,并生成一个commit对象指向这个tree对象。
<<暂存区.vsdx>>
引用
我们复原一个文件夹的时候,先要找到commit对象,然后通过commit对象找到tree对象进行复原。但是,在创建commit对象时,我们得到的索引方式是一串hash值,并不会指定一个名称。引用便是一个名称到hash值得映射关系,这样我们便可以直接从便于记忆的名称找到commit对象。
<<引用.vsdx>>
- 分支:可以随着提交(commit)发生改变的引用
- 标签:创建后不再改变的引用
- HEAD:当提交暂存区tree,新建一个提交commit时,这个commit对象的父指针指向HEAD所指向的commit对象。
- 远程引用:标记远程仓库分支的引用。当和远程仓库同步时(git fetch/push)时,会同时改变。
Pro Git v2 中文版 - v1.0