遇到错误时如何合并 "Hint: You have divergent branches and need to specify how to reconcile them."

How to Merge when you get error "Hint: You have divergent branches and need to specify how to reconcile them."

我正在与另外 1 位开发人员合作,他们创建了一个需要与 master 合并的分支。

尝试将 Visual Studio 社区(而非 Visual Studio 代码)中的 Git 拉入 Bitbucket 存储库时出现此错误

如果我尝试推送它说“无法推送,因为您的本地分支在远程分支后面”。

这是错误:

Hint: You have divergent branches and need to specify how to reconcile them.
Hint: You can do so by running one of the following commands sometime before
Hint: your next pull:
Hint: 
Hint:   git config pull.rebase false  # merge
Hint:   git config pull.rebase true   # rebase
Hint:   git config pull.ff only       # fast-forward only
Hint: 
Hint: You can replace "git config" with "git config --global" to set a default
Hint: preference for all repositories. You can also pass --rebase, --no-rebase,
Hint: or --ff-only on the command line to override the configured default per
Hint: invocation.
Git failed with a fatal error.
Git failed with a fatal error.
Need to specify how to reconcile divergent branches.

我发现了各种讨论这个的东西,例如

https://laracasts.com/discuss/channels/code-review/git-pull-error-pulling-without-specifying-how-to-reconcile-divergent-branches-is-discouraged?page=1&replyId=773818

但其中 none 解释了为什么会发生这种情况以及这些操作的实际作用。

如何将其他分支中的内容合并到 master 中,为什么会出现此消息以及提示中的所有建议有什么作用?

谢谢

Git 中经常出现令人困惑的内容,其中涉及一些历史。

首先要知道的是 git pull 做的事情太多了。好吧,对于某些人(我)来说,它做的太多了;其他人喜欢它做了这么多;但实际上,它做了两项工作,每项工作都有自己独立的 Git 命令:

  1. git pull运行s git fetch。大多数,但不是全部,你给 git pull 的参数直接传递给 git fetch。所以 git pull 表示 运行 git fetchgit pull origin somebranch 表示 运行 git fetch origin somebranch.

  2. 假设第一步成功,git pull 运行第二个 Git 命令。

进行第 2 步的原因很简单:git fetch 从其他 Git 存储库获取新提交,填充这些新提交到您自己的存储库中,您现在可以在其中访问它们。但随后 停止 。您可以访问 新提交,但是没有任何东西真正使用 新提交。要使用 新提交,您需要第二步。

最初,第二步总是 git mergegit merge 命令非常庞大和复杂,但它的含义很容易描述:Merge 表示 combine work. Git 将尝试获取您已完成的工作(如果您已完成)和他们已完成的工作(如果他们已完成),并使用简单而愚蠢的自动化来合并这些工作规则。这些规则没有关于你如何或为什么做这项工作的线索,或者你所做的任何更改 意味着 。它们只是基于差异中的“线”工作。

然而,这里有四种可能性:

  • 也许你没有工作,他们也没有工作。你没有新的提交。几乎无事可做,git merge 什么也不做。

  • 也许你做了一些工作,他们什么也没做;你没有新的提交;无事可做,git merge 又什么都不做。

  • 也许你没有工作,他们做了一些工作。你有一些新的提交。将您的 lack-of-work 与他们的实际工作相结合很容易,如果您允许,git merge 会走捷径。

  • 也许你和他们都工作了。你有新的提交 你从他们那里得到了新的提交,并且 git merge 必须使用它的 simple-and-stupid 规则来组合工作。 Git 不能在这里走捷径,你会得到一个 full-blown 合并。

Git可能可以采取的捷径是简单地检查他们最新的提交,同时向前拖动你的分支名称。 git merge 命令将其称为 fast-forward 合并 ,尽管实际上并没有涉及 合并。这种 not-really-a-merge 是微不足道的,而且通常非常安全:唯一 可以 出错的地方是他们最新的提交实际上没有正常运行。 (在那种情况下,您可以返回到旧版本。)所以“快进”合并特别友好:没有复杂的 line-by-line 合并规则可能出错。很多人喜欢这种“合并”。

有时走捷径是不可能的,有时有些人不希望 Git走捷径(原因我们不会在这里介绍以保持这个答案很简短,或者对我来说很简短)。有办法告诉git merge 不要走捷径,即使可以.

因此,仅对于 git merge,这给了我们三种可能性:

  • 无所事事(而git merge总是愿意无所事事);
  • fast-forward是可以的,但也许Git不应该这样做;和
  • fast-forward 是不可能的,这意味着这个合并不是微不足道的。

git merge 命令有选项告诉它在除了“无事可做”的情况下做什么:

  • (无标志):如果可能,请执行 fast-forward,否则,请尝试真正的合并。
  • --ff-only:如果可能的话做一个fast-forward。如果不行,报错说fast-forward是不可能的;不要尝试合并。
  • --no-ff:即使 fast-forward 是可能的,也不要使用快捷方式:在每种情况下尝试完全合并(当然“无事可做”的情况除外)。

git pull 命令接受所有这些标志并将它们传递给 git merge,如果您选择 git pull 运行 git merge 作为它的第 2 步。

等等,还有更多

不是每个人都希望 Git 进行合并。假设你做了一两个新的提交,我们称之为 IJ,而你的 origingit fetch 带来了两个新的提交 他们 自你开始以来,我们称之为 KL。这给了你一组提交,如果你要绘制它们,可能看起来像这样:

          I--J   <-- your-branch
         /
...--G--H   <-- main
         \
          K--L   <-- origin/main

您可以 fast-forward 您的 main 来匹配他们的 origin/main:

          I--J   <-- your-branch
         /
...--G--H
         \
          K--L   <-- main, origin/main

而且,无论你是否这样做,你都可以合并你的提交J和他们的提交L以产生一个新的合并提交 M:

          I--J
         /    \
...--G--H      M   <-- your-branch (HEAD)
         \    /
          K--L   <-- origin/main

但有些人更喜欢 rebase 他们的提交——在本例中是 IJ——这样他们就会 在[之后] =280=] commit L,这样图片现在是这样的:

          I--J   [abandoned]
         /
...--G--H--K--L   <-- origin/main
               \
                I'-J'  <-- your-branch

在这里,我们复制提交IJ到new-and-improved提交I'J' .这些提交对 L 进行了与 I-JH 相同的 更改 ,但是这些提交具有不同的 big-ugly-hash-IDs 并且看起来像你 之后 origin 人做出了 K-L 的承诺。

git pull命令可以做这种变基:

git switch your-branch
git pull --rebase origin main

通过 运行ning git fetch 获得他们的提交,然后 运行ning git rebase 使用正确的参数使 [=385] 一次完成所有这些=] 复制 I-JI'-J' 如上所示。一旦 rebase 完成——记住,像 git merge,它可能有你必须首先解决的合并冲突——Git 会将分支名称 your-branch 移动到 select最后复制的提交:本例中的 J'

写完git pull没多久,就加了这个--rebase。由于许多人希望这种事情 自动发生 git pull 获得了 默认 使用 --rebase 的能力.您将分支配置为执行此操作(通过将 branch.<em>branch</em>.rebase 设置为 true)并且 git pull 可以给你的 rebase。 (请注意,你的 rebase 现在发生的提交取决于两件事:分支的 upstream 设置,以及你可以传递给 git pull 的一些参数。我'在此示例中,我们将事情保持明确,这样我们就不必担心较小的细节,但在实践中,您会担心。)

这让我们来到了 2006 年或 2008 年左右

在 Git 的开发中,我们有:

  • git fetch:从其他地方(例如“上游”或 origin 存储库)获取新提交,经常更新 origin/* 样式 remote-tracking 名称;
  • git merge: 什么都不做,或者fast-forward,或者某个指定的提交或者分支的上游的真正合并;
  • git rebase:使用指定的提交或分支的上游,将一些现有提交复制到 new-and-improved 提交,然后放弃原始提交以支持副本;和
  • git pull:使用分支的上游或显式参数,运行 git fetch 然后 运行 git mergegit rebase

因为 git merge 可以接受 --ff-only--no-ff 参数,git pull 必须能够将这些传递给 git merge if 我们正在使用 git merge.

随着时间的推移,更多的选项开始出现,比如auto-stashing、rebase的“fork point”等等。此外,事实证明许多人 想要 变基为 他们的默认值 git pull,因此 Git 获得了一个新的配置选项,branch.autoSetupRebase。当设置为 remotealways 时,这会满足许多人的要求(尽管今天实际上有四个设置;我不记得当时是否有四个,也懒得去检查) .

时间继续前进,我们到了 2020 年代

到现在为止——2020 年到 2022 年之间的某个时间——很明显 git pull 做了 错误的事情 对许多,甚至可能是大多数新手来说到 Git。我个人的建议是 避免 git pull。干脆不要用了:先运行 git fetch,再看看git fetch说的。然后,如果 git fetch 做了很多,也许接下来使用 git log。然后然后,一旦你确定你是否想要 git merge 与任何选项,或者 git rebase 也与任何选项,运行那个命令如果您使用此选项,您将拥有完全的控制权。您决定会发生什么,而不是从 Git. 那里得到一些惊喜我喜欢这个选项:它很简单!当然,您确实需要 运行 至少两个命令。但是你得到运行额外的命令这两个之间,这可能很有用。

不过,如果 git pull 引入新的提交, 可以 合并到 git merge --ff-only 下,这通常是我想要的:做fast-forward,否则停下来让我环顾四周,然后决定我是否需要变基、合并或其他任何我可能想要的东西。1 结果通常是也成为别人想要的,现在 git pull,运行 完全没有参数,可以告诉直接这样做:

git config --global pull.ff only

实现这个。

同时,您在问题中显示的提示中的其他两个 git config --global 命令使第二个命令成为合并或变基。所以现在,在2022年,很容易告诉git pull去做想要它做的事。此外,似乎 Git 维护者已经接受了我的观点: git pull 没有 一些先见之明 不好 ,新手不要用。所以他们已经设置 git pull 现在 要求 你选择这三个选项之一,如果你想 运行 它没有参数。2

因此,您需要选择一个。 旧的默认值git config pull.rebase false,但这是一个糟糕的默认值。我不推荐它。我推荐git config pull.ff only(尽管由于15年以上的习惯,我仍然没有实际使用它)。


1一个 real-world 示例:我遇到了一些对我来说很麻烦的错误。我更改了我知道是错误的代码,但让 me 完成了 my 的工作。我犯下了这个可怕的黑客行为。然后我等待上游进行更改。他们这样做了,我带来了新的承诺。如果他们修复了 错误,我想删除 我的修复,而不是合并或变基。如果他们 没有 修复错误,我想重新设置我的 hack(可能需要也可能不需要一些调整)。 “他们修复了错误”测试需要一些东西 git pull 不能单独测试。

2注意 运行ning git pull with arguments 不应该产生这种抱怨.我仍然不 运行 太多,所以我不太确定错误是什么,但是在新功能实施的第一轮或第二轮中,有一个错误 git pull 会抱怨不当。我相信它已在 2.35 中修复并且几乎肯定它已在 2.36 中修复,现在应该随时发布。

我在使用 Visual Studio 时遇到了这个问题 - 以下命令为我解决了这个问题

   git config pull.rebase false