实例解释Git解决的冲突的原理
郝伟 2022/03/08
使用Git已经有六七年的时间了,每次主要使用的内容都是将变更通过Git进行提交,然后推送到云端,每次在工作之前会把更新的东西拉回本地,再继续编辑,经常有时候会出现冲突的情况,但是由于自己的懒惰,每次都是将变更的东西先手工保存,然后撤销变更,再将远程拉回本地,然后再将冲突的部分再保存回来。
这种方式方式虽然能够解决冲突,但是完全违背了Git的使用原则。Git作为一款优秀的版本控制系统,除了基本的版本控制,版本冲突就是它所要解决的一个重要的问题,并且Git已经很好地解决了这个问题,在不同的编译器中都可以使用。遗憾地是,笔者在这么多年的使用都没有把Git自带的版本冲突功能真正用起来,所以本次将在办公室和在家使用同一个文档产生冲突过以后为例,记录整个解决方法的过程,并提取解决的方法,以便后续使用。
Git在版本控制时,大多数人的理解的最基本的单元是文件,即基于文件的版本控制。实际上,这个颗粒度过大了。我们首先要知道Git的设计理念是用于对代码的管理,再一般一些是对文本文件的管理,因为代码都是基于文本的,而不是二进制的文件。由于文本文件可以分为多个行,所以Git的管理理念最重要的理念就是基于行的版本控制管理。
下面我们来看下一个完整的示例来演示基于行的Git是如何工作的。
首先,我们添加了一个新文件,内容如下:
Jack
Tom
Lucy
则通过 diff 查看变量时(下同),会出现以下内容:
+ Jack + Tom + Lucy
加号表示新添加的行,由于是新文件,所以每行都是新添加的。
如果我们直接删除行,Git就可以准确定位并记录删除的行,比如我们删除第2行Tom,得到以下内容:
Jack
Lucy
可以看到,第2行Tom会被记录下来。
Jack - Tom Lucy
Jack
Lucy1
Jack - Lucy + Lucy1
之所以Lucy行的变化是先删除Lucy,再添加Lucy\n的方式实现的。所以我们可以得到一个重要的结论:在Git中只有添加和删除,没有直接修改的概念。修改是通过先删除原行,再添加修改行的方式实现的。所以,我们可以在文本文件中的任意位置添加一行,在变更记录中,会在对应的行显示添加的内容。
现在,我们在文件尾追加1行数据,变更后全部内容如下:
Jack
Lucy1
Brown
再查看变更内容如上:
Jack - Lucy1 + Lucy1 + Brown
最后一行Brown是新增加的没问题,但是为什么第2行的Lucy也发生了修改呢?实际上这是由于换行符造成的。
对于Git而言,在行进行拆分时,是包括换行符 \n 的,原文件的内容实际如下:
Jack\n
Lucy
在添加了新行 Brown 后,内容变更如下:
Jack\n
Lucy\n
Brown
可见,第2行的内容末尾在变更后,添加了 \n,所以内容是发生了变化。
Git的工作原理是基于文件的行的变更管理,能够以对指定行进行添加和删除两种操作的变更记录,从而实现对文件更为颗粒化的管理。
当两名程序员对同一个文件进行修改,再提交至远程。
版本冲突的常见场景有2个:
其中尤其以场景1最为常见。
为了方便描述,现在两名程序员分别为A和B。
仍然以上面的文件为例:
Jack
Tom
Lucy
由于不管的管理是以行进行的,同时行是有序号的,所以对行的内容的修改只要不是同一行就不会有冲突,比如:A先添加了一行 Brown,B删除了第1行,那么无论是谁先提交,最后合并后,内容都是:
Tom
Lucy
Brown
整体变更内容为:
- Jack Tom - Lucy + Lucy + Brown
如果两人修改了相同行,那么部会产生冲突,比如现在对第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条命令:
Accept Current Change 选择本地的内容,即最后内容为 Tom B;Accept Incoming Change 选择远程的内容,即最终内容为 Tom A;Accept Both Changes 两者选择,则会出现两条记录,第1行为 Tom B, 第2行为 Tom A;Compare Changes 打开一个新版本控制版本,出现以分屏展示带有上下文的两个版本的完全对比。需要哪个版本直接点击对应的命令即可,而且特别友好的一点,如果点错了,可以使用 Ctrl-z 撤回后再重新选择。
通过以上测试,可以发现,Git在版本控制时即使是对同一个文件管理,以行的理念都会使管理变得非常容易。在冲突管理时会出现两种情况:
[1] Git冲突与解决方法, https://www.cnblogs.com/gavincoder/p/9071959.html