开发者必备!你真的懂Git吗(你知道Git的运行进制吗?)

大家平常在工作中可能都用过Git,或者曾经也用过Git,或者略懂一些。Git add .或者 git commit –m “ok”,这两句也是常用的,是不是很简单?当然不是,Git不简单,但是也真的不好学。
在Git里面指令一共有两种,一种是底层命令(plumbing),另一种是高层命令(Porcelain)。早期的Git使用的是底层命令,实际上不太容易上手,后来才慢慢加入了一些高层的指令,比如说add,commit。虽然我们平常用到的指令不多,但是在平常的开发中都已经够用了。
可能你之前用过SVN这些版本控制系统(VCS),但是Git这这些VCS有本质上很大的差异的,如果仅仅将Git当做SVN的加强版,那学习Git会有不少困难,Git在处理文件和分支的方式都有很大的不同。
Git add . 与Git add –all有什么区别?
Git 1.x版的时候:-all 处理新增文件,修改文件,删除文件;而点 . 则仅处理新增文件和修改文件。
但是在Git2.x版本后,这两种命令处理的效果统一了,都是处理新增文件,修改文集,删除文件。
另外一个不同的是,git add –all命令不管是在项目的哪一层目录都有效果,而点 命令git add . 仅对当前目录及其子目录有效果。
Git是什么?我们听到的多数答案都是:Git是分布式版本管理系统!老实说,我第一次听我同学介绍的时候,一脸蒙逼,然后继续反问,怎么分布式?什么版本管理?都没问出个究竟,还是一头雾水!
首先,什么是分布式?分布式不等于将项目放在Github上就是分布式。举个例子,团队有01, 02, 03, 04,05这几个开发者在共同开发一个项目,01开发完成了一个Cache(C)模块,并将项目提交到Github或者Git server上面,其他人通过pull拉一份下来,这是常见的场景。
但是这个其实也不同Git也可以实现,通过copy,email等各种网络传送方式也可以实现。Git也包含这个功能,使用Git当我们完成一个任务就可以commit到本地仓库,并push到远程仓库,其它人就可以pull下来继续开发。
什么是版本?当你每次完成commit一个任务,不管是增加,修改,还是删除,都可能会造成目录和文件上的变化,这样每一次的变化结果称为一个版本。
Git跟其它版本管理系统有什么不同?Git只侧重文件的内容,不侧重目录或文件的名称,Git实际上是一个内容追踪软件,只是刚好它用来作为版本控件很适合。那目录或文件的名称在哪里?答案是存在tree里面。
Git commit的过程中发生了什么事?
Git四大组件:blob,tree,commit,tag
Blob:主要存放文件相关的内容;
Tree:存放目录的相关内容;
Commit:存放提供提交相关的内容;
Tag:存放和tag相关的东西。
当你使用git将文件加入到暂存区的时候,则生成的blob,commit的时候,git就生成了tree,tree主要记录和目录文件相关的信息,所以tree会指向blob,同时也会指向其它的tree;commit完成,就生成了commit,该commit指向指定的tree,tag指向commit。

开发者必备!你真的懂Git吗(你知道Git的运行进制吗?)

文章图片
在git仓库新增一个文件,添加内容,该文件还未被加入到git,此时出于未被追踪的状态(内容未被追踪),可以使用git status来查看仓库当前状态。
开发者必备!你真的懂Git吗(你知道Git的运行进制吗?)

文章图片
接着我们可以通过git add命令将文件添加到git的暂存区(缓存区,索引区),此时git就生成了一个blob对象了,你可以在仓库的.git/objects下找到。你要记得,Git只侧重文件的内容,只追踪内容,内容,内容。。。
Git对你的目录是不感兴趣的,比如:
开发者必备!你真的懂Git吗(你知道Git的运行进制吗?)

文章图片
创建一个bin目录,git是不会去追踪它的。
接着,git commit提交,此时git就会根据目录生成tree对象,这些tree对象分别指向所包含的blob对象,可能还会有其它的tree,接下来就是生成commit对象,master分支的master就指向commit,而HEAD则指向master,这样就完成了一次commit。
继续,向文件中添加新内容,这时候它的状态就是modified(已被修改),git add 添加到暂存区,因为此时的内容已经不一样了,这时git又会生成一个新的blob对象,你可以在.git/objects下面看到发生了变化。
Git commit提交本次更改,git再次生成新的tree,该tree指向本次新的blob对象,并生成新的commit对象指向该tree,并且新的commit对象指向上一个commit。此时,master指针和HEAD指针往前移动,完成本次commit。
重复类似操作,你可以通过git count-objects查看当前对象数量。
对分支的理解
有些人可能会理解为将文件复制一份,进行修改再合并回去,所以这样就不会影响原来的分支。实际上不是这样,为什么说大家都说“在git上开一个分支很便宜”?其实分支只是指向某个commit的指针,和HEAD、master类似,只是一个大概40字节的文件,你可以在.git/refs/heads下面看到。打开master文件,使用git reflog查看提交列表,你可以发现分支的前七个字符串和master文件里面的前七位相同。
Git branch dev创建分支,你可以在.git/refs/heads下面发现马上多了一个dev文件,删除该文件,分支也没有了,修改文件名,分支名称也变了。分支文件里面的内容指向当前的提交。
对HEAD的理解
HEAD只是指向某个分支的指针,每一个分支branch都会指向其当前的commit,而HEAD会指向当前工作的分支branch。
你可以通过git checkout dev切换到dev分支,git branch查看当前工作分支为dev,也可以通过查看.git/HEAD文件查看:ref: refs/heads/master,注意不要随便更改该文件。
【开发者必备!你真的懂Git吗(你知道Git的运行进制吗?)】Git checkout时会把repo里的东西搬一份到工作目录,并且变更.git/HEAD文件内容。

    推荐阅读