git revert : 即使在简单的情况下也无法撤消单个提交

git revert : Unable to undo an individual commit even in a simple case

为了尝试理解 git revert,我对一个文本文件 foo.txt 进行了一系列 4 次简单提交——A、B、C、D——稍后仅撤消提交 B 并完整保留提交 A、C、D。

因此,在每次提交中,我都在文件中添加了一个新行,模拟添加的功能或引入的错误。

提交A后,foo.txt的内容:

Feature A

提交B后,foo.txt的内容: (这里,我引入一个错误,我稍后会尝试undo/revert.)

Feature A
Bug

提交C后,foo.txt的内容:

Feature A
Bug
Feature C

提交D后,foo.txt的内容:

Feature A
Bug
Feature C
Feature D

现在,要撤消提交 B(引入错误)的影响,我做了:

git revert master^^

我期望发生的是,一个新的 Commit E 从文件中删除了 Bug 行,将文件内容保留为:

Feature A
Feature C
Feature D

但是,我得到了错误:

error: could not revert bb58ed3... Bug introduced
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

不成功的 git revert 之后的文件内容为:

Feature A
<<<<<<< HEAD
Bug
Feature C
Feature D
=======
>>>>>>> parent of bb58ed3... Bug introduced

( bb58ed3 是 Commit B 的 hash,'Bug introduced' 这个 commit 的评论。)

问题:

  1. 这是怎么回事?

  2. 如果连这么简单的一行提交都不能reverted/undone自动完成,必须要我手动解决,那我怎么能还原一个更复杂的提交,其原始开发者甚至可能是别人!

  3. 是否有一组特殊情况下 git revert 更适用?

git 将每个提交视为一个更改列表(我在这里简化了事情)并尝试 "unapply" 当您调用 git revert 时。每个更改列表还包括一些上下文以确保更改有意义。例如,如果我们要进行的更改是 "add return after line 10",那么它比 "add return after line 10, if lines 7-9 contain X, Y, and Z" 更有可能破坏事物。因此,我们可以将您的第二次提交描述为(再次在这里稍微简化一下):

  1. 假设文件的第一行是Feature A.
  2. 假设没有第二行
  3. 让第二行包含Bug.

添加几行后,Bug 的上下文发生了显着变化,因此 git revert 不确定是否可以简单地删除该行。也许新添加的行实际上修复了错误。所以它要求你显式解决上下文冲突。

关于您的问题 2-3:是的,git revert 可用于您要恢复自此之后未更改的文件的情况。例如,该错误是在 foo 函数中引入的,但此后仅修改了 bar 函数(位于下面十行)。在那种情况下,git revert 可能会自动恢复更改,因为它发现上下文没有改变。

UPD:这里是一个例子,说明即使你试图恢复自己的代码,上下文也很重要:

提交 A(注意输入错误):

int some_vlue = 0;
read_int_into(some_vlue);
some_vlue = some_vlue++;

提交 B(引入错误):

int some_vlue = 0;
some_vlue = 123;
some_vlue = some_vlue++;

提交 C(名称固定):

int some_value = 0;
some_value = 123;
some_value = some_value++;

现在,为了恢复提交 B,必须有一些上下文,因为我们不能简单地将 some_value = 123 替换为旧行 read_int_into(some_vlue) - 这将是编译错误。