写在前面

在如今的时代,信任没有人还不会用Git了吧。尽管Git仅仅用两周时刻开发出来的,但因其优秀的功用、简洁的操作,现已成为了运用最广泛的版别办理系统。

在与作业的过程中发现,咱们对Git的运用等级,大致分为3种。

①,把Git当成svn运用,有分支的概念,但无法熟练运用
②,能够熟练运用git,能够应付大部分的Git操作,但无法灵活的操作,产生操作失误时不知道怎么办
③,根本能够处理项目上所有的git问题,熟练运用一些冷门但实用的指令(reflogrebasecherry-pick等)

对于一个老练的程序员来讲,假如你还是①的等级,阐明你需求学一下Git操作了,否则被商场筛选仅仅时刻问题了。
假如你到达了②的程度,Git就不再限制你的开发功率,但无法承担起核心的职责。
而假如到达③的程度的话就足以独立自主了,能够自己去设计一套项目的开发流程、分支结构。
可是,真实了解Git的运行、存储原理的人其实十分少,而了解这些会帮助咱们愈加高效优雅的运用Git。
本篇内容并不适合等级①的开发者阅览,主张先学习一下Git的基础知识后再探究其原理。

原因

在一个适(huo)合(gan)学(wan)习(le)的作业日,沉浸于Git操作的我忽然想到,曾经学习Git时学到,Git的存储方法不同于SVN的Diff存储,而是将文件以SNAPSHOT(快照)的方法存储,以空间换时刻的方法进步功率。
而在实际运用过程中,并没有发现项目的Git占用多大空间,这就需求进一步了解一下Git的原理了。

Git的简略解说

Git的核心功用涉及到两块知识,第一个是Git的三个分区(作业区暂存区仓库区),第二个是Git的快照存储三种方法(BlobTreeCommit)。
对于Git的分区信任咱们应该都了解一些,假如不清楚也不要着急,下面会提到。这儿为了方便咱们了解先阐明一下Git的存储方法。

实践展示-存储单元

从这儿开始,我会新建一个空文件夹来阐明Git的相关存储方法。

git init

初始化Git后,咱们会看到文件夹根目录下创立.git文件夹,而.git\objects便是Git的存储目录

Git:从实践到原理,再到实践

因为现在是个空文件夹,里面没有文件。.git\objects目录下除了Git生成的文件夹,没有其他内容
现在咱们新建一个文件a.txt,没有履行git add的话,.git\objects目录下依然没有内容

git add a.txt

履行后,.git\objects目录中生成了94文件夹和其目录下的ebaf900161394059478fd88aec30e59092a1d7文件

Git:从实践到原理,再到实践
代表着a.txt现已正式交给Git来管控。
94ebaf900161394059478fd88aec30e59092a1d7拼接后的94ebaf900161394059478fd88aec30e59092a1d7为这个文件的hashId,这个文件便是a.txt的快照。Git中称其为Blob(后续会详细阐明)。
ps. git cat-file是Git提供的检查快照文件的指令,了解即可,感爱好可自行查找。
Git:从实践到原理,再到实践

这时咱们提交一下试试

git commit -m "commit A"

.git\objects目录中又新生成两个快照

Git:从实践到原理,再到实践

咱们经过git cat-file指令来检查一下
Git:从实践到原理,再到实践
在Commit时,创立的TreeCommit的快照,而且构建了三个快照之间的联系(图中只写了hashId的前两位)
Git:从实践到原理,再到实践

总结一下,Git在履行git add时创立相应文件的Blob快照,在履行git commit时,依据暂存区Blob快照创立相应的TreeCommit快照。完结存储。

实践展示-提交经历

上面介绍了Git的Commit的存储依靠联系,接下来咱们再新建一些文件提交,添加一些文件结构和提交经历。
废话不多说,直接上图

Git:从实践到原理,再到实践

提交后看一下.git\objects目录,添加了四个快照
Git:从实践到原理,再到实践

为了节约篇幅我就直接上联系图了
Git:从实践到原理,再到实践

依据联系图,咱们能够经过commit B的hashId,获取到这个版别的所有文件。
到这儿咱们就能够了解到Git的存储机制了,假如不太清楚的话,主张停下来考虑一下。
ps. 引申一点考虑,这也解释了咱们在提交代码时,只需求提交文件的原因,文件的目录是由Git经过创立Tree来创立的。
ps. 再记载一下一些小知识,假如两个文件的内容是相同,仅仅目录不相同。Git只会创立一个Blob目标。这可能是Git用来优化存储空间的一点小技巧。假如感爱好能够自己试一下。

回头看一下Git的三个分区

读到这儿咱们应该对Git的存储方法有了大概的了解,咱们回头来收拾一下Git的文件分区

Git:从实践到原理,再到实践

结合Git的存储方法能够得知,Git并没有实际文件分区,而是经过散布存储文件,完成了分区的概念。
ps. 再略微扩展一下,在add之后创立的Blob目标,即使将文件移除暂存区,Blob目标依然存在,假如有该文件的hashId的话,随时能够检查文件内容

Git指针(HEAD、branch、remote等,总称Reference)

假如了解了上面的内容,可能会发现一个问题。上面说Git都是经过hashId来存储、操作,但咱们平常都是用branch来操作的。聪明小伙伴现已猜到了,其实这些branchremote仅仅一些指向某个Commit hashId的指针。
咱们翻开自己Git项目的.git\refs目录来看一下,这儿记录着指向的Commit hashId。
HEAD稍有不同,HEAD记录的是当时地点的分支,能够看一下.git\HEAD文件。

从头审视一下Git常用指令

现在,以咱们把握的知识,再来从头审视一下咱们常用的Git指令,更清晰的知道Git。

git merge

咱们知道,Git并不办理每个版别的改变,只办理每个版别的文件。而merge则是将两个分支的文件进行合并,从头提交一个Commit,而这个Commit会有两个parent指向,这也便是咱们平常看到的经历

Git:从实践到原理,再到实践

git reset

其实reset应该分开来讲,因为hardmixedsoft的差异,导致指令效果的不同。
但假如了解了其中本质的话,其他信任咱们能够自己了解。
简略来说,reset仅仅移动了当时branch的commit指向。
比方履行git reset --mixed commit2hash的话,本地文件并没有改变,仅仅改变了master分支的指向,如图所示

Git:从实践到原理,再到实践

这儿略微的扩展一下,因为Git不会删除快照文件的特性,咱们能够做一些古怪但有效的操作。

比方咱们不小心履行了git reset --hard commit2hash,导致commit 3代码的丢掉。

Git:从实践到原理,再到实践

这时咱们只需能够拿到Commit 3的hashId,然后履行git reset --hard commit3hash就能够复原了。
假如拿不到也别担心,运用reflog就能够查到

再比方咱们假如不小心merge错分支的话,只需在log中找到merge前的commit hashId,履行git reset --hard commitHash就能够康复到之前的状况。

需求提示一下,尽管Git不会删除快照文件,可是作业区的修正不在Git办理范围内,所以运用 reset –hard 时,需求稳重

git checkout

checkout的功用同样很多,这儿简略介绍一下常用的操作,其他功用咱们能够自己考虑一下。
履行git checkout branch1时,首先会修正.git\HEAD中的分支指向,然后抽出该分支对应的Commit下指向的文件,替换到咱们的目录下。
作业区假如有修正和其快照文件有抵触时,无法抽出。

而履行git checkout file1时,依据当时Commit中的Tree指向,找到该文件对应的Blob Hash,然后替换至本地目录下。

因为篇幅有限(懒),就写这么多吧。假如有爱好的话,主张自己研究一下感爱好的指令,会比其他人解说收获更多。

总结

在学习Git原理时,我不只一次的感叹Git的设计,也希望咱们在学习的过程中不仅仅仅仅会用就行。
假如了解了设计思想,不仅能进步咱们运用的功率,还能够为咱们翻开思路。
举个简略的例子,空间换时刻的功用优化方针咱们都懂,但像Git在遵照这种方针的一起,在一些细节上不断优化,这样才能配的上Git当时的地位。