当出现错误“提示:您有不同的分支,需要指定如何协调它们”时如何合并。

nii*_*ico 158 git git-pull visual-studio

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

尝试将 Visual Studio Community(而不是 Visual Studio Code)中的 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.
Run Code Online (Sandbox Code Playgroud)

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

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

以及 如何处理此 Git 警告?“不鼓励在不指定如何协调不同分支的情况下进行拉取”

但他们都没有解释为什么会发生这种情况以及这些行为实际上做了什么。

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

谢谢

tor*_*rek 274

正如 Git 中常见的令人困惑的内容一样,其中涉及到一些历史。

\n

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

\n
    \n
  1. git pull运行git fetch您提供的 大多数(但不是全部)参数都git pull直接传递给git fetch. 所以git pull意味着运行git fetch并且git pull origin somebranch意味着运行git fetch origin somebranch

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

    \n
  4. \n
\n

进行第 2 步的原因非常简单:从其他 Git 存储库git fetch 获取新提交,将这些新提交填充到您自己的存储库中,您现在可以在其中访问它们。但随后它就停止了。您可以访问新提交,但实际上尚未使用新提交。要使用新的提交,您需要第二步。

\n

最初,第二步总是git merge。该git merge命令相当大且复杂,但它的含义很容易描述:Merge意味着合并工作 Git 会尝试获取你已经完成的工作(如果你已经完成了任何工作)以及他们已经完成的工作(如果他们已经完成了任何工作),并使用简单而愚蠢的自动化规则来组合这些工作。这些规则不知道你如何或为什么做这项工作,或者你所做的任何更改意味着什么。它们只是基于差异中的“行”工作。

\n

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

\n
    \n
  • 也许你没有做任何工作,他们也没有做任何工作。您没有新的提交。确实没什么可做的,也git merge什么都不做。

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

    \n
  • \n
  • 也许你没有做任何工作,而他们做了一些工作。你有一些新的提交。将你的缺乏工作与他们的实际工作结合起来很容易,git merge如果你允许的话,会走捷径。

    \n
  • \n
  • 也许你和他们都工作过。你有新的提交,并且你从它们那里得到了新的提交,并且git merge必须使用其简单而愚蠢的规则来组合工作。Git 在这里不能走任何捷径,你将得到一个成熟的合并。

    \n
  • \n
\n

The shortcut that Git may be able to take is to simply check out their latest commit while dragging your branch name forward. The git merge command calls this a fast-forward merge, although there's no actual merging involved. This kind of not-really-a-merge is trivial, and normally extremely safe: the only thing that can go wrong is if their latest commit doesn't actually function properly. (In that case, you can go back to the older version that does.) So a "fast forward" merge is particularly friendly: there's no complicated line-by-line merging rules that can go awry. Many people like this kind of "merge".

\n

Sometimes the shortcut is not possible, and sometimes some people don't want Git to take the shortcut (for reasons we won't cover here to keep this answer short, or short for me anyway). There is a way to tell git merge do not take the shortcut, even if you can.

\n

So, for git merge alone, that gives us three possibilities:

\n
    \n
  • nothing to do (and git merge is always willing to do nothing);
  • \n
  • fast-forward is possible, but maybe Git shouldn't do it; and
  • \n
  • fast-forward is not possible, which means this merge isn't trivial.
  • \n
\n

The git merge command has options to tell it what to do in all but the "nothing to do" case:

\n
    \n
  • (no flags): do a fast-forward if possible, and if not, attempt a real merge.
  • \n
  • --ff-only: do a fast-forward if that's possible. If not, give an error stating that fast-forward is not possible; do not attempt a merge.
  • \n
  • --no-ff: even if a fast-forward is possible, don't use the shortcut: attempt a full merge in every case (except of course the "nothing to do" case).
  • \n
\n

The git pull command accepts all of these flags and will pass them on to git merge, should you choose to have git pull run git merge as its step 2.

\n

But wait, there's more

\n

Not everyone wants Git to do merges. Suppose you have made one or two new commits, which we'll call I and J, and your git fetch from origin brings in two new commits that they made since you started, which we will call K and L. That gives you a set of commits that, if you were to draw them, might look like this:

\n
          I--J   <-- your-branch\n         /\n...--G--H   <-- main\n         \\\n          K--L   <-- origin/main\n
Run Code Online (Sandbox Code Playgroud)\n

You can fast-forward your main to match their origin/main:

\n
          I--J   <-- your-branch\n         /\n...--G--H\n         \\\n          K--L   <-- main, origin/main\n
Run Code Online (Sandbox Code Playgroud)\n

And, whether or not you do that, you can merge your commit J with their commit L to produce a new merge commit M:

\n
          I--J\n         /    \\\n...--G--H      M   <-- your-branch (HEAD)\n         \\    /\n          K--L   <-- origin/main\n
Run Code Online (Sandbox Code Playgroud)\n

But some people prefer to rebase their commits\xe2\x80\x94in this case I and J\xe2\x80\x94so that they come after commit L, so that the picture now looks like this:

\n
          I--J   [abandoned]\n         /\n...--G--H--K--L   <-- origin/main\n               \\\n                I'-J'  <-- your-branch\n
Run Code Online (Sandbox Code Playgroud)\n

Here, we have copied commits I and J to new-and-improved commits I' and J'. These commits make the same changes to L that I-J made to H, but these commits have different big-ugly-hash-IDs and look like you made them after the origin guys made their K-L commits.

\n

The git pull command can do this kind of rebasing:

\n
git switch your-branch\ngit pull --rebase origin main\n
Run Code Online (Sandbox Code Playgroud)\n

does this all in one shot, by running git fetch to get their commits, then running git rebase with the right arguments to make Git copy I-J to I'-J' as shown above. Once the rebase is done\xe2\x80\x94remember that, like git merge, it may have merge conflicts that you have to solve first\xe2\x80\x94Git will move the branch name your-branch to select the last copied commit: J' in this example.

\n

Not very long after git pull was written, this --rebase was added to it. And since many people want this sort of thing to happen automatically, git pull gained the ability to default to using --rebase. You configured your branch to do this (by setting branch.branch.rebase to true) and git pull would do a rebase for you. (Note that the commit on which your rebase occurs now depends on two things: the upstream setting of the branch, and some of the arguments you can pass to git pull. I've kept things explicit in this example so that we do not have to worry about smaller details, but in practice, you do.)

\n

This brings us to 2006 or 2008 or so

\n

At this point in Git's development, we have:

\n
    \n
  • git fetch: obtains new commits from somewhere else (an "upstream" or origin repository for instance), often updating origin/* style remote-tracking names;
  • \n
  • git merge: does nothing, or a fast-forward, or a true merge, of some specified commit or the branch's upstream;
  • \n
  • git rebase: copies some set of existing commits to new-and-improved commits, using a specified commit or the branch's upstream, then abandons the original commits in favor of the copies; and
  • \n
  • git pull: using the branch's upstream or explicit arguments, run git fetch and then run either git merge or git rebase.
  • \n
\n

Because git merge can take --ff-only or --no-ff arguments, git pull must be able to pass these to git merge if we're using git merge.

\n

As time goes on, more options start appearing, such as auto-stashing, rebase's "fork point", and so on. Also, it turns out that many people want rebasing to be their default for git pull, so Git acquires a new configuration option, branch.autoSetupRebase. When set to remote or always, this does what many of these folks want (though there are actually four settings today; I don't remember if it had four back then and have not bothered to check).

\n

Time continues marching on and we reach the 2020s

\n

By now\xe2\x80\x94some time between 2020 and 2022\xe2\x80\x94it has become clear that git pull does the wrong thing for many, maybe even most, people who are new to Git. My personal recommendation has been to avoid git pull. Just don't use it: run git fetch first, then look at what git fetch said. Then, if git fetch did a lot, maybe use git log next. And then, once you're sure whether you want git merge with whatever options, or git rebase also with whatever options, run that command. If you use this option, you are in full control. You dictate what happens, rather than getting some surprise from Git. I like this option: it's simple! You do need to run at least two commands, of course. But you get to run additional commands between those two, and that can be useful.

\n

Still, if a git pull brings in new commits that can be merged under git merge --ff-only, that often turns out to be what I want: do that fast-forward, or else stop and let me look around and decide whether I want a rebase, a merge, or whatever else I might want.1 And that often turns out to be what others want as well, and now git pull, run with no arguments at all, can be told to do that directly:

\n
git config --global pull.ff only\n
Run Code Online (Sandbox Code Playgroud)\n

achieves this.

\n

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

\n

所以,你需要选择一个。 默认值 git config pull.rebase false,但这是一个糟糕的默认值。我不推荐它。我确实推荐git config pull.ff only (though I still don't actually use it due to 15+ years of habits).

\n
\n

1一个现实世界的例子:我遇到了一些对我来说是个问题的错误。我对我知道是错误的代码进行了更改,但让完成了工作。我犯下了这个可怕的黑客行为。然后我等待上游做出更改。他们这样做了,我带来了新的提交。如果他们修复了错误,我想放弃我的修复,而不是合并或重新设置它。如果他们没有修复错误,我想重新调整我的黑客攻击(这可能需要也可能不需要一些调整)。“他们修复了错误吗”测试需要一些git pull无法单独测试的东西。

\n

2请注意,git pull 参数运行不应产生此类抱怨。我仍然不经常运行它,所以我不太确定错误是什么,但在新功能的第一轮或第二轮实施中,有一个错误会git pull不恰当地抱怨。我相信它已在 2.35 中得到修复,并且几乎可以肯定它已在 2.36 中得到修复,现在应该随时发布。

\n

  • 你对此有很多话要说,而且你显然比我更了解这个话题。但我仍然想知道为什么 Visual Studio 默认情况下没有配置为工作和合并更改(我认为这就是版本控制软件所做的。)而且我个人没有使用命令行。我只是使用IDE。这是否意味着 Microsoft 在 VS 中创建的版本控制无法使用? (5认同)
  • @CatherineIvanova:Git 中的 *分支名称* 指向单个提交(请参阅上面的“但是等等,还有更多”部分中的绘图)。我们运行 `git checkout &lt;name&gt;` 或 `git switch &lt;name&gt;` 并获取名称指向的提交。但现在看看第一张和第二张图:在第一张图中,`main` 指向`H`,在第二张图中,`main` 指向`L`,就像`origin/main` 一样。我们如何使第一个“成为”第二个?Git 的做法是检查 *commit* `L`(通过名称 `origin/main` 找到),然后使 *name* `main` 指向 `L`。 (2认同)

JGi*_*tin 62

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

   git config pull.rebase false
Run Code Online (Sandbox Code Playgroud)

  • 你在哪里运行该命令? (2认同)

l-p*_*tet 32

跑步git fetch && git merge通常对我有用。


小智 14

我遇到了同样的问题。对我来说解决这个问题的是运行以下代码行:

git config pull.rebase false
git pull
git commit -m "my commit message"
git push
Run Code Online (Sandbox Code Playgroud)

然后,要检查现在是否一切正常,请再次运行“git status”。


Bip*_*jan 5

我遇到了这个问题并修复了使用git merge origin/main mainwhere main 是目标分支。