实例解释Git解决的冲突的原理
郝伟 2022/03/08

1 简介

使用Git已经有六七年的时间了,每次主要使用的内容都是将变更通过Git进行提交,然后推送到云端,每次在工作之前会把更新的东西拉回本地,再继续编辑,经常有时候会出现冲突的情况,但是由于自己的懒惰,每次都是将变更的东西先手工保存,然后撤销变更,再将远程拉回本地,然后再将冲突的部分再保存回来。

这种方式方式虽然能够解决冲突,但是完全违背了Git的使用原则。Git作为一款优秀的版本控制系统,除了基本的版本控制,版本冲突就是它所要解决的一个重要的问题,并且Git已经很好地解决了这个问题,在不同的编译器中都可以使用。遗憾地是,笔者在这么多年的使用都没有把Git自带的版本冲突功能真正用起来,所以本次将在办公室和在家使用同一个文档产生冲突过以后为例,记录整个解决方法的过程,并提取解决的方法,以便后续使用。

2 Git 基本工作原理

Git在版本控制时,大多数人的理解的最基本的单元是文件,即基于文件的版本控制。实际上,这个颗粒度过大了。我们首先要知道Git的设计理念是用于对代码的管理,再一般一些是对文本文件的管理,因为代码都是基于文本的,而不是二进制的文件。由于文本文件可以分为多个行,所以Git的管理理念最重要的理念就是基于的版本控制管理。

下面我们来看下一个完整的示例来演示基于行的Git是如何工作的。

2.1 添加行

首先,我们添加了一个新文件,内容如下:

Jack
Tom
Lucy

则通过 diff 查看变量时(下同),会出现以下内容:

+ Jack
+ Tom
+ Lucy

加号表示新添加的行,由于是新文件,所以每行都是新添加的。

2.2 删除行

如果我们直接删除行,Git就可以准确定位并记录删除的行,比如我们删除第2行Tom,得到以下内容:

Jack
Lucy

可以看到,第2行Tom会被记录下来。

  Jack
- Tom
  Lucy

2.3 修改行

Jack
Lucy1
  Jack
- Lucy
+ Lucy1

之所以Lucy行的变化是先删除Lucy,再添加Lucy\n的方式实现的。所以我们可以得到一个重要的结论:在Git中只有添加和删除,没有直接修改的概念。修改是通过先删除原行,再添加修改行的方式实现的。所以,我们可以在文本文件中的任意位置添加一行,在变更记录中,会在对应的行显示添加的内容。

2.4 追加行

现在,我们在文件尾追加1行数据,变更后全部内容如下:

Jack
Lucy1
Brown

再查看变更内容如上:

  Jack
- Lucy1
+ Lucy1
+ Brown

最后一行Brown是新增加的没问题,但是为什么第2行的Lucy也发生了修改呢?实际上这是由于换行符造成的。
对于Git而言,在行进行拆分时,是包括换行符 \n 的,原文件的内容实际如下:

Jack\n
Lucy

在添加了新行 Brown 后,内容变更如下:

Jack\n
Lucy\n
Brown

可见,第2行的内容末尾在变更后,添加了 \n,所以内容是发生了变化。

2.5 小结

Git的工作原理是基于文件的行的变更管理,能够以对指定行进行添加和删除两种操作的变更记录,从而实现对文件更为颗粒化的管理。

3 版本冲突场景

当两名程序员对同一个文件进行修改,再提交至远程。

版本冲突的常见场景有2个:

其中尤其以场景1最为常见。
为了方便描述,现在两名程序员分别为A和B。

3.1 修改的行不同

仍然以上面的文件为例:

Jack
Tom
Lucy

由于不管的管理是以行进行的,同时行是有序号的,所以对行的内容的修改只要不是同一行就不会有冲突,比如:A先添加了一行 Brown,B删除了第1行,那么无论是谁先提交,最后合并后,内容都是:

Tom
Lucy
Brown

整体变更内容为:

- Jack
  Tom
- Lucy
+ Lucy
+ Brown

3.2 修改相同行

如果两人修改了相同行,那么部会产生冲突,比如现在对第1行 Tom 进行修改,A修改为 Tom A, 而B修改为 Tom B。然后A本地提交后,先同步到远程,版本号为 0d0e3be3dc6cacb0a37c22af6188cf2990b6c467。B本地提交,然后再同步时就会产生冲突,左侧会提示冲突的文件,同时会要求对冲突文件进行合并。

Merge branch 'master' of http://www.gitee.com/hwaust/MyNotes

合并变更
  a.txt
暂存的变更
  file1
  file2
  ...

再打开冲突文件,可以看到以下内容:

Accept Current Change | Accept Incoming Change | Accept Both Changes | Compare Changes
<<<<<<< HEAD (Current Change)
Tom B
=======
Tom A
>>>>>>> 0d0e3be3dc6cacb0a37c22af6188cf2990b6c467 (Incoming Change)

这是VSCode下的模型模型:

<<<<<<< 当前版本
本地内容
==
远程内容
>>>>>>> 远程版本号

上方还有4条命令:

需要哪个版本直接点击对应的命令即可,而且特别友好的一点,如果点错了,可以使用 Ctrl-z 撤回后再重新选择。

3.3 小结

通过以上测试,可以发现,Git在版本控制时即使是对同一个文件管理,以行的理念都会使管理变得非常容易。在冲突管理时会出现两种情况:

4 参考资料

[1] Git冲突与解决方法, https://www.cnblogs.com/gavincoder/p/9071959.html