这怎么可能?
这两个分支明显不同,但PR说它没有发现差异。
我很难过。并阻止。我不能继续,直到我的代码进入dev,所以这不仅仅是好奇心。
我甚至尝试将MarketReseach2合并到dev中。它告诉我它已经是最新的了。
然而,我可以在Visual Studio中在它们之间来回切换,看看差异。
答案 0 :(得分:-1)
问题很简单:合并(作为动词,即作为要采取的动作)并不代表"制作相同的"。这意味着"结合变化"。
让我们退一步吧。我们究竟是什么意思"变化"?无论如何,提交中的内容是什么?并且:What exactly do we mean by "branch"?
你提到:
我可以在它们之间来回翻转......并看到......差异
让我们很好地掌握"他们"首先意味着查看关于我们的意思的链接问题" branch"同样,但根据你的图像,"他们"是名称 MarketResearchCampaign2
和dev
。这两个名字实际上转化为两个特定的提交ID:两个丑陋的SHA-1哈希,你的IDE帮助(咳嗽:-))隐藏,这样你就看不到它们了,但事实上它们看起来像{{1}或者其他一些。
隐藏在名称后面的哈希ID,因为哈希ID确实 对人类非常有用,代表特定的提交。每个提交都有自己唯一的哈希ID。但究竟是提交的是什么?
好吧,当你在这两个提交之间来回切换时,你会看到整个文件树:工作树的快照。这是提交内容的一部分。通过提交,您可以访问工作树的快照:您可以检出任何历史提交,并检索与该提交一起使用的已保存工作树。
提交的其余部分是一堆元数据,包括提交者和日志消息,以及 - 对于Git - 父级非常重要的东西提交,即之前的提交特定提交。
大多数提交都有一个父级。这些父母以倒退的方式形成项目的历史 - 或项目的某些部分。这让我们(和Git)从最近的提交开始并向后工作:
7b19d86e69d...
名称... <- o <- o <- o <-- dev
指向最近的提交,指向先前的提交,指向另一个提交,依此类推。除了dev
命名最新此类提交的关键事实外,我们大多不必担心箭头都是向后的,因此绘制起来更好尽管如此,这些都是这样的:
dev
这让我更容易在树枝上画画。现在...--*--o--o <-- dev
\
o--o <-- ziggy
指向最近的dev
提交,dev
指向最近的ziggy
提交。 名称 ziggy
和dev
指向一个特定的提交,这些提交指向旧的提交。
请注意,我已使用ziggy
标记了一个提交,其中两组向后连接的箭头连接起来。这是理解合并的关键项目之一。
当您在两个特定提交之间来回切换时,您会看到不同的文件。 Git可以比较这两个提交 你。从命令行,您只需运行:
*
比较这两个提交。您的IDE也应该有这样做的方法。
但是,更常见的是,您可能希望比较两个不同的分支名称,而不是某个分支上的某个提交,而不是其中一个父提交。也就是说,鉴于上面的git diff dev MarketResearchCampaign2
和dev
,我们可能会将ziggy
的提示与dev
提示后的提交进行比较:提交dev
。这将显示你在进行这两次提交中的第二次时所改变的内容。
我们也可能将这两者中的第一个与提交*
本身进行比较。这将显示您从*
更改为这两个提交中的第一个。
当然,我们可以将*
与*
的提示进行比较。这将显示您在所有提交的所有所做的一切从dev
到*
的提示。
同时,我们可以用同样的方式将dev
的提示与之前的提交进行比较。如果我们将ziggy
上的最后一次提交与其前一次提交进行比较,则表明我们在那里做了什么。如果我们将ziggy
上的最后一次提交与提交ziggy
进行比较,那么:嗯,这表明我们在分支*
上所做的一切,与提交ziggy
相比。
*
提交*
的有趣之处在于它两个分支:它是*
和{{}的最新提交1}}还在一起。从那时起,dev
已经分歧,有几个新的提交;并且ziggy
也有分歧,有几个新的提交。如果我们认为这是一个好主意,我们现在可以重新加入这两行:我们可以让Git找到&#34;自dev
&#34;以来我们所做的一切在ziggy
,以及&#34;自*
&#34;以来我们所做的一切在dev
中,并进行重新组合这两者的新提交。
合并的作用是:它发现了一些公共点后的变化。
请注意, commit *
之前的任何提交也在两个分支上。使ziggy
特殊的事情不仅仅在于它在两个分支上,而在于它是两个分支上的最近提交。 1
1 从技术上讲,它是最低的共同祖先&#34;或DAG或有向无环图中的LCA。这种技术性仅在存在多个LCA时才有意义。 LCA中的一个或另一个可能实际上是较新的,但实际上它是拓扑而不是日期。 Git和其他一些VCS处理多LCA情况不同于例如Mercurial。在这种情况下,Git实际上为您提供了合并策略选择:*
执行一项操作,*
执行另一项操作。但这是一个独立的高级主题。
我们说我们进行合并,并在分支-s recursive
上进行:
-s resolve
这个新的合并提交ziggy
有一些特殊的东西:它指向的是,而不是一个之前的提交,而是两个。第一个&#34;之前的提交&#34;是...--o--o--o <-- dev
\ \
o--o--M <-- ziggy
的旧提示,最右边的M
位于底行,第二个是&#34;之前的提交&#34;仍然是!ziggy
的一角,最右边的o
。{/ p>
现在让我们通过更改一些内容,在dev
上进行更多提交。我还要使用o
标记ziggy
的提示最多提交:
dev
让我们将*
的提示(即提交...--o--o--* <-- dev
\ \
o--o--M--o--o--o <-- ziggy
)与dev
的提示进行比较。他们肯定不会匹配:当我们通过合并将*
中的内容带入ziggy
时,他们甚至不会在{{1}匹配} -vs - dev
,因为我们合并 ziggy
更改与之前的*
更改。现在他们可能会更少匹配。
如果我们现在要求Git再次将M
合并到dev
,会发生什么?好吧,Git首先要找到两个分支在一起的最新点......那就是ziggy
。因此,Git会将dev
与ziggy
的提示进行比较,以查看我们在*
上所做的更改,并将其与我们在*
上更改的内容相结合。< / p>
但dev
是dev
的提示。什么都没有合并!实际上,整个提交的最顶行都在两个分支上,ziggy
再一次只是最近的这样的提交(或者,再次,从技术上来说,它是LCA节点) )。
当Git告诉你没有要合并时,这意味着你选择的两个特定提交,作为他们最近的共同祖先提交,这两个提交之一。 (或者,在最新的Git版本中,如果有 no 常见提交,则会出现不同的错误;较旧的Git版本只是对空树进行两次提交,并像往常一样组合生成的差异。) &#34;合并&#34; 表示&#34;根据某些常见的基本提交结合查看的更改&#34;,必须 在两个端点上针对某些常见的基本提交进行一些更改:公共基础提交不能是两个端点之一。
在这种情况下:
*
您无法将dev
合并到*
,但您可以将...--o--o--* <-- dev
\ \
o--o--M--o--o--o <-- ziggy
合并到dev
中。实际上有两种方法可以做到这一点。一个是真正的合并:
ziggy
这会将ziggy
到dev
的变化(即无变化)与...--o--o--o------------N <-- dev
\ \ /
o--o--M--o--o--o <-- ziggy
到*
的变化相结合,从而产生相同的变化文件在*
的提示中。也就是说,它结合了“没有变化”和#34;与&#34;一些变化&#34;。结果显然是&#34;匹配*
&#34;的提示,所以在这种情况下,它 最终制作一个新提交ziggy
,其文件与ziggy
提示中的文件完全匹配。 (并且,ziggy
的提示成为新的N
:新的最近共同提交。)
但是,Git通常不会为这种情况进行合并提交。相反,它意识到将某些东西与某些东西组合在一起会自动产生&#34;某些东西&#34;,即新的提交将与ziggy
的尖端完全匹配。因此,毕竟没有理由去做提交ziggy
。 2 相反,它只是移动分支标签,以便它们都指向相同的 commit:
*
这是一个所谓的&#34;快进&#34;操作(或&#34;快进合并&#34;,虽然没有实际的合并进行)。这里的缺点是这根本不会创建新的合并提交;相反,两个分支标签最终指向相同的提交,直到您在两个分支中的一个或两个上进行新的提交。例如,假设您在ziggy
上进行了一次新提交,在N
上进行了两次提交:
...--o--o--o
\ \
o--o--M--o--o--* <-- dev, ziggy
因为从来没有实际的合并提交,所以不再可能独立地遵循这两行。如果您使用dev
来强制进行真正的提交,那么您将获得提交ziggy
并以此结束:
...--o--o--o o <-- dev
\ \ /
o--o--M--o--o--*--o--o <-- ziggy
现在可以使用每个提交中的正确(git merge --no-ff
)链接来准确地跟踪每个开发阶段的发生位置。
这有关系吗?你决定了!
2 当然,除了 原因之外,正如本节其余部分所述。有时这是一个重要的原因,有时它不是;由你决定它是否重要,如果是的话,强制Git进行真正的合并提交。