git merge & git rebase 你用哪一个?

前言

git merge和git rebase都用来从一个分支获取内容并合并到当前分支,但它们采用不同的策略来进行「合并」操作。

开始之前,我们先来模拟一个日常开发中很常见的场景:
假设远程分支dev已经有了两个提交C1和C2,我们基于这个分支创建feature分支进行功能开发。

我们在自己的feature分支上进行开发,并生成了两个commit,分别是C5和C6。但是与此同时,dev分支上也有了一些新的修改并且进行了提交,生成了C3和C4。也就是说,dev分支和feature分支产生了“分叉”。

一般来说,feature分支是为了开发某些特定需求而从dev分支拉取出来的,开发完成后还需要「合并」回dev分支上。所以,这个时候就出现了两种选择:merge或rebase

 

git merge

执行以下命令:

git checkout feature
git merge dev

Git会根据两个分支的共同祖先(C2)和它们各自的最新提交(C4和C6)进行一个三方合并,然后将合并中修改的内容生成一个新的commit(merge commit: ),即下图中的C7。

 

git rebase

执行以下命令:

git checkout feature
git rebase dev

Git会从两个分支的共同祖先C2开始提取feature分支上的修改,即C5和C6,并把它们保存成临时补丁(存放在.git/rebase目录中)C5'和C6‘,再将feature分支更新为最新的dev分支,即C4处,然后将刚刚保存的补丁依次应用到feature分支上。rebase操作会“抹掉”feature分支上的C5和C6。

rebase操作不会生成一个merge commit,可以理解为把featrue分支(当前所在分支)上的修改在dev分支(目标分支)上复制了一个副本,操作完成后的版本历史是一个线性结构:

 

git log查看commit提交顺序

我们假设commit的提交时间C3早于C5早于C4早于C6,使用git log命令来查看它们的提交顺序(由新到旧),对于merge和rebase操作,显示的顺序是不同的。

  • merge操作显示的顺序:C7, C6, C4, C5, C3, C2, C1(按照commit提交的时间先后排序)

  • rebase操作显示的顺序:C6', C5', C4, C3, C2, C1(修改被直接提取到了分支前面)

可以看出,merge操作的结果是一个以提交时间为基准的 网状历史结构 ;而rebase操作显示的是一个 线性历史结构 

 

合并过程中的冲突处理

合并过程中如果遇到了冲突,merge和rebase的冲突处理各有不同。

  • merge:立即停止合并,待手动修改冲突成功并重新提交commit后,才能再次merge。

  • rebase:暂停合并,然后有三种选择1)手动解决冲突后通过git add和git rebase --continue继续应用余下的补丁进行合并;2)git rebase --skip忽略冲突(注意:此操作中当前分支的修改会直接覆盖目标分支的冲突部分)继续rebase;3)git rebase --abort 直接停止此次操作。

 

两种合并方式的比较

merge
优点:忠实记录了commit的情况,包括每个分支的详情。
缺点:多人开发时会和别人的分支提交交织在一起,代码历史树杂乱。

rebase
优点:不包含merge commit,可以得到一个干净简洁的项目历史。
缺点:因为重写了历史,如果合并出现代码bug不容易定位。

可以看出,merge和rebase操作的优缺点有互补的特征,孰优孰劣要根据具体的使用场景来决定,要根据项目和团队的开发需求来判断。

 

总结和一点小tips

  • merge 是一个合并操作,会将两个分支的修改合并在一起,同时产生一个merge commit;merge 的提交历史忠实地记录了实际发生过什么,关注点在真实的提交历史上面。

  • rebase 实质上并没有进行合并操作,只是将当前分支的修改副本复制到了目标分支的最新提交后面;rebase 的提交历史反映了项目过程中发生了什么,关注点在开发过程上面。

  • 不要在公共分支上使用rebase。即不要在已经 push 到远程,和其他人正在协作开发的分支上执行 rebase 操作。

  • 如果个人分支落后于主干分支,可以在个人分支上先执行rebase并且整理好commit,再回到主干分支去执行merge。

  • 在主干分支上执行合并时不应该存在冲突,且要记录合并过程,保留历史分支记录。

 

最后的碎碎念

分支管理是项目开发中很重要的一个环节。一个可读性强、整洁干净的项目历史可以使后续的维护、迭代工作节省大量时间和精力。因此,在日常开发中,要养成良好的commit习惯,每次push提交到远程仓库前记得整理一下现有的commit,若commit过于冗杂可以进行适当的合并。记住,你的代码并不是只写给自己看的。让团队中的其他人也能够轻松理解你的分支历史,尽量让git log graph的保持线性结构。

 

原文地址:https://www.cnblogs.com/barryyeee/p/13474511.html