Git 还原和樱桃采摘



我对一些事情感到困惑,这里有一个总结。

我们的回购有一个主分支和一个d2l_phase_4分支。自从创建d2l_phase_4分支以来,已经向 master 添加了几个新的提交。

今天,有人错误地将拉取请求合并到本应针对d2l_phase_4分支的提交主节点中。

因此,我这样做了:

  1. 还原提交并推送到分叉/主节点
  2. 向主存储库/主存储库发出拉取请求。
  3. 合并了该拉取请求。

此时,我们已经安全地撤消了提交对主分支的影响。

然后我做了这个:

  1. 已签出d2l_phase_4分行
  2. 樱桃从师傅那里挑选了原来的提交。
  3. 推到叉子/d2l_phase_4
  4. 发出针对主存储库/d2l_phase_4的拉取请求
  5. 合并此拉取请求

在这一点上,d2l_phase_4分支与原始提交相比具有所需的更改,到目前为止一切顺利。

最后,我想让 d2l_phase_4 分支与自创建 d2l_phase_4 分支以来 master 上的所有其他内容保持同步。因此,再次在我从 master 合并的d2l_phase_4分支上,创建了一个新的合并提交。

然后,我将这个更新的d2l_phase_4分支推送到我的fork/d2l_phase_4,最后创建了一个针对主存储库/d2l_phase_4的拉取请求,然后合并了该PR。

但是 - 我突然意识到 Id 现在合并到主分支的早期还原中,因此必须使我之前挑选的提交无效!

但是,在检查d2l_phase_4上的文件状态时,没有从 master 合并的还原的痕迹 - 这正是我想要的 - 但我不明白。

为什么我的樱桃在d2l_phase_4没有被从主节点合并的还原无效?我原以为我需要在d2l_phase_4分支上恢复我的恢复,但是当我尝试这个 git 似乎告诉我没有什么可以恢复的......

如果我查看所涉及的文件之一,在d2l_phase_4分支上,那么我看不到还原或任何东西的痕迹,我只能看到我所做的樱桃选择提交。

这有点复杂,并且像往常一样,要了解正在发生的事情,我们必须绘制(部分)提交图。

这是您显然(基于您的文本)在错误合并之前拥有的内容。 我可能有错误的提交次数,但整体图表必须相当接近:

...--A--B--C--D--E   <-- master

F--G--H    <-- dl2_phase_4

对于下一部分,我们必须猜测,因为只有(以及有权访问您的存储库的其他人)拥有正确的信息:

今天,有人错误地将拉取请求合并到本应针对d2l_phase_4分支的提交主节点中。

"定位"并不重要;重要的是提交图。 拉取请求仅意味着有人发布了一个存储库,该存储库具有与您相同的提交,以及至少一个提交。 关键问题是提交在他们的图表中的位置,我不知道答案。 但是,让我们说它出现在D之后,因此它看起来像这样:

__I   <-- their-commit
/
...--A--B--C--D--E    <-- master

F--G--H     <-- dl2_phase_4

(如果它出现在E之后,我们可以快进而不是合并,尽管最终这没有区别。 如果它出现在F之后,图表会变得混乱,尽管它最终没有区别。

然后,正如您所说,合并到master中,给出一个新的合并提交(我称之为M但我们将有更多的合并,所以让我们继续使用单个字母并称其为J):

__I
/   
...--A--B--C--D--E--J   <-- master

F--G--H    <-- dl2_phase_4

(我们不需要再给I贴标签,因为它只是J的第二个父代——第一个父代是E的;第一父级与第二父级的区别往往会在这些图中丢失)。

然后,按照您的三个步骤,使用一个漫长的复杂过程(推到分叉,从分叉中拉回来)恢复合并,这只会给你与在master的新提交K中简单地回退提交I相同的效果:

__I
/   
...--A--B--C--D--E--J--K   <-- master

F--G--H          <-- dl2_phase_4

关于提交K需要注意的关键是它的(存储的快照)与提交E中的树完全相同提交J只是EI的合并(因此 J = E + I)1,K添加了"负 I",因此 K = E + I - I = E。


1这不能保证! 如果I复制了E中的更改,则J实际上不是 E 加 I,而是 E 加 I 减去重复的部分。 但如果是这样的话,我们稍后会看到效果,所以I一定不能在E中复制某些东西,或者IE之后出现。 这种事情就是为什么实际拥有图形,有时是存储库本身很重要的原因。


然后,您经历了另一个漫长而复杂的过程,实际上只是一个简单的git cherry-pick

  1. Cherry 从 master [into dl2_phase_4] 中选择了原始提交。

目前尚不清楚哪一个是"原始的"(IJ,尽管如果I实际上是快进的,则没有J),但这也不重要:我们只是得到一份副本,我称之为I'

__I
/   
...--A--B--C--D--E--J--K   <-- master

F--G--H--I'      <-- dl2_phase_4

虽然I'树与I树不同,但两个相对的变化——如果我们运行git diff <id-of-D> <id-of-I>git diff <id-of-H> <id-of-I'>,我们会得到的是相同的。 这就是git cherry-pick所做的:它对其父级进行差异处理,然后将生成的差异应用于当前提交并进行新的提交。

最后,我想让 d2l_phase_4 分支与自创建 d2l_phase_4 分支以来 master 上的所有其他内容保持同步。因此,再次在我从 master 合并的d2l_phase_4分支上,创建了一个新的合并提交。

所以现在让我们画出来

__I
/   
...--A--B--C--D--E--J--K     <-- master
              
F--G--H--I'----L   <-- dl2_phase_4

然而 - 我突然意识到我现在已经合并了主分支的早期恢复......[但是]检查d2l_phase_4上的文件状态,没有从 master 合并的还原的痕迹 - 这正是我想要的 - 但我不明白。

这进入了第一个关键项:

Git 合并不查看任何"内部"提交

为什么我的樱桃被选中提交d2l_phase_4没有被从主节点合并的还原无效?[截图]

从图中可以看出,我们正在合并提交K,同时"打开"提交I'合并基础(图形线重新加入的提交提交)是提交B

合并过程,合并作为动词,本质上是通过执行(然后组合)两个git diff来工作的:一个从合并基础到当前提交,一个从合并基础到另一个提交。 因此:

git diff <id-of-B> <id-of-I'>
git diff <id-of-B> <id-of-K>

我们已经注意到,就(git diff查看的内容)而言,提交K与提交E相同。 因此,第一个差异会忽略不正确的提交及其还原。 第二个差异,比较BI',将精心挑选的提交视为更改,因此它包含该更改。 合并第一个和第二个差异将为您提供更改的一个副本。

git log命令说谎,一点点(有时很多)

如果我查看所涉及的文件之一的历史记录,在d2l_phase_4分支上,那么我看不到还原或任何东西的痕迹,我只能看到我所做的樱桃是否选择了提交。

现在我们有了这个最终的合并Lgit log应该向我们展示提交L的内容,L的第一和第二父级的内容,它们的父级的内容,等等

提交L是一个合并提交,所以它有两个父级,KI'I'是你挑选的提交,所以当你到达那个点时,git log会看到它。

K是具有一个父级J的简单提交;但J是具有两个父项EI的合并提交。 你会希望在这里看到提交I,而且——这部分变得棘手——有时你会这样做

当你运行git log -- <pathspecs>时,你给 Git 一个隐含的论点,(我认为)不能明确拼写,尽管--dense--sparse可能足够接近以用于解释目的。 这些打开了历史简化,如果可以的话,历史简化会抛出合并的一个"方面"。 在任何情况下,您还会指示 Git 忽略<pathspecs>参数中命名的所有文件。

当 Git 查看提交L时,它是具有两个父级的合并提交,因此这部分历史简化规则适用:

如果提交与任何父级不是 TREESAME,则包含提交(尽管 这可以更改,请参阅下面的--sparse)。如果提交是 合并,并且它与一个父级相同,只跟随该父级。...否则,请遵循所有父母。

这个"TREESAME"被定义为:在抛出除指定路径之外的所有路径后,修剪后的提交的树是否与其父树的树相同? 因此,对于L,我们将它与K进行比较,并在删除除您要询问的一个文件之外的所有文件后I'。 如果L中的文件版本与I'中的版本匹配(也许确实如此),那么此时git logK修剪掉,并且将不再看到K,也看不到J,也看不到J的父母中的任何一个,也看不到EDC。 然而,它将跟随I'回到H,然后回到GFB等等,回到历史。

如果L中的文件版本与K中的版本匹配,那么git log修剪掉I';但由于I'肯定接触过文件,Git 在这里只遵循I'

要使 Git 显示完整的历史记录,而不是简化在图形中找到的侧分支,您需要--full-history。 Git 仍将历史记录限制为接触文件的提交,但这次它将查看L的父级。

git log文档中有所有这些示例,但它绝对是令人眼花缭乱的材料。

另一件需要注意的事情是,git log通常对合并提交本身一无所知,除非您指示它"拆分"它们(使用-m)或强制组合差异(使用-c--cc)。 也就是说,它可能会打印日志消息,但不显示任何更改,即使git show会显示某些内容(--cc样式组合差异)。

相关内容

  • 没有找到相关文章

最新更新