赞
踩
在开发过程中使用 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
就这么简单。
赞同 1025 条评论
分享
收藏喜欢收起
更多回答
知乎用户
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会提高所有人的认知成本。
赞同 63831 条评论
分享
收藏喜欢收起
调参码农
110 人赞同了该回答
编辑说明:这个答案目前还有人点赞,所以想要完善下,之前的答案有些问题。添加上图片,让说明更加直观。截图来自于网站Learn Git Branching。因为Git是分布式的并且本地仓库和远程仓库可以看作一个整体,所以将本地分支和服务器上的分支显示在同一张图上做说明。本地分支是master,服务器分支是server_ma。
不管用什么风格的操作流程来整合你的本地分支,最终,服务器上的目标分支是要和本地分支做一次类似于fast-forward merge的合并的。
通常情况下有这么两种情形:
1. 本地分支是C0-C1-C2-C3,服务器上是C0-C1。这时候直接push,服务器上也会变成C0-C1-C2-C3,这种是快进式合并。结束后,历史树是一条线。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。