当前位置: 首页 > news >正文

在开发过程中使用 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,这种是快进式合并。结束后,历史树是一条线。

相关文章:

  • Visual Studio 2019 远程调试工具(Remote Debugger)使用方法
  • Windbg使用说明书
  • 使用Windbg查看CrashDump
  • 【无标题】windbg 分析dump文件
  • Dump文件有三种:完整内存转储,内核内存转储,小内存转储。System Properties中的高级选项中可以看到这些设置。
  • Analysing crash dump in windbg
  • 【无标题】使用VS调试DUMP文件
  • 使用VS调试Dump文件
  • 【无标题】dump解析入门-用VS解析dump文件进行排障
  • Crash Dump调试:Symbol Server/Source Server、PDB原理分析
  • [笔记]Ray Tracing with Cones
  • bundletool 工具使用
  • 123456789
  • Visual Studio调试器指南---自动启动调试器
  • 在启动时无法再使用vsjitdebugger来调试进程
  • 「面试题」如何实现一个圣杯布局?
  • android 一些 utils
  • Angular2开发踩坑系列-生产环境编译
  • Bytom交易说明(账户管理模式)
  • E-HPC支持多队列管理和自动伸缩
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • Java编程基础24——递归练习
  • Java读取Properties文件的六种方法
  • Js基础知识(四) - js运行原理与机制
  • Spring Cloud中负载均衡器概览
  • 程序员最讨厌的9句话,你可有补充?
  • 机器学习中为什么要做归一化normalization
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 检测对象或数组
  • 如何胜任知名企业的商业数据分析师?
  • 如何实现 font-size 的响应式
  • 微服务框架lagom
  • 小程序 setData 学问多
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • const的用法,特别是用在函数前面与后面的区别
  • ​比特币大跌的 2 个原因
  • #1014 : Trie树
  • #NOIP 2014# day.2 T2 寻找道路
  • #WEB前端(HTML属性)
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (2009.11版)《网络管理员考试 考前冲刺预测卷及考点解析》复习重点
  • (3)选择元素——(14)接触DOM元素(Accessing DOM elements)
  • (板子)A* astar算法,AcWing第k短路+八数码 带注释
  • (二)Linux——Linux常用指令
  • (二)学习JVM —— 垃圾回收机制
  • (附源码)ssm高校升本考试管理系统 毕业设计 201631
  • (附源码)ssm跨平台教学系统 毕业设计 280843
  • (转)ABI是什么
  • .md即markdown文件的基本常用编写语法
  • .net web项目 调用webService
  • .NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect),解决不同版本 dll 的依赖问题
  • .Net 知识杂记
  • .net 逐行读取大文本文件_如何使用 Java 灵活读取 Excel 内容 ?
  • .NET/C# 使用 SpanT 为字符串处理提升性能