在开发过程中使用 git rebase 还是 git merge,优缺点分别是什么?

在开发过程中使用 git rebase 还是 git merge,优缺点分别是什么?

在开发过程中使用 git rebase 还是 git merge,优缺点分别是什么? - 知乎 (zhihu.com)

作者:一个小号
链接:https://www.zhihu.com/question/36509119/answer/1990894567
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 

rebase 和 merge 根本不是二选一的关系,要协同使用。如果你听到任何人说我只用 rebase/merge 那一定是他不懂。

一个最简单的模型,从 master 分支 checkout 出几个本地 feature 分支,你或者你的团队在协同开发某个 feature-a 时,可能别人已把 feature-b 的代码 merge 回 master 了,所以应该及时将 master 的改动 rebase 到你的本地分支,顺便 fix conflicts。即:

$ git switch feature-a
$ git rebase master
fix conflicts...
$ git rebase --continue

当你开发完成 feature-a 时,应该将改动 merge 回 master。即:

$ git switch master
$ git merge --no-ff -m "Merge branch 'feature-a'" feature-a

在本地分支中使用 rebase 来合并主分支的改动,是为了让你的本地提交记录清晰可读。(当然, rebase 不只用来合并 master 的改动,还可以在协同开发时 rebase 队友的改动。)

在主分支中使用 merge 来把 feature 分支的改动合并进来,是为了保留分支信息。

如果全使用 merge 就会导致提交历史繁复交叉,错综复杂。如果全使用 rebase 就会让你的 commits history 变成一条光秃秃的直线。

一个好的 commits history,应该是这样的:

*   e2e6451 (HEAD -> master) feture-c finished
|\
| * 516fc18 C.2
| * 09112f5 C.1
|/
*   c6667ab feture-a finished
|\
| * e64c4b6 A.2
| * 6058323 A.1
|/
*   2b24281 feture-b finished
|\
| * c354401 B.4
| * 4bfefb8 B.3
| * eb13f72 B.2
| * c2c62b9 B.1
|/
* bbbba82 init

而不是这样的:

*   9f0c13b (HEAD -> master) feture-c finished
|\
| * 55be61c C.2
| *   e18b5c5 merge master
| |\
| |/
|/|
* |   ee549c2 feture-a finished
|\ \
| * | 51f2126 A.3
| * |   72118e2 merge master
| |\ \
| |/ /
|/| |
* | |   6cb16a0 feture-b finished
|\ \ \
| * | | 7b27b77 B.3
| * | | 3aac8a2 B.2
| * | | 2259a21 B.1
|/ / /
| * | 785fab7 A.2
| * | 2b2b664 A.1
|/ /
| * bf9e77f C.1
|/
* 188abf9 init

也不是这样的:

* b8902ed (HEAD -> master) C.2
* a4d4e33 C.1
* 7e63b80 A.3
* 760224c A.2
* 84b2500 A.1
* cb4c4cb B.3
* 2ea8f0d B.2
* df97f39 B.1
* 838f514 init

就这么简单。

编辑于 2021-07-11 09:42

​赞同 102​​5 条评论

​分享

​收藏​喜欢收起​

更多回答

知乎用户

638 人赞同了该回答

搞清楚这个问题首先要搞清楚merge和rebase背后的含义。

先看merge,官方文档给的说明是:

git-merge - Join two or more development histories together

顾名思义,当你想要两个分支交汇的时候应该使用merge。

根据官方文档给的例子,是master merge topic,如图:

                     A---B---C topic
                    /         \
               D---E---F---G---H master

然而在实践中,在H这个commit上的merge经常会出现merge conflict。为了避免解决冲突的时候引入一些不必要的问题,工程中一般都会规定no conflict merge。比如你在github上发pull request,如果有conflict就会禁止merge。

所以才会有题主问的问题:在当前的topic分支,想要引入master分支的F、G commit上的内容以避免merge conflict,方便最终合并到master。

这种情况下用merge当然是一个选项。用merge代表了topic分支与master分支交汇,并解决了所有合并冲突。然而merge的缺点是引入了一次不必要的history join。如图:

                     A--B--C-X topic
                    /       / \
               D---E---F---G---H master

其实仔细想一下就会发现,引入master分支的F、G commit这个问题上,我们并没有要求两个分支必须进行交汇(join),我们只是想避免最终的merge conflict而已。

rebase是另一个选项。rebase的含义是改变当前分支branch out的位置。这个时候进行rebase其实意味着,将topic分支branch out的位置从E改为G,如图:

                             A---B---C topic
                            /         
               D---E---F---G master

在这个过程中会解决引入F、G导致的冲突,同时没有多余的history join。但是rebase的缺点是,改变了当前分支branch out的节点。如果这个信息对你很重要的话,那么rebase应该不是你想要的。rebase过程中也会有多次解决同一个地方的冲突的问题,不过可以用squash之类的选项解决。个人并不认为这个是rebase的主要问题。


 

综上,其实选用merge还是rebase取决于你到底是以什么意图来避免merge conflict。实践上个人还是偏爱rebase。一个是因为branch out节点不能改变的情况实在太少。另外就是频繁从master merge导致的冗余的history join会提高所有人的认知成本。

发布于 2016-11-16 11:48

​赞同 638​​31 条评论

​分享

​收藏​喜欢收起​

Elpie Kay

调参码农

110 人赞同了该回答

编辑说明:这个答案目前还有人点赞,所以想要完善下,之前的答案有些问题。添加上图片,让说明更加直观。截图来自于网站Learn Git Branching因为Git是分布式的并且本地仓库和远程仓库可以看作一个整体,所以将本地分支和服务器上的分支显示在同一张图上做说明。本地分支是master,服务器分支是server_ma。

不管用什么风格的操作流程来整合你的本地分支,最终,服务器上的目标分支是要和本地分支做一次类似于fast-forward merge的合并的。

通常情况下有这么两种情形:

1. 本地分支是C0-C1-C2-C3,服务器上是C0-C1。这时候直接push,服务器上也会变成C0-C1-C2-C3,这种是快进式合并。结束后,历史树是一条线。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值