合并与我自己的冲突

Merge conflict with myself

前一段时间我有一个合并冲突,我怀疑是我自己。现在我又遇到了同样的问题,这次我确定,我对冲突负责。

昨天我对我的分支进行了一些更改 commited 更改并 pushed 它们。这是第一次也是迄今为止唯一一次提交到分支(本地和远程)。

今天我做了更多更改(显然是在同一个文件中),添加更改并使用 commit --amend 提交它们并尝试 push 它们。

这是我遇到的合并冲突:Updates were rejected because the tip of your current branch is behind

我修复了更改,添加、提交并推送了它们。现在好了。

但我想从错误中吸取教训。 错误是什么?(我想这与使用git commit --amend有关)

What IS the mistake? (I assume it has something to do with using git commit --amend)

是的,这与使用 git commit --amend 有关。通过修改提交,您已经覆盖了 git history(通过更改提交的 SHA1)。 Amend 没问题,只要你还没有发布你的更改(推送到某个远程分支),但一旦你这样做了,你真的不应该乱搞历史,因为如果有人已经得到你的更改,然后当他们再次尝试拉取时,它可能会失败(提示:而不是修改提交,您应该使用更改进行新提交)。

现在,git 将拒绝使用您的分支更新远程分支(已修改 public 提交),因为您的分支的 head 提交不是当前 head 提交的 direct descendant您要推送到的分支。

如果您想用(修改后的)本地历史记录替换远程历史记录,则需要 force push。 如果你不这样做,Git 将拒绝推送,并要求你拉取(在这种情况下这没有帮助,因为你会合并相同的内容但不同的提交消息)。

不过,git push --force-with-lease要安全得多。关注link了解更多。

if something happens at 'origin' to the branch you are forcing or deleting since you fetched to inspect it, you may end up losing other people's work.

Somebody who is unaware of the decision to rewind and rebuild the branch may attempt to push to the branch between the time you fetched to rebase it and the time you pushed to replace it with the result of the rebasing.

We can make these pushes safer by optionally allowing the user to tell "git push" this:

I am forcing/deleting, based on the assumption that the value of 'branch' is still at this object. If that assumption no longer holds, i.e. if something happened to the branch since I started preparing for this push, please do not proceed and fail this push.

amend -- 更改历史记录。

初始状态

Origin: A
Local:  A

然后你提交并推送

Origin: A - B
Local:  A - B

然后你提交了 amend

Origin: A - B
Local:  A - C // local commit B replaced by new commit C

然后您尝试推送并收到消息,您需要先拉取。提交 C 包含来自 B 的更改,但它是一个不同的提交。拉动后你有冲突。

首先,这个:

Updates were rejected because the tip of your current branch is behind

不是合并冲突。合并冲突更具体。当 三向合并 试图合并两组不同的更改,但更改重叠时,就会发生这种情况。您正在 运行宁 git push不会合并任何东西

git push的反义词是而不是git pullgit pull命令是一个方便的命令,运行s 两个 Git依次为您命令。第一个命令是 git fetch——git push 相反的,或者至少,尽可能接近。 second命令git pull运行s通常是git merge可以导致合并冲突.或者,您可以将 Git 指向 运行 git rebase 秒; git rebase 也可能导致合并冲突。

只需记住一些关键事实

这会变得非常混乱,要很好地使用 Git,重要的是要了解 Git 实际上是如何做所有这些事情的,因为 Git 倾向于让原始实现显示通过。因此,作为基本概述,请记住以下几点:

  • 大多数时候,最重要的是提交
  • 每个提交都有一个 "true name",这是它的哈希 ID。哈希 ID 是那些又大又丑的 40 个字符,例如 face0ff(但更长)。没有两个不同的提交具有相同的 ID,并且 ID 完全由提交的内容决定。
  • 提交的内容有点详细但不会太长——要完整看一个,运行git cat-file -p HEAD,它只是将其打印出来——但要记住的主要重要事项是内容包括:

    • 父提交,这又是这些哈希之一;
    • 提交消息--amend 似乎可以让您编辑);和
    • 您提交的源代码树,即您拥有的任何git add-ed.


    当您使用 git commit --amend 时,这个 似乎 会更改提交,但实际上并没有。不能!提交的 ID 由其内容决定。如果您更改内容,您会得到一个新的,因此 不同 提交。

  • 因为提交中有它们的父 ID,Git(和你)可以绘制一个 提交,从最近的开始那些并向后工作。 Git 运行 一切都是倒退的:我们喜欢将分支视为前进。如果我们有一个提交的时间线,我们在其中提交 A,然后是 B,然后是 C,依此类推,我们希望他们去 "A then B then C":

    A -> B -> C
    

    但是 Git 将它们倒过来,所以它们实际上是:

    A <- B <- C
    
  • 因此,分支名称 仅指向 tip-most 提交。这就是为什么 Git 一直在谈论 "the tip of your branch".

  • 而且,这意味着我们要像这样绘制图形:

    A--B--C   <-- branch
    

    名称 branch 指向 最新的 提交,C。 Git 然后倒退,从 CB 再到 A。大多数时候我们不必担心那部分,但我们确实需要记住 name 只是指向提示提交。

这导致树枝生长的方式

分支名称指向分支提示。假设你有这些链之一:

A--B--C   <-- branch

现在您进行新的提交 D。 Git 所做的是使 D 的父对象为 C,然后使 branch 指向 D。我们现在可以画出来了:

A--B--C--D   <-- branch

git commit --amend 的作用现在很容易看出来。不是通过使 D 指向 CD 附加到 ,而是使 D 指向 [=33] =](即 C 的父级)。然后它通过使 branch 指向 D:

C 推到一边
     C
    /
A--B--D   <-- branch

现在我们可以看到 git push 也做了什么

当你 运行 git push 时,你的 Git 会打电话给其他人 Git 并进行一些交谈。你的 Git 告诉他们 Git 关于 你的 提交。他们的 Git 告诉你的 Git 关于 他们的 提交。然后,您的 Git 会向他们 Git 发送您的一些提交 - 无论您有哪些,他们没有,您已要求 Git 发送。

假设他们已经有 A--B--C 而你有 A--B--D。通过请求你的 Git 到 git push branch,你可以让你的 Git 将你的 D 发送给他们(它也会发送你的 AB,但他们已经有了;同样,这些都是由那些又大又丑的哈希 ID 完成的,而不是简单的单字母名称,但思路是一样的。

接下来就是最后一部分了,这就是问题所在。您的 Git 要求他们的 Git 将 他们的 branch 设置为指向与您的 branch 相同的提交。也就是说,你要求他们将 他们的 branch 设置为指向 D.

您的 D 指向 B,而不是 C。如果他们将 他们的 branch 设置为指向新的共享 D,他们将 "lose" 他们的 C.

当然,因为你的amend,你希望他们"lose"他们的C,就像你Git 把它推到一边。所以你可以使用--force。强制标志改变了你的 Git 的礼貌请求——"please, if you will, change your branch to point to D—into a command: "立即将你的 branch 指向 D!”他们仍然可以拒绝,但通常他们会服从。

如果那个只有输了C,那很好:这就是你想要的。但是,如果他们的 Git 是其他人也推送到的,也许是第三个人,拥有第三个 Git 存储库,已经编写了一个提交 E 并将其推送到他们的 Git,所以他们实际上有A--B--C--E。如果您向他们发送您的 D,它指向 B,并让他们将他们的 branch 设置为指向您的 D,他们将失去 C--E ,不仅仅是C提交。

这就是为什么(以及何时)修改不好

如果您修改已共享的提交,则其他人可能已在其上构建。如果你现在"take it away",你可能会给这个别人制造麻烦。

另一方面,如果您从未共享过提交,或者如果另一个 Git 是您自己的私有存储库,或者如果您和所有第三方事先同意 这种 "taking away" 是正常的和预期的,你在这里不会有问题。

否则 - 如果 "taking away" 不好 - 那么当另一个 Git 有一个提交时,你应该确保你推送的任何新提交只是 添加到 他们的承诺。您可以通过合并或变基来做到这一点,这就是 git mergegit rebase 的用武之地。并非完全巧合的是,这两个命令是 git pull 的后半部分——但让我们保留它再来一次。