如果同时在单独的分支中更改了文件结构,git在合并时如何处理内容更改?

时间:2019-06-07 21:11:04

标签: git bitbucket

为简单起见,我将用此示例描述情况。

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

enter image description here

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

enter image description here

同时(第2天),在master分支中,文件Class1.java和Class2.java被其他属性更改(从另一个分支合并到master)。

我的问题是,何时将分支合并回master(第3天)... git将保留master上其他人对Class1.java和Class2.java所做的更改,否则它将替换它们和我在分支机构中拥有的那些?

3 个答案:

答案 0 :(得分:1)

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

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

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

git merge theirbranch

将提交作为其合并的第二个提交中的第二个,在其分支的顶端插入。

第三次提交是大多数魔术发生的地方。 Git自动找到第三次提交。 Git将此称为合并基础,它是从提交图派生的。在某些情况下,很容易看出它的来源。

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

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

此结构的合并基础只是提交*

在复杂的设置中,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

由于这些更改来自相同的 source 文件,因此Git现在将您的更改与其更改结合在一起。它尝试将相同更改(包括重命名)应用于基本文件。因此,Git获得了基本提交版本folder1/src/Class1.java,将您的更改及其更改(可能有冲突也可能没有冲突)组合在一起,并将结果作为folder2/src/Class1.java放置在索引和工作树中,并使用( )重命名。

如果Git无法使用重命名的文件标识原始的基本提交文件,则所有这些组合都将失败。因此,您可以运行相同的git diff --find-renames,也许与--name-status一起运行,以跳过实际的差异,仅查看匹配的内容。如果正确的事情相匹配,git merge将会做正确的事情。

如果正确的东西匹配,则可以尝试调整Git的“重命名阈值”,将其指定为数字--find-renames=num。该数字是Git的相似性索引的限制。当Git比较两个提交(例如合并基础和您当前的提交)时,如果合并基础具有文件d1/d2/file.ext但您的提交没有,并且您的提交具有d3/d4/other.ext则该基础没有,Git比较两个文件的内容。然后计算相似度指数。大致来说,这是保持不变的文件的数量; 最高达到100%相似,并且如果文件的任何部分都不匹配,则低至0%相似。默认设置是配对至少50%相似的文件。如果一个来源与五个目的地匹配,只要满足阈值,Git就会采用相似性指数最高的配对。

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

如果有多个合并基础

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

  • -s recursive(默认设置):Git首先通过在它们之上或多或少运行git merge来合并合并基础。最终的提交将成为合并基础。
  • -s resolve:Git以(显然)随机的方式选择一个合并基础,并使用它。

其余所有内容都相同,但是请注意,如果递归合并遇到合并冲突,Git只会提交合并冲突本身。然后,这种冲突的合并结果将成为输入基础,通常会导致看起来很奇怪的冲突。

答案 1 :(得分:0)

Git会进行增量更新,只有您更改的行将被合并。如果两个或两个以上的人修改相同的行,您将遇到合并冲突,并且需要在合并更改之前对其进行修复。

https://www.atlassian.com/git/tutorials/using-branches/merge-conflicts

答案 2 :(得分:0)

  

git是否会保留master上其他人对Class1.java和Class2.java所做的更改,或者将其替换为我在分支机构中拥有的更改?

是的,Git的合并操作通常会注意到您已经重命名了一些文件(将它们移到了另一个目录中-这是一个重命名操作)。在此期间,其他人在原始位置引入的更改被保留。

但是,这仅在您不重用一侧的路径folder1/src/Class*.java且另一侧在合并发生之前也没有引入Folder2/src/Class*.java的情况下才有效,因为那样会使Git的重命名检测失败。