您是不是可以在不产生冲突的情况下恢复除最后一次以外的提交?

Posted

技术标签:

【中文标题】您是不是可以在不产生冲突的情况下恢复除最后一次以外的提交?【英文标题】:Can you ever revert a commit other than the very last without creating a conflict?您是否可以在不产生冲突的情况下恢复除最后一次以外的提交? 【发布时间】:2019-08-14 18:56:04 【问题描述】:

注意:*** 的机器人认为这个问题是主观的,但它是非常真实的——我不是在这里征求意见!

假设我只有一个分支,我的提交历史是: A - B - C - D

如果我在 GUI(如 GitKraken 或 Windows 版 Git)中单击 C 并执行“revert commit”,我会收到文件冲突消息。

两个问题:

    这是因为还原“撤消”了在 C 和 GIT 中所做的更改 现在被 B 和 D 卡住了,它们以不同的方式修改文件, 不兼容的方式?是这个原因吗?

    如果是,这是否意味着您只能还原最后一个 犯罪?除了最后一个提交之外,你能恢复一个提交吗 不产生冲突?

更新:在@RomainValeri 非常有用的澄清之后编辑问题。我认为我在还原和重置之间混淆了太多。在他下面的例子中,做

git revert B

导致分支离开

A-B-C-D

A-B-C-D-E

如果 B 是唯一更改 file2.txt 的提交,并且 B 没有对其他文件进行任何其他更改,则在还原后创建的新提交 E 将保留在 C 和 D 中所做的所有更改但不是那些 B 制造的。这是正确的吗?

这是因为从技术上讲,还原意味着取消、撤消提交的任何更改 - 这是正确的吗?

另外:假设 我的工作目录中只有一个文件。如果 B 是唯一更改我文件中函数 fun1() 的提交, 而所有其他提交更改同一文件中的其他函数,则 还原 B 很可能会导致冲突 ,因为 git 考虑文件中的行,而不是文件中的函数。这是正确的吗?

假设 B 改变 fun1(),C 改变 fun2(),D 改变 fun3()。然后我意识到在 fun1() 中对 B 所做的更改是错误的,我需要撤消它们。 如果所有这些函数都在同一个文件中,有没有办法撤消 B 中的更改,同时保留 C 和 D 中的更改?

如果这三个函数中的每一个都在一个单独的文件中,那么在不影响其他提交的情况下撤消一个提交的更改会简单得多,对吧?

我想这当然是单个文件不应该太大也不应该包含太多功能做不同事情的众多原因之一,对吧?

【问题讨论】:

嗯,你总是可以解决冲突吗? 【参考方案1】:

简短回答:是的。您可以非常好恢复给定的提交(甚至是旧的提交)并且没有冲突。


为什么会这样?

git revert 在树的当前顶端创建一个新的提交,它删除给定的提交,也不以任何方式修改它。

当您使用还原创建的提交与HEAD 之间的merge-base 中的某些内容不同时,您就会发生冲突。


自己试试这个

简单的例子

鉴于回购树,

Folder1
  file1.txt
  file2.txt
Folder2
  file3.txt

以及追随历史,

A---B---C---D << HEAD

在哪里

A 创建所有文件及其内容。B 修改file2.txtC 修改file1.txtfile3.txtD 进一步修改file1.txt

然后是命令

git revert B

不会造成任何冲突,只是一个新的提交 Efile2.txt 的新更改。

A---B---C---D---E << HEAD

git revert 创建的新提交的内容 (添加在 cmets 之后)

E 中的更改与 B 引入的完全相反的更改。

B 像这样:

$ git show B
commit f3ba29d98c0998297126d686d03d33794cbc0f73
Author: Pythonista <some.name@some.isp>
Date:   Sun Mar 24 20:41:49 2019 +0100

    Some change

diff --git a/javascript/functions.js b/javascript/functions.js
index 543c9cb..5f166d3 100644
--- a/javascript/functions.js
+++ b/javascript/functions.js
@@ -3710,5 +3710,6 @@ function dumpLists(isMixted) 
 // end of process

 createTrigger(window, 'load', START_EVERYTHING);
+doStuff(param);

您将有一个 E 提交,其中包含:

$ git show E
commit 2ff723970e81d64f3776c081a1f96242589774b6 (HEAD -> master)
Author: Pythonista <some.name@some.isp>
Date:   Sun Mar 24 20:42:33 2019 +0100

    Revert "Some change"

    This reverts commit f3ba29d98c0998297126d686d03d33794cbc0f73.

diff --git a/javascript/functions.js b/javascript/functions.js
index 5f166d3..543c9cb 100644
--- a/javascript/functions.js
+++ b/javascript/functions.js
@@ -3710,6 +3710,5 @@ function dumpLists(isMixted) 
 // end of process

 createTrigger(window, 'load', START_EVERYTHING);
-doStuff(param);

注意最后一个差异行前面的+-,意思是“添加”(+)和“删除”(-)。


最后,在你的帖子末尾说一下你的担忧(由于某种原因,我大部分都错过了):

我认为您不应将应用程序的架构选择基于源代码控制问题。 Git 是一个非常棒的工具,它足够聪明,可以防止我们不可避免地遇到的大部分常见问题。寻找最好的方式来按照自己的逻辑对代码进行分组,而不管 git。

顺便说一句,冲突检测发生在块级别,而不是文件。这意味着如果同一文件发生在不同的非连续部分中,则两个源可以修改同一个文件而不会引起冲突。我不能确切地说阈值在哪里,但这无关紧要。

【讨论】:

让我看看我是否理解这一点:这是一个冲突,因为,比如说,通过恢复 CI 回到 B,它定义 x=y,而 D 在同一行代码中定义 x=z .这是一个例子吗?同样,如果 D 所做的只是在最后添加另一行代码,例如 z = 1,这不会在我还原后产生冲突,对吧? 我不确定我是否理解您所描述的内容。 “通过恢复 C 我回到 B”听起来很错误,我错过了什么?我们不要在这里混淆恢复和重置。还原C不会返回任何地方。它只是在顶部创建一个新的提交。我们可以吗? 自从我开始尝试学习 git 以来,我从未感到如此愚蠢!还原不会撤消提交吗?至少可以说我很困惑?不?还是在 D 之后还原 C 会创建一个新的提交? Reset 将分支指针设置在别处。 Revert 创建一个新的提交,其中包含给定提交的确切反向更改。不要这么生气,你不是唯一一个为此挣扎的人,一开始并不那么容易掌握。对我来说,你听起来不像一个愚蠢的人,这是肯定的 :-) 所以 git revert B 导致树从 A-B-C-D 变为 A-B-C-D-B。现在有点清楚了。谢谢!我希望在我翻阅的数百页文档和教程中找到这个简单的单行字!【参考方案2】:

在 Git 中,除了直接提交之外,几乎所有的历史记录操作最终都会在某处涉及合并。 Rebase、cherry-pick、revert、stash pop、merge 当然,它们都合并,只是根据需要选择合并基础。结帐时甚至还有一个合并选项,它使用您当前的结帐作为基础,新的提交作为提示,您的工作树/索引作为另一个提示,并合并到新的工作树/索引中,这非常方便。

对于还原,比如说A---B... * ... M master 和你git revert B,git 查看从BA 的更改以及从BM 的更改,这就是你的合并。如果在两组更改中触及了相邻或重叠的行,那么某些人需要修复文本的几率会飙升,因此 Git 拒绝默默地这样做。无论如何,琐碎的案例需要几秒钟。

【讨论】:

关于git checkout -m(如果我理解你在第一段末尾暗示的内容),我不得不查一下,但从未使用过,谢谢你的提示:-)

以上是关于您是不是可以在不产生冲突的情况下恢复除最后一次以外的提交?的主要内容,如果未能解决你的问题,请参考以下文章

PHP 为除最后一项以外的每一项添加逗号

您可以在不使用 Storyboard 的情况下在 AutoLayout 控制台输出中标记视图吗?

用户是不是可以在不安装您的应用的情况下通过 Firebase Auth 注册新帐户?

POSIX正则表达式中的环顾四周,以匹配除最后一个(对于gsub而言)以外的所有空格

当您连接 2 个具有相同架构的表并检查除一个以外的所有字段是不是相等时,如何避免在 SQL 中编写冗长的 where 子句?

如何在不考虑最后一个空白行的情况下对 jqGrid 进行排序