git pull --allow-unrelated-histories是如何工作的?

时间:2019-02-22 23:24:18

标签: git github gitlab

对,所以我搜索了其他一些SO线程,并还检查了这个:https://git-scm.com/docs/git-merge

我了解--allow-unrelated-histories允许两个项目结合在一起,但是,我不了解的是它的工作原理

它像这样工作吗? https://imgur.com/a/ZFVVs7s

上面的git站点显示此图:

      A---B---C topic
     /         \
D---E---F---G---H master

但是,在我看来,它们似乎没有无关的历史记录,因为主题分支从'E'中分离出来了。即使主题分支从“ D”的母版中分离出来,他们仍然会共享“ D”分支。

任何人都能够(最好是用视觉效果)解释允许无关历史如何工作吗?我正在尝试git pull,但是我的团队成员之一编辑了我要从中拉出的分支,现在我必须使用--allow-unrelated-histories。

谢谢!

2 个答案:

答案 0 :(得分:4)

您是正确的,您的图表(来自git-scm.com/docs/git-merge)显示了具有共同祖先提交的合并。值得注意的是,这是一个共享的 commit; 术语 branch 在Git中有点棘手。 (请参见What exactly do we mean by "branch"?

无论如何,如果您忘记了git pull的存在,我认为这会有所帮助。 git pull所做的全部工作就是为您运行两个不同的Git命令。除非您对Git有足够的经验,否则最好使用单独的git fetchgit merge命令。 (请注意,git pull --rebase将第二个命令切换为git rebase,但在此不做详细介绍。)使用git pull运行其他两个命令存在多个问题。其中之一是git pull使用怪异的pull-only语法,与 all 其他Git命令不同,包括运行git merge的{​​{1}}。也就是说,您将运行git pull而不是git pull origin xyz。要查看其含义,请运行git merge origin/xyzgit log origin/xyz等。它们总是用斜杠拼写为git show origin/xyz,但使用origin/xyz时除外,所以不要不要使用git pull。 :-)让我们将其分为两个单独的命令。

  • git pull运行的第一个命令是git pull,您可以随时运行它:git fetch调用其他Git,询问它对您有什么承诺,使用什么名称(通常是分支名称和标记名称)。它收集这些提交(当然还有它们的文件),并为每个分支名称创建或更新您的远程跟踪名称。这样就是git fetch的来源,例如:origin/master看到他们有一个git fetch,他们的主人正在提交master或其他任何东西,并创建或更新了{{ 1}}要记住: badf00d的主人是我上次检查的origin/master

  • origin为您运行的第二个命令是所有有趣的操作所在。第二条命令不应在任何旧时间在任何旧分支上运行,因为无论您运行了Git的第二条命令,都必须在 right 分支上运行:您要合并到其中的一个,或您要重新设定基准的一个。我发现在这里使用单独的命令会有帮助,因为很明显badf00d会影响 current 分支,即使您将命名为git pull之类。

现在我们知道git merge确实是origin/master的一种选择,让我们深入研究--allow-unrelated-histories,然后看看它的作用。首先,我们将看看它在 的共同出发点,然后再来看它在没有的情况下进行的工作。

合并是关于合并从共同起点开始的更改

考虑一下上面引用的图,我将重画一下:

git merge

这表示无论谁在git merge上工作,他们都是从签出提交 A--B--C <-- topic / D--E--F--G <-- master (HEAD) 开始的。当时,提交topic可能是对{em> E的最后一次提交:

E

此后,有人在master上添加了两个提交,分别是D--E <-- master, topic master,有人(可能是其他人)在F上添加了三个提交,分别是现在是G链(topic的父提交为A-B-C)。

每个提交代表所有源文件的完整快照。因此,提交A包含其中的所有文件-以及,当您或任何人进行提交E时拥有的所有文件-永远保存为这种形式。您或该人从该已保存状态对任何文件进行的任何更改(例如,提交E)都会导致这些文件在E中处于新状态。 A中任何未更改文件都与A中的文件完全匹配。

为简单起见,我们假设有两个人在这里行事,“ you”和“ they”,并且您在A上进行了更改,最终导致了E的提交。然后,他们从masterG。因此,您和他们俩都从永久保存在提交A中的所有内容开始。您将得到永久保存在C中的内容。因此,Git可以通过简单的E找出您所做的更改,以比较提交G与提交git diff。同样,它们最终以E结尾,因此Git可以通过比较GC来找出相似的简单git diff对它们的改变。

  • E:您所做的更改
  • C:它们发生了什么变化
然后,

Git从提交git diff --find-renames hash-of-E hash-of-G中检出文件,即,您俩都是从 combines 开始的,对这些文件所做的更改,并使用 combined建立新的提交。 中的更改。这确定哪些文件/内容进入提交git diff --find-renames hash-of-E hash-of-C

E

新提交H的第一个父级是 A--B--C <-- topic / \ D--E--F--G---H <-- master (HEAD) ,它是H的尖端,也是您已签出的分支。它的第二个父级是G,您告诉master合并的那个。

请注意,当Git完成所有这些更改组合时,它对两个分支提示中的完全相同的所有文件都非常容易,因为无论情况如何,合并基础时,两个技巧匹配,因此两个文件都相同,并且两个都工作正常。如果您更改了文件X而没有更改文件X,以及您更改了文件Y而又没有更改文件X,那么这也很容易,因为再次声明,它可以只获取您或这些文件的版本。只有在您俩以不同的方式触摸了相同文件的地方,Git才需要努力工作。

不相关的历史记录

当两组提交之间没有 通用连接时,就会发生不相关的历史记录:

C

您的提交从git merge开始并向后进行,直到A--B--C <-- master (HEAD) J--K--L <-- theirs 为止。 C之前没有提交:A没有父母。

在这种情况下,它们的提交从A开始(我跳过了很多字母,以留出空间来插入我们的合并)。 A的父母是LL的父母是K,但是K也没有父母。因此根本没有共同的起点。

如果您告诉Git合并这些,Git只是假装有一个。假装的起点没有 no 个文件。 Git运行:

  • J:您所做的更改
  • J:它们发生了什么变化

当然,您从此差异中所做的更改是添加了每个文件(位于提交git diff empty-tree hash-of-C中)。他们所做的更改是,他们添加了每个文件(位于其提交git diff empty-tree hash-of-L中)。

如果文件具有不同的名称,则它们是不同的文件,并且没有问题:Git接受您或他们的文件。如果它们具有相同的名称,但内容完全相同,那么这里也没有冲突:Git可以接受您的(或他们的)名称。对于您和他们的文件具有相同名称但内容不同的所有文件,都会出现问题。就Git而言,您是从头开始的,他们也是,所以一切都冲突了。您必须从“一切冲突”输入中选择获奖内容或构建新文件。

一旦您解决了所有这些冲突并运行C使Git完成,Git就会像往常一样进行合并提交:

L

新提交具有两个父git merge --continueA--B--C--D <-- master (HEAD) / J--K---L <-- theirs ,并永久保存通过修复Git报告的冲突而构建的快照,否则{中的任何文件都完全相同{1}}和C或仅在 L中或仅在C中。

(“ Forever”有点太强大:保存的文件仅能持续到提交本身。但是,默认值是每次提交都可以永久存在。如果使提交消失,文件也会这样做。 )

答案 1 :(得分:0)

合并不相关的历史记录的过程是这样的:您想象每个历史记录的根源之前都有一个共同的祖先,根本没有内容。这是合并基础。