这篇笔记旨在理解几个核心的git基本概念,如果对git了解较少,可以先看git基本教程。
这里推荐一个:廖雪峰-Git教程

commit(提交) 与 branch(分支)

版本号

commit是git管理的基本单位,在有多个分支的情况下,这些commit就构成了一颗commit树。
每个提交会有一个版本号(commit id),类似3628164fb26d48395383f8f31179f24e0882e1e0,即这个提交的命名,是用十六进制表示的一个SHA1计算出来的一个非常大的数字。

实际使用中,不必要写全这串字符,使用前面一部分,如前8位,git能唯一识别出来某个提交就可以了。
文中的提交二字,就是指一次commit,也就是一个版本,可以用版本二字代替,习惯使用提交这个说法了。

分支

从形式上看,分支(branch)就是从树的叶子节点到跟节点的一条线(可能有分叉),那git中是如何表示这条线的呢?或者换个问法,git中的分支名,究竟是表示什么呢。

其实分支名,如dev表示的只是dev这个分支上最新的那次提交,或者说,dev这三个字母,这个字符串,就是dev分支上最新的那个提交的别名,或者理解为那个最新提交的一个引用。

git中“并没有”分支,只有commit; 分支只是对某个特定commit的引用

如下图所示,dev只是 commit-6 的一个引用,如果在 commit-6 的基础上,做了一次新的提交,则dev指向这次新的提交。每个提交(除了第一次提交)都会有一个或者两个父节点提交,如图中的 commit-4 ,就是一次合并提交,拥有两个父节点。

git中用 HEAD 表示当前分支的最新的那个提交,即如果在dev分支,则 HEAD 表示(指向)的就是dev,也就是指向commit-6。HEAD^ 表示 HEAD 的前一个提交,即 commit-5; HEAD^^ 表示 HEAD 的前两个提交,前100个可以写成 HEAD~100 ,相应的,前一个也可以写成 HEAD~1

本地仓库 和 远程仓库

常见的GIT 工作区暂存区本地仓库远程仓库 的示意图。

看这幅图会理解git只有两个仓库,本地仓库远程仓库;但如果理解为三个仓库,才能更好地理解有些git命令或操作是什么回事。哪三个仓库?
本地工作仓库本地远程仓库远程仓库
名字是随便取的,重点在于便于理解。看下面这幅图。

这其中多了一个Local Remote repository, 即上面提到的本地远程仓库,这个仓库就是远程仓库在本地的一个拷贝,目的是为了和远程仓库保持一致。
常听到关于pull命令的解释就是,pull 是 fetch 和 merge 的结合,完成的实际上是先fetch,再merge。 如果没有本地远程仓库这个概念,其实不是特别好理解这句话。

git fetch做的其实是把远程仓库的更新全部拉取到本地远程仓库(注意,是全部,不只是当前分支)。而这里的merge,完整的写出来应该是这样(假设在dev分支):

/* pull */
git pull origin dev 

/* fetch + merge */
git fetch 
git merge origin/dev

这里的origin/dev指的就是本地远程仓库中的dev分支,它刚刚通过fetch命令,从真正的远端仓库拉取到了最新的数据,然而,你自己的dev分支并没有更新,需要将本地远程仓库中的dev分支(名字是origin/dev)merge到本地的dev分支。 pull就是完成的上面两步。 所以pull之后,本地的dev就直接更新了,所以才感受不到origin/dev的存在。

理解了 本地远程仓库 ,可以方便地完成一些操作。如,现在正在 dev 分支,需要合并远端最新的 master 分支的代码,如何做?
之前的作法可能是这样:

git checkout master     // 切换到 master 分支
git pull origin master  // 拉取远端代码到 master 分支
git checkout dev        // 切换回 dev 分支
git merge master        // 合并 master  分支

而实际上,不需要切换分支,这样就可以:

git fetch               // 更新本地远端仓库
git merge origin/master // 从 本地远端仓库 合并 master 分支

需要注意的是,这里必须先使用 git fetch 命令,直接使用 origin/master 并不会让本地远端仓库自动去远端拉取最新的代码。


END