Git实用教程 7.1:合并和删除分支

目录

合并分支

删除分支

补充


话说天下大势,分久必合,合久必分。

我们知道,在 Git 仓库里,所有的快照事实上就是按照提交时间排放的。我们把串联这些快照的时间轴称之为分支,默认情况下 Git 只有一条 master 主分支。但是在现实开发中,从来不存在一条分支定乾坤的事情。

那么实际开发中的分支应该是长这样的:

注:这些分支的命名方法以及具体的创建逻辑,大家可以看一下上一篇 —— Git实用教程 7.0:创建和切换分支—— 的文末补充部分。

从上图中我们看出,一个项目根据其需求会产生很多分支,但最终都会被合并回去。

这一讲,我们就来谈谈分支的合并。


合并分支

当一个子分支的使命完结之后,它就应该回归到主分支中去。

上次经我们这么一折腾之后,分支的状态如下:

合并分支我们使用 merge 命令

git merge 分支名

执行 git merge feature 命令,将 feature 分支合并到 HEAD 所在的分支(master)上:

Holy crap!!  这就叫出师不利……

从 Git 提示的内容来看,我们知道这次的合并并没有成功,Git 说:

合并 README.md 文件的时候出现冲突。

所以自动合并失败;请修改冲突的内容并重新提交快照。

意思是说现在你需要先解决冲突的问题,Git 才能进行合并操作。所谓冲突,无非就是像两个分支中存在同名但内容却不同的文件,Git 不知道你要舍弃哪一个或保留哪一个,所以需要你自己来决定。

此时执行 git status 命令也会显示需要你解决的冲突:

然后 Git 会在有冲突的文件中加入一些标记,不信你打开 README.md 文件看看:

以“=======”为界,上到“<<<<<<< HEAD”的内容表示当前分支,下到“>>>>>>> feature”表示待合并的 feature 分支,之间的内容就是冲突的地方。

现在我们将 README.md 统一修改如下(同时去掉了 <<<<<<< HEAD 等内容):

保存文件,然后提交快照:

执行 git log --decorate --all --graph --oneline 命令,可以看到此时的分支已经自动合并了:

当然,如果在合并分支的过程中,git 发现两个快照不存在冲突,就不用搞这么多了……

我再做一次给你看!

执行 git checkout -b feature2 命令(相当于 git branch feature2(创建分支) 和 git checkout feature2(切换分支) 两个命令的合体):

在工作目录随便创建一个文本文件(feature2.txt)并提交快照:

执行 git log --decorate --all --graph --oneline 命令:

可以看到,feature2 分支比 master 分支快了一步。现在我们切换回 master 分支,并将 feature2 分支合并进来:

这次 Git 只显示了 Fast-forward(快进)这词儿,这是因为 feature2 这个分支的父结点是 master 分支,所以 Git 只需要简单移动 master 的指向即可。

执行 git log --decorate --all --graph --oneline 命令:


删除分支

对于不再需要的分支,我们还是把它们删了吧!

删除分支,使用 git branch -d 分支名 命令:

执行 git log --decorate --all --graph --oneline 命令:

这时 feature 和 feature2 已经消失了……

(两个快照的名字已经删除了,但是具体的快照还存在,因为Git 的分支原理实际上只是通过一个指针来实现的,也就是说它所谓的分支就是用一个指针(一个名字)来记住你这个快照)

由于 Git 的分支原理实际上只是通过一个指针记载,所以创建和删除分支都几乎是瞬间完成。

注意:如果试图删除未合并的分支,Git 会提示你“该分支未完全合并,如果你确定要删除,请使用 git branch -D 分支名 命令。


补充1

对于分支的合并,Git 又是领先业界好几年! 

Git 使用自动三方合并的方式,就算在一段较长的时间内,反复把一个分支合并入另一个分支,也不是什么难事。因为通常情况下,Git 会自动解决一些它能够理解的“冲突”,不需要你来干预;但如果是 SVN 之流则需要你亲自处理每一次合并的冲突。

那你知道什么是三方合并(three-way merge)吗?

上面的例子我们演示了 Git 的两种合并方式:Fast-forward 和 Three-way merge。

Fast-forward

所谓的 Fast-forward 就是当待合并的分支位于目标分支的直接上游时,Git 只需把目标分支的指针直接移动即可实现合并。

比如下面图片中 master 分支是位于 feature2 分支的直接上游:

将 feature2 分支合并到 master 分支,只需要移动 master 的指针即可:

Three-way merge

如果待合并的两个分支不在同一条线上,那么进行合并就需要解决一个根本的问题 —— 冲突!

我想有同学可能就会问了:为何两个分支在同一条线上就不会冲突呢?

因为 Git 的快照是按时间顺序提交的,所以在同一条线上的两个快照,它们是有先后顺序的,尽管两者可能出现同名文件不同内容,Git 会认为这是“改变”而不是“冲突”。

对于冲突的处理,Git 相对来说是比较机智的。

举个例子:

合并 C3 和 C4 得到 C5,但 C5 应该如何处理“冲突”呢?

SVN 会把问题抛给用户,让用户自行解决;Git 则显得更为高明,它会找到第三个快照,然后综合三者特点自动解决冲突。

那第三个快照应该如何决定呢?

没错,应该找两者的共同“祖先”作为参照物,一比较就知道两个分支都干了些什么。

图片中 C3 和 C4 的共同祖先是 C1,可以看到 C3 和 C4 分别增加了 test2.py 和 test1.py 两个文件。因为对比之后发现三者并没有冲突,所以 C5 应该是三者的合体,即同时拥有 common.py、test1.py 和 test2.py 三个文件(有兴趣的鱼油不妨测试一下)。

另外,值得一提的是,Git 这种合并方式也适用于同名文件的不同更改。

举个例子:

这里 C3 和 C4 都只有一个文件(test.txt),但是内容却不一样。如果这样合并,你猜 Git 会不会报“冲突”?

答案是不会的!

因为 Git 找到它们的共同祖先 C1,可以看到 C3 和 C4 都是在 C1 的基础上进行添加(C4 在第 2 行添加了“I”,C3 在第 4 行增加了“FishC”,C1 第 3 行的“love”是它们共同拥有的),同时在每一行并没有产生冲突的地方,所以自动合并后的 C5 是这样的:

  • # test.txt
  • I
  • love
  • Python

注意:如果 Git 检测到同一行有不同的内容,还是会报冲突并让你自行决定谁去谁留的!


补充2

通常情况下,Git 会尽可能地尝试使用 Fast-forward 方式来合并分支,因为这样效率非常高,只有在万不得已的时候才使用三方合并(Three-way merge)……

但是,有时候 Fast-forward 的方式却很容易让我们忽略了分支的存在!

举个例子,比如下面这样:

 

如果我们把“feature”分支删除,就会直接丢掉分支的信息:

 
根本就不知道有个分支存在过……

可有时候我们确实希望保留分支的信息,应该怎么办呢?

做法如下:

只需要在 merge 的时候使用 --no-ff 选项即可告诉 Git 不要使用 Fast-forward 方式合并分支。

这样 Git 就会乖乖地使用三方合并(Three-way merge):

 

OK,这样就算删掉了“feature”分支,我们仍然可以很容易看出有个分支曾经存在过:

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值