由于Git“建议[文件]可能应该移动”,导致Git文件级合并冲突

6
我有一个Markdown文章的Git仓库,人们会创建拉取请求(每个Markdown文章一个),最终合并到“主分支”,然后直接在“主分支”上工作,将该文章移动到名为“wordpressed”的文件夹中,然后提交并推送到“主分支”。
我有一个非常老的拉取请求,仍然停留在名为“home-base”的分支中,由一个名为“dan/homebase.md”的文件组成,我正在考虑通过一些提交将其编辑到适当的形状,然后合并到“主分支”。
这个“home-base”分支是如此之旧,从它的角度来看,仓库与今天在“主分支”中的样子完全不同。因此,我想倒置合并“主分支”到“home-base”中,只是为了使它更加更新,并将合并基础向上移动很多。
但是当我尝试这样做时,我遇到了一个我无法理解的合并冲突。
为了展示情况,我将展示每个分支中文件和文件夹的“ls”命令输出。我将从“主分支”开始:
$ git status
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean

$ ls -1 -R
README.md
dan
instructions.md
wordpressed

./dan:

./wordpressed:
AppDelegateRefactor.md
BareListContentView.md
CellContentConfiguration.md
ControlTargetActioniOS14.md
GetStartedWithPods.md
MultipleTrailingClosures.md
StatesAndBackgrounds.md
Swift52notes1.md
Swift52notes2.md
Swift52notes3.md
UserInteractiveCells.md
bdd-common-pitfalls.md
bdd.md
brantsMenuBar.md
collectionViewContentConfig.md
collectionViewLists.md
collectionViewLists2.md
collectionViewOutline.md
debuggingLinks.md
diffableDataSources.md
diffableDataSources2.md
forwardCompatibity.md
haptics.md
iOS13deprecations.md
images
inAppPurchases.md
logging.md
miscSwiftTricks1.md
miscSwiftTricks2.md
miscSwiftTricks3.md
miscSwiftTricks4.md
multipleSelection.md
mysteriesOfLayout.md
newInIOS14.md
packages.md
photoPicker.md
protocolParadox.md
splitViewControllers.md
splitViewControllers2.md
swiftTrickTwoClasses.md
targetActionRant.md
typescript-shape-of-things.md
untappableButton.md
what-vs-how.md
xcode12Editing.md
xcode12testing.md
xcodeWhereAmI.md
xcodeWorkInTwoPlaces.md

./wordpressed/images:
argumentsInScheme.png
bigFlags.png
callHierarchy.png
callersMenu.png
chooser.png
config.png
createdTester.png
dataOrUrl.png
ipadportraitoverlay.png
listOfPepBoys.png
listWithSectionHeaders.png
littleFlags.png
looksLikeADuck.png
newTester.png
outlineIndicatorsWrong.png
outlineIndicatorsWrong2.png
perfectAfterHack.png
pickAPeppa.png
rootOnly.png
sandboxAccountOnDevice.png
selfSizingStringDrawer.png
simpleList.png
split1.png
split2.png
splitViewControllerPadLandscape.png
tableViewAsChoice.png
threecolumns.png
twoPossibilities.png
twoscreensphone.png
variableRowHeights.png
workingOutline.png

正如你所看到的,主文件夹本身大部分为空。所有文章都在 wordpressed 中,包括它们的图片在 wordpressed/images 中。


现在,这里是 home-base

$ git switch home-base
Switched to branch 'home-base'
Your branch is up to date with 'origin/home-base'.

$ ls -1 -R
GetStartedWithPods.md
README.md
Swift52notes1.md
Swift52notes2.md
Swift52notes3.md
dan
debuggingLinks.md
images
logging.md
miscSwiftTricks1.md
miscSwiftTricks2.md
miscSwiftTricks3.md
miscSwiftTricks4.md
packages.md
wordpressed
xcode12Editing.md
xcode12testing.md
xcodeWhereAmI.md
xcodeWorkInTwoPlaces.md

./dan:
bdd.md
homebase.md

./images:
chooser.png
config.png
split1.png
split2.png

./wordpressed:
images

./wordpressed/images:

正如您所看到的,这是一个更早的情况。还没有将任何内容移动到wordpressed。有一些文章漂浮在顶层,而dan文件夹中有两篇文章,包括我在这里感兴趣的homebase.md


最后,为了完整起见,我将向您展示masterhome-base之间的合并基础:

$ git merge-base master home-base
b5d7355fe42eddad96beb200df2cba65381c288a
$ git checkout b5d7355fe
$ ls -1 -R
GetStartedWithPods.md
README.md
Swift52notes1.md
Swift52notes2.md
Swift52notes3.md
dan
debuggingLinks.md
images
logging.md
miscSwiftTricks1.md
miscSwiftTricks2.md
miscSwiftTricks3.md
miscSwiftTricks4.md
packages.md
wordpressed
xcode12Editing.md
xcode12testing.md
xcodeWhereAmI.md
xcodeWorkInTwoPlaces.md

./dan:
bdd.md

./images:
chooser.png
config.png
split1.png
split2.png

./wordpressed:
images

./wordpressed/images:

好吧,它看起来非常像现在的home-base,不是吗? 主要区别在于那是在 dan 文件夹中出现 homebase.md 之前。


现在,请问当我尝试将 master 合并到 home-base 时会发生什么。合并的双方都有哪些贡献? 在我的观点中,Git 应该意识到在 master 中出现了很多新文件在wordpressed,其中一些文件是原来位于顶层的文件的重命名。 当然,在 home-base 中,dan 中出现了一个新文件homebase.md

在我的看法中,这些贡献不应该冲突。

好的,让我们试试吧。

$ git switch home-base
$ git merge master
CONFLICT (file location): dan/homebase.md added in HEAD inside a directory that was renamed in master, suggesting it should perhaps be moved to wordpressed/homebase.md.
Automatic merge failed; fix conflicts and then commit the result.

$ git status
On branch home-base
Your branch is up to date with 'origin/home-base'.

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Changes to be committed:
    deleted:    dan/homebase.md
    new file:   instructions.md
    new file:   wordpressed/AppDelegateRefactor.md
    new file:   wordpressed/BareListContentView.md
    new file:   wordpressed/CellContentConfiguration.md
    new file:   wordpressed/ControlTargetActioniOS14.md
    renamed:    GetStartedWithPods.md -> wordpressed/GetStartedWithPods.md
    new file:   wordpressed/MultipleTrailingClosures.md
    new file:   wordpressed/StatesAndBackgrounds.md
    renamed:    Swift52notes1.md -> wordpressed/Swift52notes1.md
    renamed:    Swift52notes2.md -> wordpressed/Swift52notes2.md
    renamed:    Swift52notes3.md -> wordpressed/Swift52notes3.md
    new file:   wordpressed/UserInteractiveCells.md
    new file:   wordpressed/bdd-common-pitfalls.md
    renamed:    dan/bdd.md -> wordpressed/bdd.md
    new file:   wordpressed/brantsMenuBar.md
    new file:   wordpressed/collectionViewContentConfig.md
    new file:   wordpressed/collectionViewLists.md
    new file:   wordpressed/collectionViewLists2.md
    new file:   wordpressed/collectionViewOutline.md
    renamed:    debuggingLinks.md -> wordpressed/debuggingLinks.md
    new file:   wordpressed/diffableDataSources.md
    new file:   wordpressed/diffableDataSources2.md
    new file:   wordpressed/forwardCompatibity.md
    new file:   wordpressed/haptics.md
    new file:   wordpressed/iOS13deprecations.md
    new file:   wordpressed/images/argumentsInScheme.png
    new file:   wordpressed/images/bigFlags.png
    new file:   wordpressed/images/callHierarchy.png
    new file:   wordpressed/images/callersMenu.png
    renamed:    images/chooser.png -> wordpressed/images/chooser.png
    renamed:    images/config.png -> wordpressed/images/config.png
    new file:   wordpressed/images/createdTester.png
    new file:   wordpressed/images/dataOrUrl.png
    new file:   wordpressed/images/ipadportraitoverlay.png
    new file:   wordpressed/images/listOfPepBoys.png
    new file:   wordpressed/images/listWithSectionHeaders.png
    new file:   wordpressed/images/littleFlags.png
    new file:   wordpressed/images/looksLikeADuck.png
    new file:   wordpressed/images/newTester.png
    new file:   wordpressed/images/outlineIndicatorsWrong.png
    new file:   wordpressed/images/outlineIndicatorsWrong2.png
    new file:   wordpressed/images/perfectAfterHack.png
    new file:   wordpressed/images/pickAPeppa.png
    new file:   wordpressed/images/rootOnly.png
    new file:   wordpressed/images/sandboxAccountOnDevice.png
    new file:   wordpressed/images/selfSizingStringDrawer.png
    new file:   wordpressed/images/simpleList.png
    renamed:    images/split1.png -> wordpressed/images/split1.png
    renamed:    images/split2.png -> wordpressed/images/split2.png
    new file:   wordpressed/images/splitViewControllerPadLandscape.png
    new file:   wordpressed/images/tableViewAsChoice.png
    new file:   wordpressed/images/threecolumns.png
    new file:   wordpressed/images/twoPossibilities.png
    new file:   wordpressed/images/twoscreensphone.png
    new file:   wordpressed/images/variableRowHeights.png
    new file:   wordpressed/images/workingOutline.png
    new file:   wordpressed/inAppPurchases.md
    renamed:    logging.md -> wordpressed/logging.md
    renamed:    miscSwiftTricks1.md -> wordpressed/miscSwiftTricks1.md
    renamed:    miscSwiftTricks2.md -> wordpressed/miscSwiftTricks2.md
    renamed:    miscSwiftTricks3.md -> wordpressed/miscSwiftTricks3.md
    renamed:    miscSwiftTricks4.md -> wordpressed/miscSwiftTricks4.md
    new file:   wordpressed/multipleSelection.md
    new file:   wordpressed/mysteriesOfLayout.md
    new file:   wordpressed/newInIOS14.md
    renamed:    packages.md -> wordpressed/packages.md
    new file:   wordpressed/photoPicker.md
    new file:   wordpressed/protocolParadox.md
    new file:   wordpressed/splitViewControllers.md
    new file:   wordpressed/splitViewControllers2.md
    new file:   wordpressed/swiftTrickTwoClasses.md
    new file:   wordpressed/targetActionRant.md
    new file:   wordpressed/typescript-shape-of-things.md
    new file:   wordpressed/untappableButton.md
    new file:   wordpressed/what-vs-how.md
    renamed:    xcode12Editing.md -> wordpressed/xcode12Editing.md
    renamed:    xcode12testing.md -> wordpressed/xcode12testing.md
    renamed:    xcodeWhereAmI.md -> wordpressed/xcodeWhereAmI.md
    renamed:    xcodeWorkInTwoPlaces.md -> wordpressed/xcodeWorkInTwoPlaces.md

Unmerged paths:
  (use "git add <file>..." to mark resolution)
    added by us:     wordpressed/homebase.md 

好的,那么我不理解最后一行是什么意思。什么叫做“added by us” 是 wordpressed/homebase.md?不!那不是我们添加的位置。我们是在 dan 中添加的。
无论是 master 还是 home-base 都没有一个名为 wordpressed/homebase.md 的文件。那么 Git 从哪里得到这个想法呢?它似乎与它认为有合并冲突的原因有关:

dan/homebase.md 在 HEAD 中添加在一个在主分支中被重命名的目录中,建议将其移动到 wordpressed/homebase.md。

那么:Git 在这里说了什么?为什么 Git 认为 dan/homebase.md 应该是 wordpressed/homebase.md,却找不到任何证据呢?我如何防止这种合并冲突,让 Git 更加简单化处理呢?我可以使用不同的合并策略吗?还是我该如何以这样的方式简单地解决冲突,告诉 Git:“不要管 dan/homebase.md,请离开它”?
再次对问题的详细描述表示歉意。

冲突就是冲突,你仍然可以从不同类型的合并或甚至重置中获得相同的结果。最佳实践是不要让你的各个分支失去同步,以避免出现这个问题。 - Tim Biegeleisen
2
@TimBiegeleisen 有趣的想法,但这并不是我让事情一直这样下去的错。_homebase.md_文章的作者似乎已经放弃了它。现在我想要恢复它。此外,“失步”正是分支的作用所在。这就是为什么分支存在的原因——它们可以分叉。 - matt
分支不存在脱离同步的情况。人们应该经常将主分支合并到本地分支中。每周,如果不是每天都要这样做。这就是@TimBiegeleisen所说的。 - Greg Burghardt
2
@GregBurghardt 我认为恰恰相反。分支应该在它们准备好之后而不是之前合并。在随机状态下合并分支只会引入另一个不稳定的因素。如果你一定要将主分支合并回功能分支,那么你应该在主分支处于已知稳定状态时这样做。至少这是 Git 发明者中的共识智慧。 - j6t
1个回答

11

这场特定的冲突:

CONFLICT (file location): dan/homebase.md added in HEAD inside a
directory that was renamed in master, suggesting it should perhaps
be moved to wordpressed/homebase.md.
Git中比较新的发明是目录重命名。现在,Git尝试在合并基础提交和每个端点提交之间查找目录重命名。也就是说,在运行了git diff --find-renames 和git diff --find-renames 之后,它检测到左侧有一些重命名,右侧有一些重命名(总数为正)。其中至少有一些重命名意味着目录名称更改。所以现在Git认为也许合并的另一侧应该应用相同的目录名称更改。
(Git 2.x版本的预发布版中增加了各种实验性版本的此代码,我忘记了它什么时候真正稳定下来。浏览发布说明,看起来像是从2.17开始使用,然后在2.19中修复了一个难题。)
[编辑,2021年4月15日:] 当这个新功能被添加时,还添加了一个新的控制开关:merge.directoryRenames。默认值为“conflict”,但您可以将其设置为“true”(表示始终做出它已经做出的假设,但不称之为冲突)或“false”(表示永远不要做出新的假设,因此不要将其视为冲突)。
这是Git正在宣布的唯一冲突,因此它是最终“未合并文件”部分中出现的唯一文件。
Unmerged paths:
  (use "git add <file>..." to mark resolution)
    added by us:     wordpressed/homebase.md

Git采用了自己的内部建议:它认为在合并基础和HEAD之间添加到dan/的所有内容应该添加到wordpressed/,因为它看到了在合并基础和master末端之间的更改。

如果这个想法对您来说是正确的, Git希望您通过在该路径名上运行git add来确认。如果不是,请将文件移动到您认为应该去的位置,并且git add结果(使用git rm或者可能是git add),移除Git使用的名称。


我不太清楚Git从何处获取目录重命名的想法。运行git diff --find-renames从合并基础到主分支会很有趣,看看是否有任何可识别的东西。至于后来的合并,现在应该有一个不同的合并基础,希望能减少这种情况的发生。 - torek
好的,绝大部分内容都是_WordPressed_中全新的内容或从顶层移动(重命名)过来的内容。只有一个现有的重命名操作,其中一篇文章从_dan_移动到了_wordpressed_:diff --git a/dan/bdd.md b/wordpressed/bdd.md。然而基于这一点,Git就认为在_dan_中出现的这篇新文章本应该出现在_wordpressed_中??很抱歉,我认为这是一个Git的错误。至少需要有一种方法来关闭这个“功能”,不是吗? - matt
从“一个”推广到“所有”,这似乎有点过于激进了,至少可以这么说。 :-) - torek
很遗憾,目前没有简单的方法向Git团队报告错误。 - matt
我在邮件列表上看到了这个。我错过了这些新的merge.directoryRenames设置(它们很容易被忽略!)。我会编辑这个答案来提到它们。 - torek
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接