Git 的原理与使用(上)中介绍了Git初识,Git的安装与初始化以及工作区、暂存区、版本库相关的概念与操作,本文接着上篇的内容,继续深入介绍Git在的分支管理与远程操作方面的应用。
五、分支管理
1.理解分支
分支是Git的杀手级功能之一。
它就像是科幻电影里面的平行宇宙,当你正在电脑前努力学习C++的时候,也许另一个平行宇宙里的另一个你正在努力学习JAVA。这两个平行宇宙互不干扰,而在某个时间点,它俩发生了合并——于是,你既精通了C++,又精通了JAVA!
在版本回退章节里我们已经知道,Git会把每次提交串成一条时间线,这条时间线就可以理解为是一个分支。截止到目前我们的Git仓库里中只有一条时间线,这个分支叫主分支,即master分支(也叫main分支)。
然后再来理解一下HEAD。HEAD严格来说指向的不是“提交(commit)”,而是当前所在的分支,而分支才是指向“提交(commit)”的。比如当前我们所在的分支是master,那么HEAD指向的就是master,master指向的才是“提交”。如下图所示:
每次执行commit操作,master分支都会向前移动一步以指向最新的“提交”。这样,随着你不断commit,master分支的线也会越来越长。而HEAD则一直指向指向当前分支不变,即现在我们所在的master分支。
可以通过
git cat-file -p <commit ID>
命令来查看提交信息:
2.创建分支 branch
如何查看当前本地仓库中已有的所有分支?
在工作目录下执行
git branch
命令,系统就能为我们打印出当前本地仓库中有哪些分支:
hyb@139-159-150-152:~/gitcode$ git branch #查看当前本地所有分支 * master # 显示结果
这里查询到当前本地仓库中只有一个master主分支。
在我们创建本地仓库时,git会自动给我们创建出一个master主分支。master前面有一个 * ,*master 既表示master是当前的工作分支,也表示master分支正在被HEAD指针所指向。
关于HEAD:
HEAD不仅可以指向master主分支,也可以指向其它任意分支。
被HEAD指向的分支是当前正在工作的分支。由于之前我们的HEAD一直指向的是master分支,因此add操作、commit操作都是在master主分支上完成的。
为了演示多分支的情况,这里我们来创建第一个新的分支dev,对应的命令为:
git branch <分支名>
hyb@139-159-150-152:~/gitcode$ git branch dev #新建分支dev hyb@139-159-150-152:~/gitcode$ git branch dev * master
当我们创建新的分支后,Git 便新建了一个指针叫 dev。而 *HEAD
表示当前的 HEAD 仍然指向 master 分支,并没有发生变化。
也就是说,单纯的创建分支,是不会自动切换分支的。
# 打印一下HEAD指针的内容,可见HEAD指向的还是master hyb@139-159-150-152:~/gitcode$ cat .git/HEAD ref: refs/heads/master
此时,cat一下dev和master的内容,会发现它们两个指向的是同一个修改(即commit Id),这是因为dev分支是站在当前的版本去创建的,所以dev分支初始情况下也指向了最新的提交:
一张图总结:
3.切换分支
我们要想在dev上进行操作,就必须把dev分支当作工作分支。那如何切换到dev分支下进行开发呢?使用
git checkout <分支名>
命令即可完成切换。示例如下:
执行 git checkout
dev切换分支后,HEAD就指向了dev,表示我们已经成功地切换到了dev上。随后,我们在dev分支下修改ReadMe文件,在末尾新增了一行内容 aaa on dev branch,并进行了一次提交操作。
现在,dev分支的工作完成,我们切换回master分支:
hyb@139-159-150-152:~/gitcode$ git checkout master Switched to branch 'master'
切换回master分支后再查看ReadMe文件内容,此时发现ReadMe文件中刚才新增的内容不见了:
而再切回dev查看,刚才新增的内容又有了:
为什么会出现这个现象呢?
我们来看看dev分支和master分支的指向,发现dev和master两者此时指向的提交已经不一样了:
hyb@139-159-150-152:~/gitcode$cat.git/refs/heads/dev bdaf528ffbb8e05aee34d37685408f0e315e31a4 # dev和刚创建时相比已经发生了改变 hyb@139-159-150-152:~/gitcode$cat.git/refs/heads/master 5476bdeb12510f7cd72ac4766db7988925ebd302
进一步的,可以通过命令
git cat-file -p bdaf528ffbb8e05aee34d37685408f0e315e31a4
来查看此时dev所指向的提交的详情。查询到的详情中,parent关键字标识了当前commit的上一个commit是哪一个。可以看到,dev当前指向的上一个指向,正是刚创建dev时dev的指向。因为我们是在dev分支上提交的,所以dev会指向最新的提交;而master分支此刻的指向并没有改变(正如两个平行宇宙之间彼此独立的)。
master的视角下,当然就看不到在dev下的提交了,因此在master和dev两个分支下的ReadMe文件内容会有差别。
4.合并分支
为了在master主分支上能看到新的提交,就需要将dev分支合并到master分支。
使用
git merge dev
命令,示例如下:
git merge
命令用于合并指定分支到当前分支。合并后,master分支上就能看到dev分支提交的内容了。此时的状态如图如下所示:
仍然可以用 cat .git/refs/heads/master命令和 cat .git/refs/heads/dev命令分别查看master和dev的指向,此时发现二者指向再次相同,dev中存储的commitID也被同步给了master。
By the way,细心的朋友可能注意到,刚才merge操作后,结果其实有些特殊:
这里的 Fast-forward 代表“快进模式”,它是Git分支合并的一种方式。快进模式意味着合并的方式是直接把master指向dev的当前提交,所以合并速度非常快。当然,也不是每次合并都是Fast-forward,我们后面会讲其他方式的合并。
5.创建、切换、合并分支流程图
1、创建新的dev分支。
2、git checkout dev
:切换至dev分支。
3、在dev分支上commit。
4、切换回master分支,git merge dev
6.删除分支
合并完成后,dev分支对于我们来说就没用了,那么dev分支就可以被删除掉。删除分支的命令为:
git branch -d <分支名>
注意,如果当前正处于dev分支下,是不能删除dev分支本身的:
hyb@139-159-150-152:~/gitcode$ git branch *dev master hyb@139-159-150-152:~/gitcode$ git branch -d dev error:Cannot delete branch 'dev' checked out at'/home/hyb/gitcode'
要删除当前分支,只能先切换到其他分支下,再执行删除操作,如:
hyb@139-159-150-152:~/gitcode$ git checkout master Switched to branch 'master' hyb@139-159-150-152:~/gitcode$ git branch -d dev Deleted branch dev(was bdaf528). hyb@139-159-150-152:~/gitcode$ git branch * master
此时的状态如图如下所示:
因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全(“安全”正是使用分支的一个原因)。
7.合并冲突
在实际中,分支并不是想合并就一定能合并成功的,有时可能会遇到代码冲突的问题。
为了演示这问题,我们创建一个新的分支 dev1 ,并切换至目标分支。
注意,可以使用
git checkout -b dev1
一步完成创建并切换的动作,示例如下:
it checkout -b dev1
和
git branch dev1
+ git checkout dev1
效果是相等的。
我们在dev1分支下修改ReadMe文件,将 aaa on dev branch 改为 bbb on dev branch,然后提交:
此时在dev1分支中,ReadMe文件最终修改并提交的结果为 bbb on dev branch。
切回master分支,然后在master分支下将ReadMe文件最终修改并提交为 ccc on dev branch:
现在master分支和dev1分支中都各自有了新的提交:
Git只能试图把各自的修改合并起来,但这种合并可能会有冲突。
在master分支中执行 git merge dev1
,将dev1分支与master分支上提交的修改合并,结果如下:
发现ReadMe文件有冲突后,可以直接查看文件内容来查看冲突详情。Git会用<<<<<<<,=======,>>>>>>>来标记出不同分支冲突的具体内容,如下所示:
此时我们要做的是手动调整发生冲突的代码(将不要的内容手动删除,把要的内容保留下来),并需要再次提交修正后的结果!!
(再次提交很重要,切勿忘记)
到这里冲突就解决完成,此时的状态变成了:
Git 的原理与使用(中)(二)
+https://developer.aliyun.com/article/1521973?spm=a2c6h.13148508.setting.17.439a4f0eWJ4WIw