我对一些事情感到困惑,这里有一个总结。
我们的回购有一个主分支和一个d2l_phase_4分支。自从创建d2l_phase_4分支以来,已经向 master 添加了几个新的提交。
今天,有人错误地将拉取请求合并到本应针对d2l_phase_4分支的提交主节点中。
因此,我这样做了:
- 还原提交并推送到分叉/主节点
- 向主存储库/主存储库发出拉取请求。
- 合并了该拉取请求。
此时,我们已经安全地撤消了提交对主分支的影响。
然后我做了这个:
- 已签出d2l_phase_4分行
- 樱桃从师傅那里挑选了原来的提交。
- 推到叉子/d2l_phase_4
- 发出针对主存储库/d2l_phase_4的拉取请求
- 合并此拉取请求
在这一点上,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
只是E
和I
的合并(因此 J = E + I)1,K
添加了"负 I",因此 K = E + I - I = E。
1这不能保证! 如果I
复制了E
中的更改,则J
实际上不是 E 加 I,而是 E 加 I 减去重复的部分。 但如果是这样的话,我们稍后会看到效果,所以I
一定不能在E
中复制某些东西,或者I
在E
之后出现。 这种事情就是为什么实际拥有图形,有时是存储库本身很重要的原因。
然后,您经历了另一个漫长而复杂的过程,实际上只是一个简单的git cherry-pick
:
- Cherry 从 master [into dl2_phase_4] 中选择了原始提交。
目前尚不清楚哪一个是"原始的"(I
或J
,尽管如果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
相同。 因此,第一个差异会忽略不正确的提交及其还原。 第二个差异,比较B
与I'
,将精心挑选的提交视为更改,因此它包含该更改。 合并第一个和第二个差异将为您提供更改的一个副本。
git log
命令说谎,一点点(有时很多)
如果我查看所涉及的文件之一的历史记录,在d2l_phase_4分支上,那么我看不到还原或任何东西的痕迹,我只能看到我所做的樱桃是否选择了提交。
现在我们有了这个最终的合并L
,git log
应该向我们展示提交L
的内容,L
的第一和第二父级的内容,它们的父级的内容,等等。
提交L
是一个合并提交,所以它有两个父级,K
和I'
。I'
是你挑选的提交,所以当你到达那个点时,git log
会看到它。
K
是具有一个父级J
的简单提交;但J
是具有两个父项E
和I
的合并提交。 你会希望在这里看到提交I
,而且——这部分变得棘手——有时你会这样做。
当你运行git log -- <pathspecs>
时,你给 Git 一个隐含的论点,(我认为)不能明确拼写,尽管--dense
和--sparse
可能足够接近以用于解释目的。 这些打开了历史简化,如果可以的话,历史简化会抛出合并的一个"方面"。 在任何情况下,您还会指示 Git 忽略<pathspecs>
参数中未命名的所有文件。
当 Git 查看提交L
时,它是具有两个父级的合并提交,因此这部分历史简化规则适用:
如果提交与任何父级不是 TREESAME,则包含提交(尽管 这可以更改,请参阅下面的
--sparse
)。如果提交是 合并,并且它与一个父级相同,只跟随该父级。...否则,请遵循所有父母。
这个"TREESAME"被定义为:在抛出除指定路径之外的所有路径后,修剪后的提交的树是否与其父树的树相同? 因此,对于L
,我们将它与K
进行比较,并在删除除您要询问的一个文件之外的所有文件后I'
。 如果L
中的文件版本与I'
中的版本匹配(也许确实如此),那么此时git log
会K
修剪掉,并且将不再看到K
,也看不到J
,也看不到J
的父母中的任何一个,也看不到E
、D
或C
。 然而,它将跟随I'
回到H
,然后回到G
,F
和B
等等,回到历史。
如果L
中的文件版本与K
中的版本匹配,那么git log
修剪掉I'
;但由于I'
肯定接触过文件,Git 在这里只遵循I'
。
要使 Git 显示完整的历史记录,而不是简化在图形中找到的侧分支,您需要--full-history
。 Git 仍将历史记录限制为接触文件的提交,但这次它将查看L
的父级。
git log
文档中有所有这些示例,但它绝对是令人眼花缭乱的材料。
另一件需要注意的事情是,git log
通常对合并提交本身一无所知,除非您指示它"拆分"它们(使用-m
)或强制组合差异(使用-c
或--cc
)。 也就是说,它可能会打印日志消息,但不显示任何更改,即使git show
会显示某些内容(--cc
样式组合差异)。