Git revert冲突疑问:为何部分修改无冲突部分触发冲突?
我创建了一个测试文件,进行多次修改并提交,执行 git log -p 得到以下输出:
commit 6d1ab83bd1b9fbe0b0953eeb209afec90f592b7d (HEAD -> main) a further line diff --git a/test.txt b/test.txt index 987fb68..9692e29 100644 --- a/test.txt +++ b/test.txt @@ -4,4 +4,6 @@ here i am i have never been there +a further line + commit 35f743c6ae1835182691729eaf447b09337d2ed6 hello -> hio and newline added diff --git a/test.txt b/test.txt index c511e9f..987fb68 100644 --- a/test.txt +++ b/test.txt @@ -1,5 +1,7 @@ -hello +hio here i am +i have never been there +
之后执行 git revert 35f743c 尝试撤销该提交的修改,结果出现合并冲突,编辑器中文件内容如下:
hello here i am <<<<<<< HEAD i have never been there a further line ======= >>>>>>> parent of 35f743c (hello -> hio and newline added)
我的疑问:提交35f743c做了两处修改——将第一行从“hello”改为“hio”,以及添加行“i have never been there”。后续提交并未触碰这些行,但撤销时前者无冲突,后者却触发冲突,想了解Git文件对比的底层机制。
Git 执行 git revert 时本质是执行三方合并操作,涉及三个版本的文件内容:
- 待撤销的目标提交(35f743c)
- 目标提交的父提交(记为
P,即35f743c执行前的版本) - 当前 HEAD 指向的最新提交(6d1ab83)
revert 的核心逻辑是:把目标提交相对于父提交 P 的修改反向应用到当前 HEAD 版本上,相当于要恢复到「从未执行过35f743c提交」的状态。
第一行修改无冲突的原因
目标提交35f743c对第一行的修改是:将父提交 P 中的 hello 改为 hio。而当前 HEAD 版本的第一行恰好是 hello,与父提交 P 的内容完全一致。
Git在三方合并时判断:父提交 P 和当前 HEAD 的第一行内容相同,目标提交是修改后的 hio,反向应用修改就是把 hio 改回 hello——这和当前 HEAD 的内容完全匹配,因此可以自动完成合并,不会触发冲突。
新增行触发冲突的原因
目标提交35f743c的父提交 P 中,here i am 行下方是空的;35f743c新增了 i have never been there 这一行。但当前 HEAD 版本中,i have never been there 行下方又新增了 a further line。
Git在处理这个新增行的反向操作(即删除 i have never been there)时,发现父提交 P 中不存在该行,而当前 HEAD 中该行的上下文(前后行内容)已经发生了变化——原来的上下文是here i am + 空行,现在变成了here i am + a further line。
Git的行上下文对比机制依赖行的前后内容来判断修改的边界,当上下文发生变化时,它无法确定删除目标行后,后续新增的a further line应该如何处理(是保留还是一并删除),超出了自动合并的安全判断范围,因此触发合并冲突。
内容的提问来源于stack exchange,提问作者P.Jo

