什么是合并冲突?

已完成

在这里,我们将讨论合并冲突解决如何帮助开发人员从两个重叠的源中产出最佳结果。

GitHub 流

除提供协作软件开发平台外,GitHub 还提供了一个既定工作流,旨在优化其各种功能的使用。 虽然本单元专门介绍合并冲突,但建议你先查看了解 GitHub 流

合并分支

请考虑这样一种方案:开发人员根据 main 创建一个名为 feature-branch 的分支,并创建两个提交。 随着此工作的进行,其他人将不相关的拉取请求合并到 main 中。 当开发人员尝试将 feature-branch 合并回 main 时,会发生什么情况?

答案是:视情况而定。

虽然 feature-branch 是从 main 创建的,但它并不基于分支本身。 而是基于当时 main 的 HEAD 提交。 从那时起,它就无法识别已应用到 main 的所有提交。 它当前跟踪的提交不一定会折叠到分支的当前状态而不覆盖最近的更改。

如果事实证明,自创建分支以来,feature-branch 提交与对 main 进行的并行提交不重叠,那么就不会出现问题。 可以添加新文件。 可以删除未改动过的文件。 可以在 feature-branch 中更改在 main 中更改过的代码行,只要并行工作自 feature-branch 创建以以来未对其进行更改。

A pull request with no merge conflicts.

但是,如果两组提交都包括对同一代码行的更改,该怎么办? 由于合并冲突,此合并尝试将失败。

A merge conflict.

什么是合并冲突?

当开发人员尝试合并会无意中覆盖并行更改的更改时,将引发合并冲突。 这些其他更改合并到基分支中的方式并不重要。 Git 不会为了支持一组更改而自动覆盖另一组更改。 相反,它会将这些更改指向尝试进行合并的人员,以便他们可以在尝试再次合并之前在其比较分支上解决这些更改。

A pull request with merge conflicts.

解决合并冲突

为帮助解决合并冲突,GitHub 会生成一个临时混合文件,其中包含每个分支间的差异。 约定是,来自比较分支的文本显示在基分支上方,用一行等于号 (=======) 分隔。

Resolving a merge conflict.

如果只是细微的更改,则可以使用此视图直接编辑文件。 如果你决定保留它,则最终结果会被提交到比较分支。 或者,如果涉及较多合并,则你可能会更倾向于使用其他开发工具来进行处理。 无论采用哪种方式,都不要忘记在提交之前从代码中删除任何分支标记。 如果你在提交冲突解决时忘记删除这些标记,它们将保留在文件中且不会被注释禁止。

注意

本单元讨论如何解决浏览器上下文中的合并冲突。 还有许多开发平台(如 Visual Studio)可提供集成的合并冲突解决体验。

在分支上解决所有合并冲突后,可以重试合并。

避免合并冲突

某些合并冲突无法避免。 对于等待获得批准的其他拉取请求,任何合并都可能产生合并冲突。 不过,可通过一种有效的方法来降低合并冲突的复杂性,那就是经常拉取分支。

及早并经常拉取

git pull 命令会向下拉取尚未应用于当前分支的任何基分支提交。 该命令在概念上类似于 Get Latest 命令,许多版本控制系统都使用该命令以允许将本地代码更新到最新版本。 拉取分支的更新时,将合并自分支创建(或上一次提取)以来发生的所有更改。

向分支拉取更新可能会导致合并冲突,但这没有关系。 反正晚些时候你都会收到它们,而且越早收到它们,它们通常就越容易处理。

除降低合并冲突的影响外,拉取更新还使你能够在工作时将提交的更改集成到分支中。 这样做可以使你尽早阻止潜在问题的发生。 例如,其他文件中的类定义可能会更改,导致代码无法再编译。 此更改不会在你随后进行合并时导致合并冲突,但如果不事先测试它,它可能会中断生成。 最佳做法是经常拉取更新以使分支尽可能接近其基。

使用 git 变基整理历史记录

git rebase(或 git pull --rebase)命令会重写分支历史记录,以使用基分支的当前 HEAD 提交作为其基分支。 换句话说,你的分支会被更新,使其行为如同它只是从当前状态的基分支所分支而来的。 这样的变基意味着,你所做的所有更改都会与最新状态的基分支进行比较,而不会与最初从中进行分支的原始提交进行比较。 变基可以使历史记录在最终合并后更易于跟踪,因为提交将以线性方式遵循先前的并行提交。 建议的做法是,在合并上游之前,立即对分支进行变基处理。

了解有关 Git 变基解决 Git 变基后的合并冲突的详细信息。