Git:只有一些未修改但已删除的文件是合并冲突为什么? TL; DR 长 git merge如何合并更改

问题描述

我试图了解为什么会发生这种情况:

我从mybranch分支了master,并进行了几次提交,这些提交涉及文件ABC。同时,其他人已将masterX以及其他一些更改一起提交到Y的提交中。当我将master合并到mybranch中时,git status告诉我X被“取消合并”,Y被“删除”。为什么对这些文件进行不同的处理?

我相信,这是为了减少现实生活中http://github.com/borglab/SwiftFusion上发生的事情,

[编辑”:我对此有误,因此上面的描述不准确。 @torek的answer很好而且很有启发性,所以我不会尝试删除此问题]

标签{{1}处有master,在标签conflict-merge-source处有mybranch,但是我会发布实际的仓库,以防万一。这两个文件conflict-merge-target(冲突)和Tests / SwiftFusionTests / Core / FixedShapeTensorTests.swift(已删除)。

解决方法

TL; DR

两个文件之一存在修改/删除冲突,而另一个文件根本没有冲突。这两个文件之一没有冲突的原因是,合并的“一侧”根本没有碰到文件,而另一侧完全没有碰到。

对于有冲突的文件,Git会将修改后的文件保留在工作树中,并将冲突保留在Git的索引中,您必须在其中解决它。 如何解决该问题取决于您,但是如果解决方案是“保留删除”,则可以在该名称上使用git rm,如果要保留修改后的文件,则可以使用git add。无论哪种方式,Git的 index (建议的 next 提交)现在都可以更新,并且可以解决此特殊冲突。

我克隆了the repository in question并发现了以下内容:

$ git merge-base --all conflict-merge-source conflict-merge-target
a07af749bdd416c5217c363b3f1da509c58d2d14
$ base=$(git merge-base --all conflict-merge-source conflict-merge-target)
$ git diff --find-renames --name-status $base conflict-merge-source
M       Sources/BeeDataset/BeeFrames.swift
M       Sources/SwiftFusion/Core/DataTypes.swift
D       Sources/SwiftFusion/Core/FixedShapeTensor.swift
M       Sources/SwiftFusion/Core/MathUtil.swift
A       Sources/SwiftFusion/Core/TensorVector.swift
M       Sources/SwiftFusion/Image/OrientedBoundingBox.swift
A       Sources/SwiftFusion/Inference/AppearanceTrackingFactor.swift
M       Sources/SwiftFusion/Inference/FactorGraph.swift
M       Sources/SwiftFusion/Inference/FactorsStorage.swift
M       Sources/SwiftFusion/Inference/JacobianFactor.swift
A       Sources/SwiftFusion/Inference/PPCA.swift
M       Sources/SwiftFusion/Inference/PPCATrackingFactor.swift
M       Sources/SwiftFusion/Optimizers/CGLS.swift
M       Sources/SwiftFusion/Optimizers/LM.swift
M       Sources/SwiftFusionBenchmarks/Patch.swift
M       Tests/BeeDatasetTests/BeeDatasetTests.swift
A       Tests/BeeDatasetTests/BeePPCATests.swift
D       Tests/SwiftFusionTests/Core/FixedShapeTensorTests.swift
M       Tests/SwiftFusionTests/Image/PatchTests.swift
M       Tests/SwiftFusionTests/Inference/FactorGraphTests.swift
R062    Tests/SwiftFusionTests/Inference/PPCATrackingFactorTests.swift
        Tests/SwiftFusionTests/Inference/PPCATests.swift
M       Tests/SwiftFusionTests/Inference/SwitchingMCMCTests.swift
M       Tests/SwiftFusionTests/Optimizers/LMTests.swift
$ git diff --find-renames --name-status $base conflict-merge-target
M       Sources/SwiftFusion/Core/FixedShapeTensor.swift
M       Sources/SwiftFusion/Inference/AnyArrayBuffer+Differentiable.swift
R050    Sources/SwiftFusion/Inference/ValuesStorage.swift
        Sources/SwiftFusion/Inference/AnyArrayBuffer+Vector.swift
M       Sources/SwiftFusion/Inference/ArrayBuffer+Differentiable.swift
M       Sources/SwiftFusion/Inference/ArrayBuffer+Vector.swift
M       Sources/SwiftFusion/Inference/PenguinExtensions.swift
M       Tests/SwiftFusionTests/Inference/AnyArrayBufferTests.swift

请注意R062R050行:Git检测到,由于合并基本提交$basea07af749...),这些文件在两个选定的提交中被重命名。在这里,这实际上并不是很重要,但是它们很长,我将它们分开用于发布目的。

我不完全确定您已检出两个提交中的哪个作为分支,以及您使用git merge命令选择了哪个,但是由于合并过程主要是对称的,因此 1 并不重要。

我们可以使用一个分离的HEAD进行合并,但是我希望拥有一个分支名称,因此我现在创建了一个分支名称:

$ git checkout -b t1 conflict-merge-source
Switched to a new branch 't1'
$ git merge conflict-merge-target
CONFLICT (modify/delete): Sources/SwiftFusion/Core/FixedShapeTensor.swift
deleted in HEAD and modified in conflict-merge-target. Version
conflict-merge-target of Sources/SwiftFusion/Core/FixedShapeTensor.swift
left in tree.
Automatic merge failed; fix conflicts and then commit the result.

(再次,我为了发布目的将一行长长的一行。)


1 任何不对称性都是将一个提交视为“我们的”而将另一个提交视为“其”的结果。例如,-X扩展选项本身必须选择我们的与他们的,而重命名/重命名冲突(如果有)必须选择要采用的重命名(如果有)。这些决胜局引入了不对称性。


git merge如何合并更改

在所有使用git merge的情况下,Git都会将合并基础(我们在上面找到)与要合并的每个提交进行比较。由于一个共同的起点,这两个快照中的每一个都变成了更改。我们可以使用git diff(请参见上面的示例)来找出哪些文件包含哪些更改。

找到这些更改后,合并代码的工作就是组合更改。在合并的任一“侧”进行一些更改与在另一“侧”进行更改的组合,是要批量进行更改。大多数此类文件就是这种情况,例如M中的全部22个文件。因此,在这种情况下,git merge可以从更改了文件的任何一方获取文件的副本。合并的这一部分很容易:有时,合并必须从merge-base提交开始,以文件的基本版本开始,并向其添加两面的更改。

但是我们列出了其他八种状态文件(不是M),一侧是7,另一侧是1:

  • 从基础到一侧有四个A文件(新文件)。它们彼此之间没有同名操作,因此合并操作只是将这些新文件作为新文件。

  • 有两个R文件。它们位于另一侧没有接触的不同文件上,因此Git保留了重命名的副本:我们仅获得新名称(如果内容也被更改,则具有新内​​容);合并基本提交中原始文件的原始副本被完全删除。 (如果合并比较困难,则可能不是这种情况。)

  • 有两个D文件,仅在一侧。这些是:

     D       Sources/SwiftFusion/Core/FixedShapeTensor.swift
     D       Tests/SwiftFusionTests/Core/FixedShapeTensorTests.swift
    

    (您提到的两个)。这是我们在 other 端针对这两个文件看到的内容:

     M       Sources/SwiftFusion/Core/FixedShapeTensor.swift
    

    也就是说,第二个文件根本没有出现:没有被触摸。

Git如何将“已删除”与“未修改”结合在一起? Git的答案是“保留删除”。 Gem应该如何将“已删除”与“已修改”结合在一起? Git的答案是将修改后的文件保留在工作树中,并将该文件标记为在索引中冲突。这并不总是正确的答案,但是如果不是,您可以手动处理索引内容,例如从HEADMERGE_HEAD恢复已删除的文件。

完成合并后,最终合并提交将包含Git复制到Git索引中的所有文件。文件的工作树副本对Git并不重要。他们就在那里,以便您可以与他们合作。每个文件的索引副本都采用Git自己的内部压缩和重复数据删除格式。