git代码合并
文章主要介绍Git
的三种代码提交合并方式:merge
、rebase
和cherry-pick
。
git代码合并
git
代码合并(分支合并)操作主要有三种:merge
、rebase
、cherry-pick
,其中:
merge
:合并操作,会将其他分支的内容合并到本分支,并且视情况可能需要填写提交信息rebase
:变基操作,将其他分支的内容搬到当前分支,变基操作能够让当前的分支保持成一条线的形式,使分支看起来很干净cherry-pick
:樱桃摘,真正的神!我们有些时候并不需要完全合并另外一个分支,可能我们仅仅需要合并其他分支的某个或者某几个提交记录,这个时候merge
和rebase
就不适用了,因为无论是merge
还是rebase
都会合并到待合并分支的最新的位置,而cherry-pick
就能实现这种单个提交记录的合并的需求
简单来讲,merge
和rebase
的功能都是将某个分支的提交记录(直到最新提交记录)合并到另外一个分支,而cherry-pick
是将某个分支的某个或者某几个提交记录拿到当前分支。
但无论是merge
、rebase
还是cherry-pick
,在合并记录的过程中,都有可能会遇到一个问题:合并冲突。这些我们都会在文章后续进行介绍。
rebase操作
我们假定有如下的分支情况:dev
分支在我们main
分支的基础上更新了两个版本
此时dev
分支是最新的,我们希望将dev
的代码合并到main
,由于此时我们在dev
分支(dev
分支的星号代表我们当前在dev
分支)
我们需要做的事情是:
- 先切换到
main
分支:git checkout main
- 进行变基操作:
git rebase dev
此时我们就能合并完成(这种合并我们一般称为forward
):
这里要注意,我们希望将提交记录较为落后的A
分支(比如上图的main
分支)合并到提交记录较新的B
分支(如上图的dev
分支),我们需要先切换到提交记录相对落后的A
分支上操作!
如果你的分支记录已经差分而非连续,即是下面的这种情况:
则此时rebase
的意思就不是简单的forward
了,这时候我们在main
分支上,我们对dev
分支进行rebase
,执行git rebase dev
,将会得到下图的记录
我们之前提过rebase
会将提交记录整理成一条线,其实说的就是这种情况,并且可以看到rebase
的过程实际上就是把C1记录之后的main分支上的所有提交搬运到dev分支的最新提交的后面,因此我们举一反三一下,目前我们的提交如下:
则我们进行rebase
将会得到:
能理解rebase
是怎么操作的了吗?好,这个时候我们回到这张图
我们checkout
一下(git checkout dev
),切换分支到dev
,我们反过来,要将dev
分支rebase
到main
又会是什么情况呢?没错,同样的方法,我们将C1
之后的所有dev的提交记录搬运到main
上(git rebase main
)
懂了吧,这就是rebase
哦
merge操作
我们在介绍rebase
的时候实际上分了两种情况,即:和
我们可以看到rebase
对这两种情况的处理是一样但又不完全一样,在操作上,第一种情况是直接前进而第二种情况是会将一个分支的C1
之后的所有提交变基到另外一个分支上。但就结果而言他们是一样的,因为最终的提交记录都会变成一条线的形式!
然而,你可能看过一些其他的git
的文章,很多博主都推荐使用merge
而不是rebase
,为什么呢?
第一,merge
操作专门设置了两种模式来专门处理这两种情况。处理第一种情况的模式叫fast-forward
(快速前进,--ff
参数),我们切换到main
分支,使用fast-forward
模式来进行merge
:
1 | git checkout main |
merge
的结果如下,可以看到针对第一种情况,其实rebase
和merge
的结果并没有区别(因此实际上第一种情况使用rebase
和merge
都可以),这是因为fast-forward
模式并不会发生真正的合并,只是通过移动指针造成合并的假象,即移动main
分支的HEAD
指针到dev
的最新分支,来实现合并功能。
然而针对第二种情况,则merge
就展现出其和rebase
的不同了,第二种情况使用merge
会产生新的提交记录,这种针对第二种情况产生新纪录的merge模式被称为non-fast-forward
(需要加上-–no-ff
参数),我们同样切换到main
,然后使用这种模式来处理第二种情况:
1 | git checkout main |
将得到下面的结果:
这也是为什么使用merge
而不是rebase
的情况,因为使用merge
,则dev
上的提交记录和main
上的提交记录都被保留下来,并且你还能看到他们是在C1
记录开始进行差分的,而使用rebase
,则这些分支的差分记录都会被擦除,变成一条直线,这个时候你就看不出来到底dev
和main
在哪个记录开始进行差分了!这一点非常不利于后续的bug跟踪和版本跟踪!!
使用non-fast-forward
,在git log
会看到很多类似:Merge branch 'feature001' into master
的commit
。(即图上的C5
)
最后,merge
除了上面两种模式之外,其实还有第三种模式-–ff-only
,即只使用fast-forward
模式进行合并,如果合并失败的话则直接退出,返回失败信息!
git merge
在不加参数的时候会先使用fast-forward
来合并,如果合并失败则会自动切为使用non-fast-forward
模式。
个人使用建议,使用时先使用git merge --ff-only
,如果合并失败才考虑是否要使用--no-ff
cherry-pick
// todo