为什么Git知道它可以选择一个恢复的提交?

为什么Git知道它可以选择一个恢复的提交?,git,git-revert,cherry-pick,git-cherry-pick,Git,Git Revert,Cherry Pick,Git Cherry Pick,例如,在一个分支中,有3个提交:acherry pick是一个合并,从cherry pick的父级到cherry pick的差异,以及从cherry pick的父级到签出提示的差异。就这样。Git不必知道更多。它不关心任何提交的“位置”,它关心的是合并这两组差异 “还原”是还原到其父级的差异和还原到签出提示的差异的合并。就这样。Git不必知道更多 这里:试试这个: git init test; cd $_ printf %s\\n 1 2 3 4 5 >file; git add .; g

例如,在一个分支中,有3个提交:
acherry pick是一个合并,从cherry pick的父级到cherry pick的差异,以及从cherry pick的父级到签出提示的差异。就这样。Git不必知道更多。它不关心任何提交的“位置”,它关心的是合并这两组差异

“还原”是还原到其父级的差异和还原到签出提示的差异的合并。就这样。Git不必知道更多

这里:试试这个:

git init test; cd $_
printf %s\\n 1 2 3 4 5 >file; git add .; git commit -m1
sed -si 2s,$,x, file; git commit -am2
sed -si 4s,$,x, file; git commit -am3
运行
git diff:/1:/2
git diff:/1:/3
。当您在这里说
git cherry pick:/2
时,这些就是git运行的差异。第一个diff更改第2行,第二个commit更改第2行和第4行;第4行的变化与第一个差异中的任何变化不相邻,第2行的变化在这两个方面是相同的。没什么可做的了,所有的
:/1
-
:/2
更改也在
:/1
-
:/3

现在在你们开始下面的内容之前,让我说一下:用散文来解释这一点比仅仅看到它更难。执行上面的示例序列并查看输出。看它比读任何描述都要容易得多。每个人都会经历一段太新的经历,也许有一点方向感会有所帮助,这就是下面几段的目的,但再一次:散文本身比差异更难理解。运行差异,试着理解你在看什么,如果你需要一点帮助,我保证这是一个非常小的驼峰,请按照下面的文本。当它突然聚焦时,看看你是否至少在精神上拍了拍你的额头,然后想“哇,为什么这么难看?”,就像,嗯,几乎每个人一样

Git的合并规则非常简单:对重叠或邻接行的相同更改可以按原样接受。对变更线路的一个diff中没有变更的线路的变更,或在另一个diff中与变更线路相邻的线路的变更,按原样接受。对任何重叠或邻接行的不同更改,好吧,要查看的历史太多了,没有人找到一个规则来预测每次的结果,所以git声明更改冲突,将两组结果转储到文件中,让您决定结果应该是什么

如果你现在换第三行会怎么样

sed -si 3s,$,x, file; git commit -amx
运行
git diff:/1:/2
git diff:/1:/x
,您将看到,相对于樱桃树的父树,
:/2
更改了第2行,而您的小费更改了第2、3和4行。2和3相邻,这在历史上太接近了,自动精灵无法正确处理,所以,是的,你必须这样做:
git cherry pick:/2
现在将声明冲突,显示对第2行的更改以及第3和第4行的两个不同版本(:/2两者都没有改变,您的提示两者都改变了,在这里的上下文中,第3行和第4行的更改很明显是正确的,但再次说明:没有人能够找到可靠识别此类上下文的自动规则)

您可以在此设置上进行更改,以测试还原的工作方式。此外,还可以隐藏pops和合并,以及
git checkout-m
,它可以运行与索引的快速临时合并

您的
git cherry pick B^..C
是两个提交的cherry pick,
B
C
。它一个接一个地执行它们,正如上面所述。由于您已还原
B
C
,然后cherry再次选择它们,这与应用
B
C
的效果完全相同樱桃采摘
B
(目的是采摘樱桃
C
).我的结论是,
B
C
触摸重叠或邻接的行,因此
git diff B^B
将显示重叠或邻接在
git diff B^C'
中的更改,而这正是git不会为您选择的,因为在其他情况下,没有人可以编写识别规则g、 相同外观的选择将是错误的。因此git说这两组更改冲突,您需要对其进行排序。

这就扩展了

考虑这样的历史记录中的常规合并:

a--b--c--d--e--f--g--h
       \
        r--s--t
Git通过只查看这些提交的内容来执行合并:

c--h   <-- theirs
 \
  t    <-- ours
^
|
base
A--B
 \
  R
B--A
 \
  C
第一个
git cherry pick B
后面的合并操作查看了以下提交:

A--B
 \
  C
这里选择
A
,是因为它是
B
,也称
B^
的父级。显然,从
A
C
的更改也包含从
A
B
的更改,并且合并机制产生一个无更改的合并结果,而产生
cherry pick的更改现在是空的圣人

然后您通过还原
B
C
来创建此历史:

A--B--C--R
接下来的
git cherry pick B
查看了这些提交:

c--h   <-- theirs
 \
  t    <-- ours
^
|
base
A--B
 \
  R
B--A
 \
  C
这一次,从
A
R
的更改不再包含从
A
B
的更改,因为它们已被还原。因此,合并不再产生空结果

一个小迂回:当您在历史记录中执行
git revert B
时,合并机制会查看以下提交:

c--h   <-- theirs
 \
  t    <-- ours
^
|
base
A--B
 \
  R
B--A
 \
  C
请注意,与
git cherry pick B
相比,只有
B
B
的父级,即
a
被交换


(我在描述单次提交反转,因为我不确定多次提交反转是如何工作的。)

让我们退一步,在这里了解一下Git是什么

Git提交是所有文件的快照。它基本上代表了您的整个项目。它与差异无关。这是一个出色的体系结构,因为它速度极快且有效无误。任何提交都可以绝对