Skip to content

Latest commit

 

History

History
34 lines (21 loc) · 3.41 KB

the-beauty-of-commits.md

File metadata and controls

34 lines (21 loc) · 3.41 KB

优美的提交(Commit)

本节我们来讨论一下有关Commit的内容,在Git出现以前,很多版本控制工具把“分支(branches)”这个概念认为是一种很神奇的东西,通常这些分支的概念会演化出例如“主线分支”或者是“主干分支”这样的概念,不管怎么样,版本控制工具都把”分支“和"提交(Commit)"这两个概念严格区分开来。不过在Git的世界中,分支和提交二者居然是一样的东西,没有单独叫做“分支”的东西,在Git的世界中,只有blob,tree和commit。

我们知道一个Commit节点可以有一个或多个的父节点,而这些父节点也有着各自的父节点,这就意味着我们可以使用Commit来表示一个分支,因为我们可以通过任何一个Commit节点找到从项目直到演变为他的整个项目历史。

通过git branch命令,你可以在任何时候查看这些顶层分支:

$ git branch -v
* master 5f1bc85 Initial commit

请读者和我一起在心理默念——”分支仅仅只是一个提交的别名而已“,真的是这样,从这个角度来说,“分支”和“标签“(Tag)是完全相同的概念,唯一不同的只是,标签除了是一个commit别名之外,还有着自己的描述文字(就像Commit的描述文字一样),对就真的像便签条一样~而分支就真的只是一个别名而已。

然而我们真的需要别名吗?举个例子,如果我们想的话,我甚至可以在整个仓库中只使用哈希码作为我访问某一个Commit的方式,这里我发神经一样的把我的仓库reset到某一个commit:

$ git reset --hard 5f1bc85

这里的--hard选项的意思是,不管我的工作目录树里面有什么东西,已经在仓库里的也好,未提交的也罢,统统清空(关于这个命令后面会有专门的章节来解释),并重置为与目标commit(5f1bc85)完全一致。但其实有一条更加安全的命令来做同样的事情:

$ git checkout 5f1bc85

不同点在于,在我工作目录中的修改会被保留下来,当然我也可以通过加-f选项强制清空工作目录树,这就和get reset --hard的效果类似了。不过还是有一点不同,checkout仅仅只改变工作目录树而已,而reset命令不仅改变了工作目录,而且将当前分支的HEAD指针指向了目标commit,这是有本质区别的。

使用基于Commit的版本控制工具有一个好处就是:你几乎可以使用几个简单的命令来构造极为复杂的节点图,举个例子,如果你希望一个节点有多个父节点,那么只需要将多个节点”合并“(merge)到这个节点上,如果你希望一个节点有多个子节点,你也可以在同一个节点上checkout多个分支,这样对于他们产生的子节点就有共同的祖先节点。然而我们必须记住一点:无论是合并还是分裂,这些操作在Git来看都是统一的——只是对commit图的操作而已。简而言之,Git就是一堆Commit对象的集合,其中的每个Commit都对应着一棵目录树和一系列真正存储你的数据内容的blob。任何比以上描述更加复杂的操作,我们都可以认为是某些Commit操作的集合,取了个高大上的名字而已。

我们把这些概念都画在一张图上,读者可以通过这张图理解Git的基本概念: