当只有一个人更改文件时,Git 合并会发生冲突

Dra*_* .C 0 git

我正在开发一项功能,并将所有更改合并到我们的测试基础设施团队提供的特定修补程序分支。该分支已合并到我们的发布分支中,没有冲突。后来,当测试基础设施团队尝试将发布分支合并回其他修补程序分支时,他们看到了很多合并冲突。

我的问题是,如果我是唯一处理这些文件的人,那么是什么导致了这些冲突。

而且,当我尝试在本地合并release分支和hotfix分支时,我还看到很多由于别人代码更改而导致的冲突。那么手动挑选我对修补程序分支的所有提交是解决此问题的唯一方法吗?

感谢你的帮助!

tor*_*rek 5

合并就是合并工作。这项工作是谁做的并不重要。重要的是提交中记录的工作

提交本身具有结构。每个提交都会存储两件事:

  • 每次提交都充当每个文件的完整快照。

  • 每次提交都会存储元数据:有关谁进行提交、何时(日期和时间戳)、原因(日志消息)等的信息。

每个提交都使用唯一但不可预测且看起来随机的哈希 ID进行编号。在任何给定提交的元数据中,Git 包含先前提交的原始提交哈希 ID。这将大多数提交形成简单的、向后看的链:

... <-F <-G <-H
Run Code Online (Sandbox Code Playgroud)

这里,H代表链中最后一次提交的哈希ID。CommitH包含早期 commit 的原始哈希 ID G。请记住,哈希 ID 很大、丑陋且不可预测,因此没有其他方法可以区分,例如,c6fc44e9bf85dc02f6d33b11d9b5d1e10711d125出现在之前或之后e9e5ba39a78c8f5057262d49e261b42a8660d5b9(如果H是一个且G是另一个)。除了让 Git 读取元数据之外,您无法在此处执行简单的小于或大于操作:ifH指向G,则G位于 之前H。同样,G向后指向F; 所以F先于GG先于H.

在 Git 中,我们可以根据这个“先于”概念构建的图是有向无环图或 DAG。DAG 提供 Git 执行git merge操作所需的内容。假设,从某个主分支在提交处结束的存储库开始,H如下所示:

...--G--H   <-- main
Run Code Online (Sandbox Code Playgroud)

我们添加两个分支br1br2,并在这两个分支上分别放置两个新的提交:

          I--J   <-- br1
         /
...--G--H
         \
          K--L   <-- br2
Run Code Online (Sandbox Code Playgroud)

(该名称main仍然存在,并且仍然指向 commit H;我刚刚将其从绘图中删除以使其变得杂乱)。 请注意,直到并包括的提交H都在所有分支上。

如果我们现在git checkout br1运行git merge br2Git:

  • 从 commit 开始J,因为这是我们从存储库中复制出来的 Git,现在通过 name 使用br1
  • 找到 commit L,因为br2指向L;
  • 找到合并基础提交。

一对分支尖端提交的合并基础是最佳共享(公共)提交。也就是说,Git 查看提交J并询问它是否在两个分支上。事实并非如此,因此 Git 必须返回 1 到I. 这也不在两个分支上,因此 Git 必须再次返回到H. 或者,等效地,Git 可以从 开始K并返回两次以到达H. 不过 ,现在的H答案是:是的,这个提交在两个分支上的。所以它是一个可行的合并基础。它也是最好的一个,由于我们不会在这里讨论的原因,所以它是合并基础提交。

因此,合并操作涉及提交HJL。此时不需要其他提交。Git 现在运行两个git diff操作:

  • HGit 将中的快照与 中的快照进行比较J。无论什么不同,都是“我们”在分支上改变的东西br1

  • HGit 将中的快照与 中的快照进行比较L。所不同的是“他们”在分支上改变的东西br2

就谁做出哪个承诺而言,“我们”和“他们”是谁并不重要。重要的是这三个提交中的快照以及这两个比较的结果。

git diff这里每次运行的输出是:

  • 已更改文件的列表,以及
  • 对于每个文件,如果将更改应用于H该文件的版本,则会生成同一文件的分支尖端版本。

Git 现在可以做的就是合并两组更改。这会保留我们的更改 ( H-vs- J) 并添加他们的更改 ( H-vs- L),或者同等地,保留他们的更改并添加我们的更改。只要不存在冲突,这两个操作之间就没有真正的区别。Git 将添加的更改应用到 中的文件H,结果是一个新的快照,该快照进入新的合并提交 M

          I--J
         /    \
...--G--H      M   <-- br1 (HEAD)
         \    /
          K--L   <-- br2
Run Code Online (Sandbox Code Playgroud)

合并提交会M导致名称br1前进,因为这是当前分支,并且向当前分支添加新提交会将分支名称移动到指向新提交。合并提交M有一个普通的快照,就像任何普通的非合并提交一样。M合并提交之所以成为合并提交,是因为它既指向 commit J(像往常一样),指向 commit L。也就是说,M两个父母,而不是通常的一个。

如果冲突,Git 的合并:

  • 在 Git 的索引中留下了混乱,我们必须清理它;
  • 将冲突留在文件的工作树版本中,我们可以用它来清理 Git 的索引;

并因错误而停止。我们清理一切,用 修复 Git 的索引git add,并使用git merge --continuegit commit告诉 Git 我们清理了所有东西,它应该继续并M立即进行合并提交。

这里冲突的根源并不相关。它必须来自 ( H-vs- J)-vs-( H-vs- L)。这三个提交的作者和提交者是无关的重要的是这些提交中的快照,它们生成了那些冲突的差异。 将成为合并提交的作者和提交者。现有的提交不能也不会被更改。此时,您的责任只是进行新的合并提交。