如何在 fork 和 master 分支之间获得 git 冲突

How do I get a git conflict between a fork and the master branch

我fork了一个项目,添加和修改了文件。

过了一会儿,我意识到我应该从 master 分支更新一些配置。我尝试 git-pull master 和其他东西,结果我丢失了一些文件(不知何故)。

现在我删除了整个目录(可能不是很聪明)并且我正在尝试从上次提交加载我的分支,如下所示:

> git clone [URL of the project]
> git checkout [code of the last commit in my fork]
> git pull origin master

它只说:

* branch            master     -> FETCH_HEAD
Already up-to-date.

什么都不做,也没有实施任何更改。我预计某些文件会发生一些冲突,所以我想知道如何到达两个版本(我的叉子和主版本)会显示冲突(通常与 >>>>>>>> HEAD .. .)

这里有一个基本问题:导致冲突的不是分支。或者,也许更好一点:分支是必要的,但还不够。 (但这也取决于我们如何定义branch。)

要在 Git 中发生冲突,您必须:

  • 执行三路合并,使用git merge1
  • 使用两个输入提交(这将来自分支,但这里重要的不是分支)
  • 提交图导致Git找到第三个合并基础提交,不同于其他两个输入,最后
  • 两次提交都对同一文件的同一行进行了更改。2

在所有这些中,术语 fork 完全无关紧要。一个分支只是一个克隆,具有一些特殊的语义,由任何托管提供商提供给你一个点击网络界面 "fork" 按钮,或网络可访问 "fork" API。如果你分叉了一个存储库,你就克隆了它。如果你克隆了你的克隆体,你就再次克隆了它。重要的是你有哪些 commits,通过他们的哈希 ID——这是他们的真实名字——即使你通过分支名称 find 那些提交。

每次提交都会记住其直接父代的哈希 ID,3 这会导致一个向后指向的链:

... <-F <-G <-H

其中 H 代表一些提交哈希 ID。提交 H 记住其前身提交的哈希 ID。我们没有使用丑陋的大哈希 ID,而是使用另一个字母来代表它,所以我们称之为 G。因此,提交 H 指向 (持有提交 G 的哈希 ID)。提交 G 依次指向 F,它继续向后指向。

在您自己的存储库中制作了一个克隆,或者只是一堆提交——所有重要的是 提交——你现在有更多像这样的东西:

          I--J   <-- branch1
         /
...--G--H
         \
          K--L   <-- branch2

分支 namesbranch1branch2 在这里,保存 last 提交的哈希 ID在分支机构。 Git 自动更新它们,例如,当您在分支上使用 git checkout 并进行新的提交时,这样当您进行新的提交时,分支会增长并且名称继续指向最后一次提交。所以现在他们分别指向提交 JL

如果您现在 运行 git checkout branch1git merge branch2,Git 将把提交 JL 作为您的两个输入提供,并会自己找到最好的 shared 提交,在这种情况下显然是提交 H,它在 both 分支机构。 Git 将:

  • H 中的所有文件与 J 中的所有文件进行比较——git diff --find-renames H J——看看 改变了什么,并且
  • H 中的所有文件与 L-git diff --find-renames H L 中的所有文件进行比较,看看 它们 发生了什么变化。

合并操作尝试合并这两组更改,将合并后的更改应用于 H 中的文件,以便创建一组可用于新快照中的文件合并提交。

如果合并成功,Git 将自己进行新的合并提交 M

          I--J
         /    \
...--G--H      M   <-- branch1 (HEAD)
         \    /
          K--L   <-- branch2

HEAD 表示这是我们所在的分支,因为我们在 branch1 上,这就是 Git 更新以保存哈希 ID 的名称新提交 M。提交 M 具有通过将组合更改应用于 H 中的快照而创建的快照。

如果存在合并冲突,Git 将停止而不进行 M。 Git 留下一堆烂摊子让你清理。4 你必须自己进行合并,然后告诉 Git 你已经成功解决了所有冲突并产生了正确 合并结果。然后,您使用 git merge --continuegit commit(其中之一)完成合并并提交 M 构建的快照。


1Git 也使用三向合并来实现 git cherry-pickgit revert,以及其他一些特殊情况,所以你可以在不使用 git merge 的情况下获得合并冲突。但是 git merge 是通常的来源,也是您所描述的内容,因此您可以将其视为 "when you run git merge" 而不会损失太多准确性。

注意 git pull 表示 运行 git fetch,然后 运行 第二个 Git 命令,通常是 git merge 。所以 git pull 只是获取 + 合并。

2关于 "the same" 的含义有一些棘手的细节,对于可以重命名的文件和可能只是邻接而不是重叠的行.但是同样,您可以将其视为 "different change to same lines of same file" = "conflict" 并且足够接近。

3对于合并提交,提交会记住其父项的所有——通常只有两个——因此不仅指向提交合并时所在的分支,也提交合并时合并分支的尖端提交。

4乱七八糟的merge状态占用了index,这里我们完全没有描述。 Git 还让您尽最大努力合并工作树中文件中的冲突更改,并留下 关于 git merge --continue 记录的合并的信息,其中 运行s git commit,或 git commit,选择进行合并提交。

您可以使用 git merge --abort 退出所有内容,这会将所有内容恢复到开始 git merge 之前的状态。