git rebase与git merge图文详解(一文看懂区别)

news2025/4/2 8:02:34

git rebase与git merge图文详解

大家在工作中团队开发的时候对于拉取分支和合并代码时就会涉及到两种选择,git rebase与git merge:

  • rebase:变基,会有一个干净的分支,但是对于记录来源不够清晰
  • merge:合并,git分支看起来比较混乱,但是清楚各个记录的来源与时间节点

推荐:全部使用merge

  1. 拉公共分支使用最新代码:merge;有些公司会要求使用rebase,也就是git pull -r或git pull --rebase。这样的好处很明显,提交记录会比较简洁。但有个缺点就是rebase以后我就不知道我的当前分支最早是从哪个分支拉出来的了,因为基底变了嘛,所以看个人需求了。总体来说,即使是单机也不建议使用。
git fetch
git merge --ff-only
  1. 往公共分支上合代码merge;如果使用rebase,那么其他开发人员想看主分支的历史,就不是原来的历史了,历史已经被你篡改了。举个例子解释下,比如张三和李四从共同的节点拉出来开发,张三先开发完提交了两次然后merge上去了,李四后来开发完如果rebase上去(注意,李四需要切换到自己本地的主分支,假设先pull了张三的最新改动下来,然后执行<git rebase 李四的开发分支>,然后再git push到远端),则李四的新提交变成了张三的新提交的新基底,本来李四的提交是最新的,结果最新的提交显示反而是张三的,就乱套了,以后有问题就不好追溯了。

  2. 正因如此,大部分公司其实会禁用rebase,不管是拉代码还是push代码统一都使用merge,虽然会多出无意义的一条提交记录“Merge … to …”,但至少能清楚地知道主线上谁合了的代码以及他们合代码的时间先后顺序

1 git rebase

1.1 过程详解

首先我们通过简单的提交节点图解感受一下rebase在干什么

  1. 构造两个分支master和feature,其中feature是在提交点B处从master上拉出的分支
  2. master上有一个新提交M,feature上有两个新提交C和D

在这里插入图片描述
此时我们切换到feature分支上,执行rebase命令,相当于是想要把master分支合并到feature分支(这一步的场景就可以类比为我们在自己的分支feature上开发了一段时间了,准备从主干master上拉一下最新改动。模拟了git pull --rebase的情形)

# 将master上的分支合并到feature
# 这两条命令等价于git rebase master feature
git checkout feature
git rebase master

下图为变基后的提交节点图,解释一下其工作原理:
在这里插入图片描述

  • feature:待变基分支、当前分支
  • master:基分支、目标分支
  1. 官方解释(如果觉得看不懂可以直接看下一段):当执行rebase操作时,git会从两个分支的共同祖先开始提取待变基分支上的修改,然后将待变基分支指向基分支的最新提交,最后将刚才提取的修改应用到基分支的最新提交的后面。
  1. 结合例子解释:当在feature分支上执行git rebase master时,git会从master和featuer的共同祖先B开始提取feature分支上的修改,也就是C和D两个提交,先提取到。然后将feature分支指向master分支的最新提交上,也就是M。最后把提取的C和D接到M后面,注意这里的接法,官方没说清楚,实际是会依次拿M和C、D内容分别比较,处理冲突后生成新的C’和D’。一定注意,这里新C’、D’和之前的C、D已经不一样了,是我们处理冲突后的新内容,feature指针自然最后也是指向D’
  1. 通俗解释(重要!!):rebase,变基,可以直接理解为改变基底。feature分支是基于master分支的B拉出来的分支,feature的基底是B。而master在B之后有新的提交,就相当于此时要用master上新的提交来作为feature分支的新基底。实际操作为把B之后feature的提交先暂存下来,然后删掉原来这些提交,再找到master的最新提交位置,把存下来的提交再接上去(接上去是逐个和新基底处理冲突的过程),如此feature分支的基底就相当于变成了M而不是原来的B了。(注意,如果master上在B以后没有新提交,那么就还是用原来的B作为基,rebase操作相当于无效,此时和git merge就基本没区别了,差异只在于git merge会多一条记录Merge操作的提交记录)

1.2 工作场景

上面的例子可抽象为如下实际工作场景:远程库上有一个master分支目前开发到B了,张三从B拉了代码到本地的feature分支进行开发,目前提交了两次,开发到D了;李四也从B拉到本地的master分支,他提交到了M,然后合到远程库的master上了。此时张三想从远程库master拉下最新代码,于是他在feature分支上执行了git pull origin master:feature --rebase(注意要加–rebase参数),即把远程库master分支给rebase下来,由于李四更早开发完,此时远程master上是李四的最新内容,rebase后再看张三的历史提交记录,就相当于是张三是基于李四的最新提交M进行的开发了。(但实际上张三更早拉代码下来,李四拉的晚但提交早)

1.3 实际git提交示例

我这里严格按照上面的图解,构造了实际的git提交示例

如下图所示,ABM是master分支线,ABCD是feature分支线。
在这里插入图片描述
此时,在feature分支上执行git rebase master后,会提示有冲突,这里是关键,之前没有把这个细节说清楚。冲突其实也简单,因为我们要生成新的C’和D’嘛,那C’的内容如何得到呢?照搬C的?当然不是,C’的内容就是C和M两个节点的内容合并的结果,D’的内容就是D和M两个节点的内容合并的结果。我们手动处理冲突后,执行如下命令即可:

# 先处理完C,会继续报D的冲突,所以下面命令一共会执行两次
git add file
git rebase --continue

变基完成以后如下图所示,ABM还是没变化,ABMC’D’是rebase完成后的feature节点图,个人认为讲到这里就还是比较清楚了
在这里插入图片描述

2 git merge

git merge有好几种不同的模式,下面我将针对这几种模式分别解释。

git merge 应该是开发者最常用的 git 指令之一,默认情况下你直接使用 git merge 命令,没有附加任何选项命令的话,那么应该是交给 git 来判断使用哪种 merge 模式,实际上 git 默认执行的指令是 git merge -ff 指令(默认值)

  • 对于专业的开发者来说,你可能无须每次合并都指定合并模式(如果需要的话还是要指定的),但是你可能需要知道 git 在背后为你默认做了什么事情,这样才能保证你的代码万无一失。

2.1 fast-forward(–ff):master与feature存在公共祖先

开发者小王接到需求任务,从 master 分支中创建功能分支,git 指令如下:

git checkout -b feature556
Switched to a new branch 'feature556'

小王在 feature556 分支上完成的功能开发工作,然后产生1次 commit,

git commit -m 'Create pop up effects'
[feature556 6104106] create pop up effects
 3 files changed, 75 insertions(+)

我们再更新一下 README 自述文件,让版本差异更明显一些

git commit -m `updated md`

这时候我们看看当前分支的 git 历史记录,输入 git log --online -all 可以看到全部分支的历史线:

f2c9c7f (HEAD -> feature556) updated md
6104106 create pop up effects
a1ec682 (origin/main, origin/HEAD, main) import dio
c5848ff update this readme
8abff90 update this readme

在这里插入图片描述
功能完成后自然要上线,我们把代码合并,完成上线动作,代码如下

git checkout master
git merge feautre556
Updating a1ec682..38348cc
Fast-forward
  .......  | 2+++
 1 file changed, 2 insertions(+)

如果你注意上面的文字的话,你会发现 git 帮你自动执行了 Fast-forward 操作,那么什么是 Fast-forward ?

Fast-forward 是指 Master 合并 Feature 时候发现 Master 当前节点一直和 Feature 的根节点相同,没有发生改变,那么 Master 快速移动头指针到 Feature 的位置,所以 Fast-forward 并不会发生真正的合并,只是通过移动指针造成合并的假象,这也体现 git 设计的巧妙之处。合并后的分支指针如下:
在这里插入图片描述
通常功能分支(feature556) 合并 master 后会被删除,通过下图可以看到,通过 Fast-forward 模式产生的合并可以产生干净并且线性的历史记录:
在这里插入图片描述

2.2 non-Fast-forward(–no-ff):master与feature不存在公共祖先

刚说了会产生 Fast-forward 的情况,现在再说说什么情况会产生 non-Fast-forward,通常,当合并的分支跟 master 不存在共同祖先节点的时候,这时候在 merge 的时候 git 默认无法使用 Fast-forward 模式,
我们先看看下图的模型:

在这里插入图片描述
可以看到 master 分支已经比 feature001 快了2个版本,master 已经没办法通过移动头指针来完成 Fast-forward,所以在 master 合并 feature001 的时候就不得不做出真正的合并,真正的合并会让 git 多做很多工作,具体合并的动作如下:

  • 找出 master 和 feature001 的公共祖先,节点 c1,c6, c3 三个节点的版本 (如果有冲突需要处理)
  • 创建新的节点 c7,并且将三个版本的差异合并到 c7,并且创建 commit
  • 将 master 和 HEAD 指针移动到 c7

补充🏡:大家在 git log 看到很多类似:Merge branch 'feature001' into master 的 commit 就是 non-Fast-forward 产生的。
执行完以上动作,最终分支流程图如下:
在这里插入图片描述

2.3 fast-forward only(–ff-only):尝试–ff方式合并,如果不满足则退出

表明只有在

先简单介绍一下 git merge 的三个合并参数模式:

  • -ff 自动合并模式:当合并的分支为当前分支的后代的,那么会自动执行 --ff (Fast-forward) 模式,如果不匹配则执行 --no-ff(non-Fast-forward) 合并模式
  • –no-ff 非 Fast-forward 模式:在任何情况下都会创建新的 commit 进行多方合并(及时被合并的分支为自己的直接后代)
  • –ff-onlu Fast-forward 模式:只会按照 Fast-forward 模式进行合并,如果不符合条件(并非当前分支的直接后代),则会拒绝合并请求并且退出

2.4 三种模式选择

三种 merge 模式没有好坏和优劣之分,只有根据你团队的需求和实际情况选择合适的合并模式才是最优解,那么应该怎么选择呢? 我给出以下推荐:

  1. 如果你是小型团队,并且追求干净线性 git 历史记录,那么我推荐使用 git merge --ff-only 方式保持主线模式开发是一种不错的选择
  2. 如果你团队不大不小,并且也不追求线性的 git 历史记录,要体现相对真实的 merge 记录,那么默认的 git --ff 比较合适
  3. 如果你是大型团队,并且要严格监控每个功能分支的合并情况,那么使用 --no-ff 禁用 Fast-forward 是一个不错的选择

3 区别及推荐

①区别

  • rebase:变基,会有一个干净的分支,但是对于记录来源不够清晰,commit的提交先后顺序也会比较错乱。(rebase以后我就不知道我的当前分支最早是从哪个分支拉出来的了,因为基底变了)
    在这里插入图片描述
  • merge(推荐使用):合并,git分支看起来比较混乱,但是清楚各个记录的来源与时间节点
    在这里插入图片描述

搞来搞去那么多,这其实是最重要的。不同公司,不同情况有不同使用场景,不过大部分情况推荐如下:

②推荐:全部使用merge

  1. 拉公共分支使用最新代码:merge;有些公司会要求使用rebase,也就是git pull -r或git pull --rebase。这样的好处很明显,提交记录会比较简洁。但有个缺点就是rebase以后我就不知道我的当前分支最早是从哪个分支拉出来的了,因为基底变了嘛,所以看个人需求了。总体来说,即使是单机也不建议使用。
git fetch
git merge --ff-only
  1. 往公共分支上合代码merge;如果使用rebase,那么其他开发人员想看主分支的历史,就不是原来的历史了,历史已经被你篡改了。举个例子解释下,比如张三和李四从共同的节点拉出来开发,张三先开发完提交了两次然后merge上去了,李四后来开发完如果rebase上去(注意,李四需要切换到自己本地的主分支,假设先pull了张三的最新改动下来,然后执行<git rebase 李四的开发分支>,然后再git push到远端),则李四的新提交变成了张三的新提交的新基底,本来李四的提交是最新的,结果最新的提交显示反而是张三的,就乱套了,以后有问题就不好追溯了。

  2. 正因如此,大部分公司其实会禁用rebase,不管是拉代码还是push代码统一都使用merge,虽然会多出无意义的一条提交记录“Merge … to …”,但至少能清楚地知道主线上谁合了的代码以及他们合代码的时间先后顺序

参考文章:https://blog.csdn.net/weixin_42310154/article/details/119004977

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1085658.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

有未经处理的异常: 0xC00000FD: Stack overflow 问题解决

Visual Studio 调试运行程序时出现xxx处有未经处理的异常(在 yyy.exe 中): 0xC00000FD: Stack overflow (参数: 0x0000000000000001, 0x000000015C203000) 报错 解决方法如下&#xff1a; 在属性 > 配置属性 > 链接器 > 系统 > 堆栈保留大小及堆栈提交大小&#…

C语言,标志法

标志法通常用来检查或者进行过程中一些状态变化。 有一些是为了观察变化&#xff0c;举出一些以往代码的例子&#xff1a; 1.找出一串数字中没有重复出现过的数字 #include <stdio.h> int main() {int arr[1000] { 0 };int n 0;scanf("%d", &n);int i…

布局--QT Designer

一、在我们使用Qt做界面设计时&#xff0c;为了界面的整洁美观&#xff0c;往往需要对界面中的所有控件做一个有序的排列&#xff0c;以及设置各个控件之间的间距等等&#xff0c;为此Qt为界面设计提供了基本布局功能&#xff0c;使用基本布局可以使组件有规则地分布。 1.1 基…

项目管理的优秀软件推荐,助力提升团队效能!

我们知道&#xff0c;每个产品在上市的过程中都需要经历市场调研、研发设计、功能测试、上市评估、营销推广等阶段。作为项目经理&#xff0c;最关键的任务不仅是确保产品的顺利孵化和上市&#xff0c;还有管理团队。我们研究了许多项目管理用户&#xff0c;工作的难点是如何在…

软件测试学习(三)易用性测试、测试文档、软件安全性测试、网站测试

目录 易用性测试 用户界面测试 优秀Ul由什么构成 符合标准和规范 直观 一致 灵活 舒适 正确 实用 为有残疾障碍的人员测试&#xff1a;辅助选项测试 测试文档 软件文档的类型 文档测试的重要性 软件安全性测试 了解黑客的动机 威胁模式分析 网站测试 网页基…

springBoot依赖管理机制

springBoot依赖管理机制 1、为什么导入starter-web所有相关依赖都导进来&#xff1f;2、为什么版本号都不用写&#xff1f;3、自定义版本号利用maven的就近原则 4、第三方的jar包 1、为什么导入starter-web所有相关依赖都导进来&#xff1f; 开发是什么场景导入什么场景启动器…

苍穹外卖(一)

苍穹外卖项目介绍 项目介绍 本项目&#xff08;苍穹外卖&#xff09;是专门为餐饮企业&#xff08;餐厅、饭店&#xff09;定制的一款软件产品&#xff0c;包括 系统管理后台 和 小程序端应用 两部分。其中系统管理后台主要提供给餐饮企业内部员工使用&#xff0c;可以对餐厅…

ACDSee Photo Studio Ultimate 2024特别版(图片编辑器)

ACDSee Photo Studio Ultimate 2024是一款功能全面、易于使用的图像编辑和管理软件&#xff0c;为摄影师和设计师提供了强大的工具和功能。无论您是进行基本的图像优化还是进行复杂的创作&#xff0c;ACDSee Photo Studio Ultimate 2024都将成为您的得力助手。 软件下载&#x…

u-boot 编译与运行

文章目录 u-boot 编译与运行环境配置ubuntu 版本qemu 版本u-boot 版本&#xff08;master&#xff09;交叉工具链版本 u-boot 源码下载编译情况一情况2编译报错解决编译日志编译产物 运行 u-boot 编译与运行 本文主要介绍 u-boot 编译&#xff0c;即 qemu 如何运行 u-boot 环…

堆1111111111111111

1)一个java进程对应这个一个JVM实例&#xff0c;Runtime&#xff0c;就对应着一个运行时数据区&#xff0c;一个进程中的多个线程&#xff0c;共享同一份堆空间和方法区&#xff0c;而栈和程序计数器使每一个线程私有的 2)通过-Xms10m -Xmx10m是初始堆空间和最大堆空间 3)堆空间…

Ceph介绍与部署

Ceph介绍与部署 一、存储基础1.1、单机存储设备1.1.1、单机存储的问题 1.2、商业存储解决方案1.3、分布式存储&#xff08;软件定义的存储 SDS&#xff09;1.3.1、分布式存储的类型 二、Ceph 简介三、Ceph 优势四、Ceph 架构五、Ceph 核心组件5.1、Pool中数据保存方式支持两种类…

LeetCode2562

public static long jointArrayData(int[] nums) {//定义变量存放数据long num 0;//头尾“相加”for (int i 0,j nums.length-1; i < j; i,j--) {if (i!j){//String.valueOf先将Int转String&#xff0c;进行字符串的连接&#xff0c;再用Integer.parseInt方法转回IntStri…

Qt Core篇 后端上位机界面开发

Qt Core篇 后端上位机界面开发 Qt Core 我选择了Qt,依旧度日如年&#xff0c;简单发布一篇&#xff0c;代表我还活着 Qt Core Qt Core是Qt框架的核心模块之一&#xff0c;它提供了一套跨平台的C类库&#xff0c;用于处理事件循环、线程、文件和目录操作、数据类型、日期和时间…

PTA 7-5 令人抓狂的四则运算

题目 曾记否&#xff0c;我们小学时&#xff0c;遇到这种四则运算&#xff0c;心情是抓狂的&#xff1a; 那么当我们学会使用计算机&#xff0c;自然是要程序去完成这个工作啦~ 现在请对输入的四则运算求值。注意&#xff1a; 四则运算表达式必定包含运算数&#xff0c;还可能…

2023NOIP A层联测10-子序列

给定一个长为 n n n 的仅有小写英文字母构成字符串 S S 1 S 2 ⋯ S n SS_1S_2\cdots S_n SS1​S2​⋯Sn​。我们定义一个字符串是好的&#xff0c;当且仅当它可以用两个不同的字母 x 和 y 表示成 xyxyxyx... 的形式。例如&#xff0c;字符串 abab、tot、z 是好的&#xff0c…

3.4 数据查询

思维导图&#xff1a; 前言&#xff1a; --- ### 概述 - 数据查询是数据库操作的核心&#xff0c;主要通过SQL中的SELECT语句来实现。 - SELECT语句提供了非常丰富的查询功能&#xff0c;包括单表查询、多表连接查询和嵌套查询等。 ### SELECT语句的基本格式 SELECT [ALL|D…

【MySQL】存储引擎简介、存储引擎特点、存储引擎区别

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 Redis 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 MySQL 一、MySQL体系结构二、存储引擎简介三…

zookeeper应用场景(一)

一、zookeeper客户端api 1、官方Java客户端api 引入zookeeper client依赖 <dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.9.0</version> </dependency> 1&#xff09…

解析项目管理任务跟踪器,助力项目进展掌握!

什么是项目管理任务跟踪器&#xff1f;项目管理任务跟踪器是项目经理简化计划、组织和执行项目任务直至完成的重要工具。该工具可帮助他们掌握需要完成的工作、确定收到的工作请求的优先级、完成项目并在预算范围内按时实现目标。 除了布置和跟踪任务之外&#xff0c;项目管理任…

unity2022版本 实现加减进度条

简介 在现代游戏开发中&#xff0c;用户界面 (UI) 扮演着至关重要的角色&#xff0c;它不仅为玩家提供信息&#xff0c;还增强了游戏的可玩性。加减进度条是一种常见的UI元素&#xff0c;它可以用于显示游戏中的进度、倒计时、资源管理和其他关键信息。在这篇博客中&#xff0…