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