Git合并分支超详细解释

摘自朱双印老师博客http://www.zsythink.net/archives/3470

在执行"合并"操作时,我们可以根据具体情况,选择不同的合并模式去合并,不同的合并模式对应了不同的命令参数,而且在合并的过程中,还可能遇到"冲突"。

为了说明白原理,我们先来看一些示意图
在这里插入图片描述
此示意图并没有涉及到任何合并操作,而是描述了合并分支之前,两个分支的创建过程。

上图中的第1步表示已经存在的一条分支,这条分支的名字是base。

第2步表示基于base分支,创建了new分支,此时,base分支的指针和new分支的指针都指向了最新的提交。

第3步表示我们在new分支中创建了新的提交。

第4步表示base分支也产生了新的提交,new分支也产生了新的提交,两个分支的指针分别指向了自己分支的最新提交,换句话说就是,从分叉点开始以后,两个分支各自产生了属于自己的提交。

上图中有两个分支,base分支和new分支,如果,我们想要将两个分支合并,该怎么做呢?没错,我们只需要将一条分支合并到另外一条分支上就行了,但是,仔细想想,此处有一个小问题,我是把new分支合并到base分支上呢?还是将base分支合并到new分支上呢?这里牵扯到一个合并方向的问题,让我们带着这个疑问,看看如下示意图:
在这里插入图片描述
图示一表示将new分支合并到base分支上以后的状态,图示二表示将base分支合并到new分支上以后的状态。

我们先聊聊图示一,图示一表示将new分支合并到base分支上,合并操作完成后,会产生一个新的提交(蓝色提交),这个新提交就是合并后的提交,它包含了两个分支中的最新代码,并且将它们合并到了一起,这个提交就是我们想要的合并后的状态,base分支的指针会指向这个新的蓝色提交,而new分支的指针则没有移动位置,仍然指向了new分支的最新提交(绿色提交)。为什么base分支的指针会指向最新的蓝色提交,而new分支的指针却保持原位呢?我们可以这样理解,在合并之前,base分支和new分支都有属于自己独有的提交(最新的黄色提交只属于base分支,绿色提交只属于new分支),如果我们是把new分支合并到base分支上,就表示要把只属于new分支上的变更合并到base分支上,对于base分支来说,会有新的变更进入(原来只属于new分支的变更对于base分支来说就是新变更),所以,base分支需要一个新的提交(蓝色提交)来对应变化后的状态,于是,base分支的指针会指向最新产生的合并提交(蓝色提交),而对于new分支来说,并没有任何内容发生变动,所以new分支的指针仍然保持原位。

图示二表示将base分支合并到new分支上,合并后会产生一个新的合并提交(蓝色提交),这个蓝色提交对应了合并后的状态,这个新的蓝色提交属于new分支,而不属于base分支,因为我们是把base分支合并到new分支上,这表示只属于base分支的变更会加入到new分支中,对于new分支来说,内容会发生变化,new分支需要一个新的提交来对应变化后的状态,而这个新的提交正是合并后产生的蓝色提交,于是,new分支将指针指向了蓝色提交,base分支的指针仍然保持原位。

你肯定已经总结出了规律,规律就是,在上述情况下,合并后的新提交属于合并到的目标分支。

还有一种合并场景
在这里插入图片描述
第4步表示:基于base分支创建new分支以后,只有new分支中产生了两个新提交,而base分支中还没有产生任何新提交。
上图中第4步的情况下,我们还可以选择另外一种更加快捷的方式完成合并,这种快捷的合并方式被称之为"Fast-forward"(可译为"快进"或者"快速重定向")

在这里插入图片描述
第5步代表使用"Fast-forward"的方式合并后的样子,你肯定已经看明白了,由于基于base分支创建new分支以后,base分支中并没有产生任何新的提交,如果此时想要将new分支合并到base分支,只需要将base分支的指针指向到new分支的最新提交,即可让base分支包含new分支中的所有新变更。

我们换个方式再解释一遍,new基于base创建,new新产生的所有变更都包含在上图中的绿色提交中,将new合并到base,就表示将new中的变更(所有绿色提交中包含的变更)也加入到base中,让绿色提交属于base分支最快的方法就是直接将base分支的指针直接指向最新的绿色提交。

"Fast-forward"的合并方式不会在base分支中产生任何合并提交(即不会产生示意图中的蓝色提交),而是利用了指针的移动,快速的实现了将new分支合并到base分支中的目的。

那么,什么情况下才能使用"Fast-forward"的合并方式呢?示意图如下:
在这里插入图片描述
如果合并分支之前,base分支和new分支都有了属于自己分支的独有提交,就不能使用fast-forward的方式进行合并。如果移动base指针到绿色提交,就会丢失最新的黄色提交,如果移动new指针到最新的黄色提交,就会丢失绿色提交,这就是为什么在状态二的情况下不能使用fast-forward的原因。


为了能够更加方便的进行演示,我们来创建一个测试仓库,在测试仓库的master分支中创建一些基础的可以用于测试的提交,操作如下

$ git init test_repo
Initialized empty Git repository in D:/workspace/git/test_repo/.git/
 
$ cd test_repo/
 
$ echo "test1" > m1
 
$ echo "test11" > m11
 
$ git add -A
 
$ git commit -m "Initializes files of module 1"
[master (root-commit) 0da419c] Initializes files of module 1
2 files changed, 2 insertions(+)
create mode 100644 m1
create mode 100644 m11
 
$ echo "test2" > m2
 
$ echo "test22" > m22
 
$ git add -A
 
$ git commit -m "Initializes files of module 2"
[master 5b8c4c8] Initializes files of module 2
2 files changed, 2 insertions(+)
create mode 100644 m2
create mode 100644 m22

此处假设,测试仓库中的这些测试文件就是我的程序代码,假设我的程序由两个模块组成,模块一和模块二,m1文件和m11文件属于模块一,m2文件和m22文件属于模块二,我会为模块一和模块二分别创建两个分支,以便针对两个模块的修改互不影响,当我需要一份完整的代码时,会将模块一和模块二对应的分支合并到master分支中,以便从master分支获取到相对完整的代码,现在,我们需要分别为两个模块创建分支,b1分支和b2分支,操作如下:

$ git status
On branch master
nothing to commit, working tree clean
 
$ git branch b1
 
$ git branch b2

如上述操作所示,我基于master分支,创建了b1分支和b2分支,使用"gitk --all"命令,查看图形化界面,如下:
在这里插入图片描述
目前来说,这三条分支时完全相同的。

现在,我切换到b1分支,修改一些文件,模拟针对模块一代码的修改工作,并且在b1分支上创建提交,操作如下:

$ git checkout b1
Switched to branch 'b1'
 
$ cat m1
test1
 
$ echo "test m1" >> m1
 
$ cat m1
test1
test m1
 
$ git add m1
 
$ git commit -m "modify m1"
[b1 be27bc9] modify m1
1 file changed, 1 insertion(+)

同样,切换到b2分支,进行一些修改,模拟针对模块二的修改。操作如下

$ git checkout b2
 
$ cat m2
test2
 
$ cat m22
test22
 
$ echo "test m2" >> m2
 
$ echo "test m22" >> m22
 
$ cat m2
test2
test m2
 
$ cat m22
test22
test m22
 
$ git add -A
 
$ git commit -m "modify module2"
[b2 a73e5ca] modify module2
2 files changed, 2 insertions(+)

完成上述操作后,再次使用"gitk --all"命令,查看图形化界面,如下:
在这里插入图片描述

如上图所示,b1分支和b2分支分别产生了属于自己的独有提交,也就是说,通过上述操作,这两个提交中分别存放了两个模块的最新代码,master分支中不包含这两个模块中任何一个模块的最新代码,如果我想要将两个模块的最新代码汇聚到master分支中,只需要将b1分支和b2分支合并到master分支中即可,那么具体该怎么操作呢?

如果你想要的将A分支合并到B分支,就需要先检出到B分支,然后再执行合并命令将A分支合并进来,也就是说,需要先检出到目标分支,再执行合并命令

先以合并b1分支为例,看看怎样将b1分支合并到master分支,具体操作如下:

#如果我们想要将某个分支的代码合并到master分支,需要先切换到master分支
$ git checkout master
Switched to branch 'master'
 
#查看一下m1文件的内容,并不是模块一最新的文件内容,m1的最新版本目前只存在于在b1分支中
$ cat m1
test1
 
#使用如下命令即可将b1分支合并到当前分支(当前分支是master分支),git merge命令就是用于合并分支的命令,此命令会将指定的分支合并到当前分支。
$ git merge b1
Updating 5b8c4c8..be27bc9
Fast-forward
m1 | 1 +
1 file changed, 1 insertion(+)

从上述命令的返回信息可以看出,当我们把b1分支合并到master分支时,git默认使用了"Fast-forward"模式,这是因为git发现,b1分支是基于master分支创建的,并且master分支并没有产生属于自己的独有的提交,所以,当我们需要把b1分支合并到master分支时,只需要将master的指针指向b1分支的最新提交即可,使用"gitk --all"查看图形化界面,如下:

在这里插入图片描述
正如我们所想,master分支的指针指向了b1分支的最新提交,也就是说,此时b1分支已经合并到了master分支中。

再次查看master分支中的m1文件内容,发现m1的内容已经变成了最新的版本

$ cat m1
test1
test m1

如上述操作所示,我们把b1分支合并到了master分支中,master分支中已经包含了模块一的最新版本的代码,但是目前,master分支中还不包含模块二的最新代码,查看master分支中模块二的文件,内容仍然是最初的,如下:

$ cat m2
test2
 
$ cat m22
test22

我们可以使用同样的方法即可将b2分支合并到master分支中。既然是想将b2分支合并到master分支中,就需要先检出到master分支,但是由于我们当前就处于master分支,所以就不用执行checkout命令了,直接执行merge命令即可,不过,在执行merge命令之前,请先思考一个问题,在当前状态下,如果将b2分支合并到master分支,还能使用"Fast-forward"模式吗?

答案是:不能。

为什么不能呢?为了更加容易理解,我们可以对比着图形化界面中的状态和之前的示意图中的状态去理解,如下:
在这里插入图片描述
master分支就相当于图示中的base分支,b2分支就相当于图示中的new分支,所以,在这种状态下,如果我们想要将b2分支合并到master分支中,则不能使用"Fast-forward"的模式进行合并,只能使用创建新提交的方法进行合并,换句话说就是,当我们将b2分支合并到master分支以后,会产生一个新的合并提交,注意,我们必须为这个新提交填写注释信息,否则将无法完成合并操作。

$ git merge b2

注意,执行上述命令后,git会自动调用vim编辑器,并且自动生成如下图中的注释信息,下图中的注释信息都是为了"新提交"而准备的,刚才说过,我们必须为新提交填写注释信息,而下图中的注释信息则是git自动默认生成的,"Merge branch b2"表示这个新提交就是为了合并b2分支而产生的提交,你也可以根据自己的需要,修改如下注释信息,但是此处为了方便,直接使用默认的注释信息。
在这里插入图片描述
在vim编辑器的命令模式下输入wq,保存退出,即可使用默认的注释信息,退出vim后,看到如下返回信息

$ git merge b2
Merge made by the 'recursive' strategy.
m2  | 1 +
m22 | 1 +
2 files changed, 2 insertions(+)

此时,再次查看图形化界面,如下:
在这里插入图片描述

从上图可以看出,当我们将b2分支合并到master分支以后,产生了一个新的提交,这个提交属于master分支,这个提交中包含了来自b2分支中的变更,这个提交的注释信息是"Merge branch b2",正是刚才git默认生成的注释信息。

其实,我们每次执行git merge命令时,git都会先去尝试能不能使用"Fast-forward"的模式进行合并,如果能,就默认使用"Fast-forward"的模式进行合并,如果不能,就创建一个合并提交进行合并


现在,我们着重的看一下merge命令以及常用的一些参数。

git merge A

上述命令表示将A分支合并到当前分支。

git merge --no-ff A

将A分支合并到当前分支,但是明确指定不使用"Fast-forward"的模式进行合并。

git merge --ff-only A

当能使用"Fast-forward"模式合并时,合并正常执行,当不能使用"Fast-forward"模式合并时,则不进行合并。

摘自朱双印老师博客http://www.zsythink.net/archives/3470

  • 26
    点赞
  • 106
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值