目录
一、什么是Git
答:Git是一个分布式版本控制软件。另外提一句,它的开发者就是大名鼎鼎的Linux之父Linus。
版本控制,顾名思义,是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史,方便查看更改历史记录,备份以便恢复以前的版本的技术。(“后悔药”)
分布式,是一种版本控制的方式,有一个中心的服务器控制最新版本代码,每个开发者自己还有个本地仓库,在开发过程中先将代码提交到本地仓库再推送到中心服务器上。
也就是说,Git可以帮助我们完成这几件事:
回到过去
改变历史
古今对比
并行开发
谁动了我的代码
除了Git外,常见的版本控制软件还有SVN等
二、Git基本原理
Git的基本操作有很多,如果不理解其中的原理只是靠死记硬背,那肯定会事倍功半,所以,我建议第一步我们来学习下它的基本原理(亲身体会啊/哭,以前学过好几次Git,但过一段一时间不用就忘得差不多了,回头想想原来学的总是一知半解)。
我们先来想一个问题:Git为什么能知道我们以前的代码,为什么能“回到过去”,答案就是因为Git把我们每一次修改提交的代码以及状态都保存了下来,而展示给我们的只有当前一个版本。看下面这张图:
每次提交或保存当前项目状态,Git都会生成一个当前所有文件状态的快照,并存储一个对该快照的引用;而且文件没有发生变化时,Git不会重复保存快照,而只是链接到之前的标识文件。
说到这,可能还点不明白,别急,我们接下来看看快照到底存储了哪些东西。
快照存储不是一个个代码源文件,而是四种对象,几乎所有的Git操作都是在这四种对象上完成的,这四种对象是:
- “blob”:一个“blob”通常用来存储文件的内容。一个“blob”对象就是一块二进制数据,它没有指向任何东西或有任何其它属性,甚至没有文件名。因为“blob”对象内容全部都是数据,所以如若两个文件在一个目录树或是一个版本仓库中有同样的数据内容,那么它们将会共享同一个“blob”对象。“blob”对象和其所对应的文件所在路径、文件名是否改被更改都完全没有关系。
- “tree”:像一个目录,管理一些“tree”对象或是“blob”对象。它有一串指向“blob”对象或是其它“tree”对象的指针,一般用来表示内容之间的目录层次关系(就像文件和子目录)。
- “commit”:“commit”对象指向一个“tree对象”,并且带有相关的描述信息,标记项目某一个特定时间点的状态。它包括一些关于时间点的元数据,如时间戳、最近一次提交的作者、指向上次提交的指针等等。
- “tag”:一个“tag”对象包括一个对象名(SHA1签名)、对象类型、标签名、标签创建人的名字(“tagger”), 还有一条可能包含有签名(signature)的消息。
这些对象的关系可以用下图来理解:
所有的对象通过commit对象联系起来,每一次commit对象中又都会指向上一次提交后的commit对象,所以如果我们什么时候想回退版本,只需要找到相应的commit对象即可。而我们所谓的HEAD对象其实就指向最近一个提交的commit对象,也就是最后一个commit对象。
例如我们有一个项目,如果我们把它提交(commit)到一个Git仓库中, 在Git中“blob”、“commit”和“tree”对象的关系看起来会如下图:
可以看到: 每个目录都创建了“tree”对象, 每个文件都创建了一个对应的“blob”对象。最后有一个“commit”对象来指向根“tree”对象,这样我们就可以追踪项目每一项提交内容。
另外,这些对象的命名也很有讲究:在Git里随处可见一种“40个字符”的字符串(例如 6ff87c4664981e4397625791c8ea3bbb5f2279a3)。由于Git中每一个“对象名”都是对“对象”内容做SHA1(SHA1是一种密码学的哈希算法)哈希计算得来的,所以对于内容不同的对象,会有不同的SHA1哈希值。这样就意味着两个不同内容的对象不可能有相同的“对象名”。
这样,一次快照就是将有关的对象,即一组组由对象名和对象内容组成的的键值对存储下来。所以Git才会有那么好的“记忆力”。
三、Git用户交互
第二部分已经介绍了Git的基本原理,但只是涉及到Git自己内部存储,没说用户该怎么操作,所以为了能正常输入和输出,与用户交互,Git做了这样几件事。
说到这,我又想提一个常见的词API,API就是Application Programming Interface(应用程序接口),《JavascriptDOM编程艺术》书里提到“简单地说,API就是一组已经得到有关各方共同认可的基本约定”,说实在地我觉得这说的一点也不简单,书里这样说可能是想定义得更加广泛,但我的理解是从小地说API就像一个封装完成的函数,你给它一个输入,它给你一个输出,从大地说API就是一个程序,也是输入与输出,区别就在于规模的不同,结合到本文的内容,Git也像是一个API,输入命令,操作数据。好了我不说了,感觉自己着迷了,看什么都像API 。\笑哭
回到正文,Git为了与交互,做了什么事呢?
答案在下面的图里:
在Git,文件可能有三种状态:已提交(committed),已修改(modified),暂存(staged):
- 已提交(commited),说明数据已经存储在本地数据库;
- 已修改(modified),说明数据被修改,但是尚未存储到本地数据库;
- 暂存(staged),说明已标记将一个被修改的文件(当前版本)添加到待提交的快照中。
这三种状态分别对应Git项目的三大区块:Git目录,工作目录,暂存区。
- Git 目录(repository),即Git存储项目元数据和对象数据库的地方,也就是我们克隆(clone)某项目仓库时拷贝下的内容所在地;
- 工作目录(working directory),即从项目某版本中检出的当前所处分支,也就是从Git目录数据库中拉取的文件在本地磁盘保存所在地;
- 暂存区(staging area),即一个文件,通常包含在Git目录中,存储下一次需提交的内容,有时,它指向我们所说的“index”索引。
所以我们的实际操作流程是:
- 从Git目录,检出分支到工作目录
- 在工作目录修改文件
- 暂存文件,将其添加到待提交快照
- 提交,将快照持久化提交到Git目录
这些结合起来,就为我们进行代码版本控制提供了充足的底层支撑,这次就先写到这里,下一篇将详细介绍Git的使用与指令。