首页 > 解决方案 > 如果文件结构同时在单独的分支中更改,git如何处理合并时的内容更改?

问题描述

为简单起见,我将用这个例子来描述这种情况。

我在(第 1 天)为我的项目创建了一个 git 分支,结构如下:

在此处输入图像描述

我立即(仍在第 1 天)将结构更改为以下内容:

在此处输入图像描述

与此同时(第 2 天),文件 Class1.java 和 Class2.java 已在 master 分支中由其他 prople 更改(从其他分支合并到 master)。

我的问题是,当我将分支合并回 master 时(第 3 天)...... git 会保留 master 上其他人对 Class1.java 和 Class2.java 所做的更改,或者它将替换为我的分行里有那些?

标签: gitbitbucket

解决方案


当你运行时git merge <thing>,Git 必须找到三个提交。

三个提交之一是您当前的提交。(您的索引和工作树应该匹配它,并且前端命令通常会强制执行此操作。在“脏”情况下git merge运行通常是不明智的,尽管一直这样做。我建议避免使用,部分原因是。)git mergegit stash applygit stash

当然,三个提交之一是您使用<thing>参数命名的提交:

git merge theirbranch

选择其分支顶端的提交作为要合并的两个提交中的第二个。

第三提交是最神奇的地方。Git 会自动找到第三次提交。Git 将此称为合并基础,它源自提交图。在某些情况下,很容易看出这是从哪里来的。

假设您的分支及其分支具有非常简单的发散结构:

          o--...--o   <-- yourbranch (HEAD)
         /
...--o--*
         \
          o--...--o   <-- theirbranch

这个结构的合并基础就是 commit *

在复杂的设置中,Git 仍然会自行找到合并基础:合并基础是要合并的两个分支提示提交的最佳共同祖先。但是,您可以在合并之前进行调查:

git merge-base --all theirbranch

会告诉你哪些提交是合并基础。理想情况下只有一个;当有两个或更多时,Git 有一个问题——Git 会解决这个问题,但是 (a) 它非常罕见,并且 (b) 它与此描述的其余部分有点混乱,所以让我们暂时忽略这个问题。:-)

只找到一个合并基础后,Git 现在执行以下操作:

  • git diff --find-renames base your-commit看看你改变了什么;
  • git diff --find-renames base their-commit看看他们改变了什么。

也就是说,Git 运行git diff 两次,使用相同的(通用的、共享的)基本提交。

鉴于您上面描述的情况,要么重命名了一些文件,要么他们重命名了一些文件——或者,很可能,你们俩都重命名了一些文件。该--find-renames选项指示git diff发现这些重命名。(在这种情况下,是您重命名了文件。)

重命名查找并不完美,但在大多数情况下,它完全符合需要。Git 会发现你们中的哪些人重命名了哪些文件。这允许 Git识别合并基础文件(应该是)folder1/src/Class1.java与重命名的文件folder2/src/Class1.java。Git 记得也发生了重命名。

他们更改的差异中,Git在最终提交中folder1/src/Class1.java使用未重命名的原始文件标识原始文件。folder1/src/Class1.java

由于这些更改来自相同的文件,Git 现在将您的更改与其更改结合起来。它尝试将相同的更改(包括重命名)应用到基本文件。因此,Git 获取基本提交的版本folder1/src/Class1.java,将您的更改及其更改(可能会或可能不会冲突)结合起来,并将结果放在您的索引和工作树中folder2/src/Class1.java,并采用(单个)重命名。

如果 Git 无法用重命名的文件识别原始的基本提交文件,那么所有这些组合都会失败。所以你可以运行相同的git diff --find-renames,也许--name-status跳过查看实际的差异,只看匹配的内容。如果正确的事情得到匹配,git merge就会做正确的事情。

如果正确的事情没有得到匹配,您可以尝试调整 Git 的“重命名阈值”,指定为. 该数字是 Git 的相似性指数的限制。当 Git 比较两个提交时,例如合并基础和您当前的提交,如果合并基础有文件但您的提交没有,并且您的提交有基础没有,Git 会比较两个文件的内容。然后它计算相似度指数。粗略地说,这是未更改的文件数量;--find-renames=numd1/d2/file.extd3/d4/other.ext它的相似度最高为 100%,如果文件的任何部分都没有匹配,则相似度低至 0%。默认设置是配对至少 50% 相似的文件。如果一个源匹配五个目标,只要满足阈值,Git 就会采用相似度指数最高的配对。

git merge命令采用相同的参数,但拼写为。-X find-renames=number

如果有多个合并基地

如果git merge-base --all为您提供了多个合并基础,以下是 Git 处理该问题的方式:

  • -s recursive(默认):Git 首先通过在合并基础上运行git merge或多或少地合并合并基础。结果提交成为合并基础。
  • -s resolve:Git(显然)随机选择一个合并基础,并使用它。

其余的都是一样的,但是请注意,如果递归合并遇到合并冲突,Git 只会自己提交合并冲突。这个冲突的合并结果然后成为输入基础,这通常会导致看起来很奇怪的冲突。


推荐阅读