假设我有ProjectA和ProjectB,在这两个项目中,我只有一个名为test.txt的文件,并用git跟踪它。在第一次提交之后,两个项目中的文件内容如下所示。
one
two
three
four
第二次提交后,两个项目中的文件内容如下所示。
one
2
three
four
对于第三次提交,我将内容更改为不同的
第三次提交后ProjectA中的test.js
one
2
3
four
第三次提交后,ProjectB中的test.js
one
2
three
4
现在我的问题是……在第三次提交之后,当我尝试使用gitrevert还原第二次提交中所做的更改时,如下所示。。
git revert secondCommitHashId
,git最终在ProjectA中出现合并冲突,但git很高兴地给了我vim编辑器模式,让我在ProjectB输入revert消息,以在没有合并冲突的情况下完成revert操作。
为什么git revert hashId
在这些情况下表现不同?
git revert
是一个合并操作。你有这样的历史:
1--2--3
你要求
git revert 2
基本版本是2
处的项目状态;我们的";version是3
处的状态;他们的";版本(正在合并)为1
。换句话说,从2
到1
以及从2
到3
的改变被合并。
在您的两个示例中,从2
到1
的更改将第2行的文本2
替换为单词two
(不幸的是,2s的错误重合)。不同之处在于项目:在项目A中,从2
到3
的更改更改更改了第3行,而在项目B中,从2
到3
的更改更改则更改了第4行。
在前一种情况下,分叉的更改会修改相邻的线。Git将此标记为冲突。
在后一种情况下,在发散变化之间有一条未修改的线(第三条线)。在这里,Git可以清楚地分离更改,并可以解决合并问题。
什么是git revert
git revert
是对当前分支进行新提交的请求,其父级将是当前HEAD提交。新的提交是使用合并逻辑创建的(我在本文中对此进行了大量解释)。但新提交本身并不是合并提交;也就是说,它只有一个父级。
提交创建如下:
-
查看(a)我们被要求恢复的提交和(b)该提交的父级之间的
diff
。换言之,想想我们将如何从提交倒退到它之前的状态。 -
还可以看看(a)我们被要求恢复的提交和(c)我们现在所在的提交(又名HEAD)之间的
diff
。 -
在(a)我们被要求恢复的提交上创建一个同时执行这两个差异的提交,并将其附加到当前分支。
因此,在您的这个小故事中,只有三次提交,(a)是第二次提交、(b)是第一次提交和(c)是第三次提交。您要求Git进行一次提交,同时执行从第二次提交到第一次提交以及从第二个提交到第三次提交的差异。
但是Git做不到;它已经到达了一个的aporia,一个它无法独自前行的地方。不幸的是,这种情况被称为合并冲突。
现在让我们来看看为什么会这样。
检查";"冲突">
当Git遇到这种情况时,它会在工作树中生成一个特殊的冲突文件来描述问题。如果您查看该冲突文件,特别是如果您将合并冲突配置为使用diff3
格式,则问题的答案是明确的。
这就是冲突文件的样子(我更改了一些名称以使事情更清楚):
one
<<<<<<< third commit
2
3
||||||| second commit
2
three
=======
two
three
>>>>>>> first commit
four
让我们一起读这篇文章。在|||
和===
之间的中间是第二个提交,2 three
。以上就是我们现在的位置,HEAD,第三个提交,2 3
。下面是第一个提交,two three
。
现在,回想一下,在这种情况下,恢复第二次提交意味着:同时执行将第二次交付反转回第一次提交和从第二次承诺更改为第三次提交
因此,Git需要找到一种方法,同时从2 three
到2 3
和two three
。但是没有这样的办法!因此,Git不知道该怎么办,并且存在冲突。
大块头
也许,这里唯一令人惊讶的是,第三行是有问题的大块头的一部分。为什么Git不单独考虑第二行?这是因为Git从来没有这样做过。在Git的心目中,没有一个像一行字的大块头这样的东西。它总是考虑上下文。因此,如果两条连续的线对于所考虑的差异之一不同,那么它们都是我们试图解决的问题的部分。
你的第二个例子呢
现在让我们来谈谈为什么第二个例子没有出现同样的问题。在第二个示例中,第三行在第二次和第三次提交之间不发生更改。事实上,在第二个示例中,第一行、第二行和第三行在第二次和第三次提交之间都没有变化。
因此,对于文件的前三行,revert的目标是实现从(a)one 2 three
到(b)one two three
的反向更改,以及从(a)one 2 three
到(c)one 2 three
的反向更改。但这很容易,因为第二个diff是根本没有diff第二个diff的贡献是什么都没有所以第一个diff是我们在文件的这一部分必须做的唯一的事情,我们可以做到。
现在让我们考虑一下文件的其余部分。让我们接第三行和第四行。(记住,没有单行的大块。)在这里,我们实现了从(a)three four
到(b)three four
的反向变化,以及从(a)three four
到(c)three 4
的变化。在这种情况下,第一个diff根本不是diff,也没有任何贡献。所以我们唯一要做的就是从three four
改为three 4
,我们可以做到
因此,第二个例子没有任何问题,Git在没有任何抱怨的情况下创建了新的提交。