Git代码冲突原理与三路合并算法

news2025/6/26 5:46:12

Git代码冲突原理

Git合并文件是以行为单位进行一行一行合并的,但是有些时候并不是两行内容不一样Git就会报冲突,这是因为Git会帮助我们进行分析得出哪个结果是我们所期望的最终结果。而这个分析依据就是三路合并算法。当然,三路合并算法并不能帮助我们绝对的避免冲突,当三路合并算法也不能帮助我们合并结果时,这个时候Git会将冲突交由开发者,由开发者进行人工干预得出最终合并结果。

1.1 两路合并算法

学习三路合并时我们先了解一下"两路合并"。两路合并算法就是将两个文件进行逐行对别,如果行的内容不同就报冲突,两路合并示意图如下图所示。

两路合并的弊端是非常大的,他几乎没有任何作用。因为在两路合并中缺少了一个比较基准,在两个分支进行合并时,只要两个文件有某一行不一样,那么合并时必定出现冲突,这显然是不友好的。

假设对于同一个文件,其中有一个人在分支上修改了内容,但是我们并没有修改文件内容,此时我们想要合并其他人刚刚修改的内容,我们当前版本的内容(Ours)和其他人当前版本的(Theirs)Git都认为正确的,最终Git只能让我们自己来处理这种冲突了,这种情况非常多且没有必要出现冲突。而这种情况产生的核心就是缺少比较基准,即不知道Ours和Theirs上一个版本是什么,无法得出Ours和Theirs有没有对上一个版本进行改动。

1.2 三路合并算法

三路合并是Git中用于解决分支间差异和冲突的核心算法。在Git进行分支合并时,它会寻找三个提交点:两个分支的HEAD(即当前提交)以及它们共同的最近祖先提交。这被称为“三路”:

  1. 共同祖先(Common Ancestor):这是两个分支合并前的最近共享提交
  2. 当前分支(Ours):即将合并到的分支,通常是你正在操作并想要合并其他分支到的分支
  3. 待合并分支(Theirs):你想要合并进当前分支的那个分支

三路合并算法的工作原理如下:

  • 对于每个文件,Git会对比这三个提交点(三路)中的内容。
  • 如果在共同祖先之后,两个分支对同一文件做出了不同的修改,那么就会出现冲突,Git会在合并过程中标记出这些冲突,并暂停合并,等待用户手动解决。
  • 如果双方对某个文件的修改不冲突(修改的内容是一致的),Git则能自动将这些更改合并在一起。

如下图,我们的代码(Ours)需要合并其他人的代码(Theris)的时候,Git会尝试找到这两次提交的共同祖先(Base),以共同祖先作为比较基准,如果一方相对于Base进行了修改,另一方相当于Base没有修改,那么此时合并成功,如果双方都相对于Base进行了修改,那么此时合并就会出现冲突。

如下图所示

代码演示如下:

rm -rf ./* .git		# 重新初始化仓库
git init
echo "Hello" >> aaa.txt
git add ./
git commit -m 'Hello' ./

git checkout -b test	# 创建并切换到一个新分支
vi aaa.txt				# 编辑为Hello World
cat aaa.txt
Hello World

git commit -m 'Hello World' ./	# 提交
git log --oneline		# 此时还是同轴开发路线
* 594456e (HEAD -> test) Hello World
* 2bd777a (master) Hello

git checkout master		
git merge test			# 属于快进合并(不会出现代码冲突)
cat aaa.txt
Hello World

如下图,在Ours合并Theirs时,双方都相对于比较基准Base进行了修改,那么此时合并就会出现冲突。我们不难发现,下图描述的其实是一个典型合并的场景。

代码演示如下:

rm -rf ./* .git					# 重新初始化仓库
git init		
echo "Hello" >> aaa.txt			
git add ./
git commit -m 'Hello' ./
git branch test

vi aaa.txt
cat aaa.txt
Hello Git

git commit -m 'Hello Git' ./
git log --oneline --all --graph	
* 1317c49 (HEAD -> master) Hello Git
* 75b8528 (test) Hello

git checkout test		# 切换到test分支开发
vi aaa.txt
cat aaa.txt
Hello World

git commit -m 'Hello World' ./
git log --oneline --all --graph			
* c7aefff (HEAD -> test) Hello World	# 产生分叉开发路线
| * 1317c49 (master) Hello Git
|/
* 75b8528 Hello

git checkout master			# 切换回master分支
git merge test				# 合并test分支(出现代码冲突)
Auto-merging aaa.txt
CONFLICT (content): Merge conflict in aaa.txt
Automatic merge failed; fix conflicts and then commit the result.

cat aaa.txt					# 查看冲突内容
<<<<<<< HEAD
Hello Git
=======
Hello World
>>>>>>> test

了解完上面的案例,我们可以把测试变为更加复杂,如下图。

通过上图我们可以得出如下规则。

  • 只有一方修改了同一个文件的同一行内容,则最终合并结果为修改过的内容
  • 双方都修改了同一文件的同一行内容:
    • 如果双方修改的内容一致,则最终合并结果为修改过的内容
    • 如果双方修改的内容不一致,则出现冲突

代码演示如下:

rm -rf ./* .git 
git init
echo "A1" >> aaa.txt
echo "B2" >> aaa.txt
echo "C3" >> aaa.txt
echo "C3" >> aaa.txt
echo "D4" >> aaa.txt
echo "D4" >> aaa.txt
echo "E5" >> aaa.txt
git add ./ 
git commit -m 'a' ./

git checkout -b test
echo "A1" > aaa.txt		# 注意 ">" 会清空文件
echo "B2" >> aaa.txt
echo "C3" >> aaa.txt
echo "C0" >> aaa.txt
echo "D4" >> aaa.txt
echo "D1" >> aaa.txt
echo "E0" >> aaa.txt
git commit -m 'b' ./

git checkout master
echo "A1" > aaa.txt		# 注意 ">" 会清空文件
echo "B0" >> aaa.txt
echo "C3" >> aaa.txt
echo "C0" >> aaa.txt
echo "D4" >> aaa.txt
echo "D0" >> aaa.txt
echo "E0" >> aaa.txt
git commit -m 'c' ./

# 合并test分支(产生冲突)
git merge test
Auto-merging aaa.txt
CONFLICT (content): Merge conflict in aaa.txt
Automatic merge failed; fix conflicts and then commit the result.

# 查看冲突文件
cat aaa.txt
A1
B0
C3
C0
D4
<<<<<<< HEAD		# 只有这一行出现了冲突
D0
=======
D1
>>>>>>> test
E0

通过这种三路合并策略,Git能够高效地处理大部分情况下的代码合并,同时确保开发者可以准确无误地解决任何出现的合并冲突,以维护项目历史的一致性和可追溯性。

通过三路合并算法,Git能够很灵活的帮助我们在一些情况下进行自动的代码合并,以及识别出代码是否冲突、冲突的部分等。但是Git底层判断文件差异的变更却是依赖于diff文件差异算法。也就是说,只有通过diff算法得出文件差异之后,才能够根据三路合并来进行下一步操作,例如是应该合并代码还是出现冲突以及冲突代码的识别等操作。这在某些情况下可能会出现一些细小的问题,例如我们分析下面案例。

通过我们之前分析的案例可以得出,冲突的只有第四行。

代码演示如下:

rm -rf ./* .git 
git init
echo "A1" >> aaa.txt
echo "B2" >> aaa.txt
echo "C3" >> aaa.txt
echo "D4" >> aaa.txt
echo "E5" >> aaa.txt
git add ./ 
git commit -m 'a' ./

git checkout -b test
echo "A1" > aaa.txt		# 注意 ">" 会清空文件
echo "B2" >> aaa.txt
echo "C0" >> aaa.txt
echo "D1" >> aaa.txt
echo "E0" >> aaa.txt
git commit -m 'b' ./

git checkout master
echo "A1" > aaa.txt		# 注意 ">" 会清空文件
echo "B0" >> aaa.txt
echo "C3" >> aaa.txt
echo "D0" >> aaa.txt
echo "E0" >> aaa.txt
git commit -m 'c' ./

# 合并test分支(产生冲突)
git merge test
Auto-merging aaa.txt
CONFLICT (content): Merge conflict in aaa.txt
Automatic merge failed; fix conflicts and then commit the result.

cat aaa.txt
A1
<<<<<<< HEAD
B0
C3
D0
=======
B2
C0
D1
>>>>>>> test
E0

但是我们实际测试得出,出现冲突的不仅仅是第四行,如下图所示。

为什么②和③也会出现冲突呢?这中间就存在了diff算法的影响,diff算法计算从①之后的代码大部分都发生了变更,并没有逐行去对比内容,而是抛出了一整块的代码冲突。这可能是Git出于性能的考虑,虽然这样的做法在某些情况下并不明智,但这并不会对我们的开发造成很大的影响。在绝大多数情况下,我们并不会对代码那几行出现了冲突很敏感,我们只要灵活的掌握如何处理代码冲突就能应对实际开发过程中的实际问题。

1.3 递归三路合并

三路合并为我们在合并分支时提供了基准(Base),这个基准就是要合并分支的共同祖先,但有时候两个分支之间的共同祖先存在多个,这个时候Git就会将这两个分支的共同祖先做一次虚拟合并,当做这两个分支的共同祖先。这种情况常见于交叉合并,如下图所示。

B、C先合并一次成为D,然后B、C再合并一次成为E,此时E、D存在多个共同祖先为B和C。此时E和D如果要进行合并,需要找到一个唯一的共同祖先,Git的做法是先将B和C这两个共同祖先做一次虚拟合并为X,以X节点作为E和D合并时的唯一共同祖先。然而在合并B和C时又需要找到B和C的共同祖先(A),如果此时B和C也存在多个共同祖先,那么同样先把B和C的共同祖先做一次虚拟合并成为一个唯一的共同祖先。这个过程就是递归三路合并。

下面我们通过代码来完成上述图中表示。

(1)初始化仓库。

rm -rf .git ./*
git init
echo 'A' >> aaa.txt
git add ./
git commit -m 'A' ./

(2)开发B版本。

echo 'B' >> aaa.txt
git commit -m 'B' ./

git log --oneline --all --graph
* 4bdf139 (HEAD -> master) B
* 18e222f A

(3)在A版本处建立分支,开发C版本。

git checkout -b test 18e222f		# 在A版本处建立分支
echo "C" >> aaa.txt					
git commit -m 'C' ./

git log --oneline --all --graph
* 940e119 (HEAD -> test) C
| * 4bdf139 (master) B
|/
* 18e222f A

(4)切换到master分支,合并test分支。相当于B合并C。

git checkout master			# 切换回master分支
git merge test				# 合并test分支,相当与B合并C,出现冲突
cat aaa.txt					# 查看冲突内容
A
<<<<<<< HEAD
B
=======
C
>>>>>>> test

vi aaa.txt					# 编辑文件(解决冲突)
cat aaa.txt
A
B
C

git add ./					
git commit -m 'D'
git log --oneline --all --graph
*   1262b32 (HEAD -> master) D
|\
| * 940e119 (test) C
* | 4bdf139 B
|/
* 18e222f A

(5)在B版本处建立一个新的分支,然后切换到该分支合并test分支。相当与B再合并一次C。

git checkout -b test-B 4bdf139		# 在B节点处建立一个新的分支
git log --oneline --all --graph
*   1262b32 (master) D
|\
| * 940e119 (test) C				# test分支的位置
* | 4bdf139 (HEAD -> test-B) B		# 新分支的位置
|/
* 18e222f A

git merge test						# 合并test分支,相当于B合并C,出现冲突
cat aaa.txt							# 查看冲突内容
A
<<<<<<< HEAD
B
=======
C
>>>>>>> test

vi aaa.txt							# 编辑文件(解决冲突)
cat aaa.txt							# 查看内容
A
C
B

git add./
git commit -m 'E'
git log --oneline --all --graph
*   9e610d9 (HEAD -> test-B) E			# E的祖先有B和C
|\
| | * 1262b32 (master) D				# D的祖先有B和C
| |/|
|/|/
| * 940e119 (test) C
* | 4bdf139 B
|/
* 18e222f A

(6)切换回master分支,合并test-B分支。相当于D合并E。

git checkout master		# 切换到master分支
git merge test-B		# 合并test-B分支,相当于D合并E,出现冲突
cat aaa.txt				# 查看冲突内容
A
<<<<<<< HEAD
B
C
=======
C
B
>>>>>>> test-B

我们结合代码和文件内容等一起来分析一下Git递归三路合并算法,如图所示。

E和D合并时寻找共同祖先,找到了B和C,接着B和C做一次虚拟合并为X,其结果如下:

A
<<<<< B
B
===== 
C
>>>>> C

本次X就是E和D合并时的共同祖先;Git将X节点冲突部分忽略,将剩余部分作为共同祖先的基准内容;因此,在D合并E时,出现如下内容:

A
<<<<<<< D
B
C
=======
C
B
>>>>>>> E

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

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

相关文章

Python第二语言(十四、高阶基础)

目录 1. 闭包 1.1 使用闭包注意事项 1.2 小结 2. 装饰器&#xff1a;实际上也是一种闭包&#xff1b; 2.1 装饰器的写法&#xff08;闭包写法&#xff09; &#xff1a;基础写法&#xff0c;只是解释装饰器是怎么写的&#xff1b; 2.2 装饰器的语法糖写法&#xff1a;函数…

AMD核显推理Stable Diffusion

目标 近期&#xff0c;我开始了尝试使用Stable Diffusion进行文生图和。为此&#xff0c;我也尝试了多种在线服务&#xff0c;如WHEE。虽然在线平台能够提供不错的生成效果&#xff0c;但是生成的图片太多的话最终还是需要收费的。 因此我想尝试在本地部署SD模型进行图像生成。…

使用Leaflet库创建交互式地图:技术解析与实践

一&#xff1a;引言 在现代Web开发中&#xff0c;地图可视化已成为许多项目不可或缺的一部分。Leaflet是一个开源的JavaScript库&#xff0c;用于在Web页面上创建交互式地图。它简单易用、轻量级且高度可定制&#xff0c;使得开发者能够快速地创建出具有丰富功能的地图应用。本…

提升你的编程体验:自定义 PyCharm 背景图片

首先&#xff0c;打开 PyCharm 的设置菜单&#xff0c;点击菜单栏中的 File > Settings 来访问设置&#xff0c;也可以通过快捷键 CtrlAItS 打开设置。 然后点击Appearance & Behavior > Appearance。 找到Background image...左键双击进入。 Image:传入自己需要设置…

Unity引擎在UI上渲染粒子播放

大家好&#xff0c;我是阿赵。   在UI上面显示粒子特效&#xff0c;如果把粒子系统直接拖到Canvas里面&#xff0c;会存在很多问题&#xff0c;比如层级问题、裁剪问题等。这里分享一种用MaskableGraphic和UIVertex来显示粒子特效的方法。 一、 MaskableGraphic和UIVertex简…

数据结构和矩阵细节用法:double、cell和complex #matlab

矩阵建立 建立矩阵用[]&#xff1b; 矩阵的同一行内的元素用逗号或者空格隔开&#xff1b; 矩阵的不同行的元素用分号隔开 eg. 矩阵 A 1 2 3 4 5 6 7 8 9 在matlab中矩阵A表示为&#xff1a; clc;clear; A[1,2,3;4,5,6;7,8,9]; %或者A[1 2 3;4 5 …

SAP FI 批量显示科目余额 发生额的报表

实际界面 结果 源代码 *&---------------------------------------------------------------------* *& Report ZRPT_FICO_F01 *&---------------------------------------------------------------------* *& 20240614 批量显示余额 发生额 *&---------…

怎么批量去除EXCEL表格内时间?(已解决)

作为竞价优化师&#xff0c;经常碰到下载表格以后发现有冗余数据&#xff0c;这个时候我们该怎么快速处理呢&#xff01; 第一步&#xff0c;选择这一行数据 第二步&#xff0c;右击选择“单元格格式-日期” 第三步&#xff0c;选择日期中的2001-3-7格式&#xff0c;点击“确定…

QML学习及实战

QML学习及实战&#xff08;更多内容&#xff09; 创建项目 3. 剩下的就是一路下一步即可 添加静态资源——图片 添加之后完成之后的路径 案列 || demo 可以参考的资料&#xff1a;https://github.com/gongjianbo/MyTestCode/blob/master/README.md 1. 文本省略号 Text {wi…

RedHat9 | Mariadb数据库的配置与管理

一、实验环境 1、Mariadb数据库介绍 MariaDB数据库管理系统是一个开源的关系型数据库管理系统&#xff0c;与MySQL高度兼容&#xff0c;并提供了更多的功能和性能优化。 起源和背景 MariaDB是MySQL的一个分支&#xff0c;主要由开源社区维护。由MySQL的创始人Michael Widen…

百度OCR初探-python

百度OCR使用&#x1f680; 在项目中需要对一些资料首先进行OCR识别&#xff0c;然后对OCR之后得到的结果进行结构化分析&#xff0c;各家的都打算简单尝试一下&#xff0c;首先尝试一下百度的OCR&#xff0c;首先找到百度的OCR的官方&#xff0c;开始自己搜索然后尝试。 OCR&am…

浅谈数据管理架构 Data Fabric(数据编织)及其关键特征、落地应用

伴随着企业从数字化转型迈向更先进的数智化运营新阶段&#xff0c;对看数、用数的依赖越来越强&#xff0c;但数据的海量增长给数据管理带来一系列难题&#xff0c;如数据类型和加工链路日益复杂&#xff0c;数据存储和计算引擎更加分散&#xff0c;数据需求响应与数据质量、数…

【电机控制】FOC算法验证步骤——PWM、ADC

【电机控制】FOC算法验证步骤 文章目录 前言一、PWM——不接电机1、PWMA-H-50%2、PWMB-H-25%3、PWMC-H-0%4、PWMA-L-50%5、PWMB-L-75%6、PWMC-L-100% 二、ADC——不接电机1.电流零点稳定性、ADC读取的OFFSET2.电流钳准备3.运放电路分析1.电路OFFSET2.AOP3.采样电路的采样值范围…

kubespray离线安装k8s

kubespray离线安装k8s 系统环境 OS: Static hostname: test Icon name: computer-vm Chassis: vm Machine ID: 22349ac6f9ba406293d0541bcba7c05d Boot ID: 83bb7e5dbf27453c94ff9f1fe88d5f02 Virtualization: vmware Operating System: Ubuntu 22.04.4 LTS Kernel: Linux 5.…

哪个牌子的开放式耳机性价比高呢?五大口碑销量双佳产品汇总!

​喜欢户外活动的朋友们&#xff0c;你们都是懂得享受生活的达人吧&#xff01;想象一下&#xff0c;在户外活动时&#xff0c;如果能有一副既适合场景又提供超棒音乐体验的耳机&#xff0c;那该多完美啊&#xff01;这时候&#xff0c;开放式耳机就闪亮登场了&#xff01;它的…

JAVAEE值之网络原理(1)_用户数据报协议(UDP)、概念、特点、结构、代码实例

前言 在前两节中我们介绍了UDP数据报套接字编程&#xff0c;但是并没有对UDP进行详细介绍&#xff0c;本节中我们将会详细介绍传输层中的UDP协议。 一、什么是UDP&#xff1f; UDP工作在传输层&#xff0c;用于程序之间传输数据的。数据一般包含&#xff1a;文件类型&#xff0…

SpringCloud2023 - 学习笔记

文章目录 1. 简介1.1 基础知识1.2 组件更替与升级 2. 微服务基础项目构建2.1 创建项目2.2 Mapper4生成代码2.3 支付模块编码2.4 项目完善2.5 订单模块编码2.6 工程重构 3. consul服务注册与发现3.1 consul简介3.2 consul下载安装3.3 微服务入驻3.4 order订单微服务入驻3.5 其他…

美摄科技匿名化处理解决方案,包含模糊、同色、马赛克、效果遮挡等各种形式

信息安全已成为企业发展中不可忽视的重要一环&#xff0c;随着信息安全法规的日益严格和公众对个人隐私保护意识的不断提高&#xff0c;企业如何在保障业务顺畅进行的同时&#xff0c;满足信息安全和隐私保护的要求&#xff0c;成为了亟待解决的问题。美摄科技凭借其强大的技术…

手把手搭建 Nginx + VIP + Keepalived 高可用集群

文章目录 1、前置讲解1.1、常用方案1.2、实现流程 2、集群搭建2.1、环境准备2.2、关于 Nginx2.2.1、安装 Nginx2.2.2、调整 Nginx 首页 2.3、关于 Keepalived2.3.1、安装 Keepalived2.3.2、编写 Shell2.3.3、调整 KeepAlived 配置 1、前置讲解 其实阿里云ECS本身不支持使用服务…

Kubernetes集群持久化部署实践

WordPress 网站持久化部署 要持久化MariaDB 可以把 Deployment 改成了 StatefulSet&#xff0c;修改 YAML添加“serviceName”“volumeClaimTemplates”这两个字段&#xff0c;定义网络标识和 NFS 动态存储卷&#xff0c;然后在容器部分用“volumeMounts”挂载到容器里的数据目…