Git强制将dev分支合并到master分支(覆盖所有本地文件)

时间:2019-03-24 23:26:17

标签: git git-merge merge-conflict-resolution

我在master分支中有一个旧的,不同步的项目代码。几个文件已被删除,添加,重写和彻底编辑。我想强制将稳定的最新代码从开发分支合并到master。我正在尝试强制合并,但仍然遇到合并冲突。

这是我遵循的步骤:

➜  project git:(development) git checkout master
➜  project git:(master) git pull
➜  project git:(master) git merge -s recursive -X theirs development

CONFLICT (modify/delete): vendors/popup/magnific-popup.css deleted in HEAD and modified in development. Version development of vendors/popup/magnific-popup.css left in tree.
CONFLICT (modify/delete): vendors/popup/jquery.magnific-popup.min.js deleted in HEAD and modified in development. Version development of vendors/popup/jquery.magnific-popup.min.js left in tree.
CONFLICT (modify/delete): vendors/owl-carousel/assets/animated.css deleted in HEAD and modified in development. Version development of vendors/owl-carousel/assets/animated.css left in tree.

... several similar conflicts

我尝试对我的master分支中的所有文件进行rm

➜  project git:(master) git reset --hard HEAD
➜  project git:(master) rm -rf *
➜  project git:(master) git merge -s recursive -X theirs development

仍然会遇到相同的冲突。我在做什么错了?

编辑:

这里是一个图表,用于更好地了解正在发生的事情。在某些时候,不相关的历史也被推动。

enter image description here

3 个答案:

答案 0 :(得分:2)

我认为您根本不需要这种合并。我认为您想要的是“他们的策略”合并,Git不提供。仍然可以完成:请参见VonC's answer here。但请继续阅读以确保该 是您想要的,然后再使用它。


这里发生了两件重要的事情,都是由您的命令序列捕获的:

git checkout master
git merge -s recursive -X theirs development

(我没有采取任何措施)。

首先是,大多数合并(包括来自默认-s recursive合并策略的合并)都是通过找到一个共同的出发点进行工作的:共享的合并基础提交。要将其绘制为一个相当简单的情况,请考虑以下提交图:

          C--D--E   <-- master (HEAD)
         /
...--o--B
         \
          F--G--H   <-- development

很显然,您自己的图要毛茸茸得多,但最终,它们的工作原理是一样的:Git接受当前提交,如您当前的分支master所指示,是提交E,作为合并过程的三个输入之一。 Git接受您指定的提交,在这种情况下,提交H,因为development是您说要查看的提交,是合并过程的三个输入中的第二个。

第三输入是根据图形计算得出的。我们从两次提交中的每一个开始,然后像Git一样向后走。提交E返回到D,后者返回到C,然后到达B。同时,提交H导致GFB。提交B在两个分支上,并且以图形的形式表示,它们是最小祖先。 1 (非正式地,它是最接近这两个分支的技巧。“最接近”在复杂图中分解,但是对于像这样的简单图,这就是最低祖先的意思。)这使其成为最佳的共享提交,因此也是合并基础。

现在发生的事情从总体上来说很简单,但是在某些细节上变得棘手。 Git从“所需结果”方面的合并基础提交开始。也就是说,Git开头的内容是提交B中快照中的内容。对于这些内容,Git需要将所做的任何更改和他们所做的任何更改的组合应用。因此,Git需要运行两个git diff命令:

  • git diff --find-renames hash-of-B hash-of-E:我们在master上所做的更改
  • git diff --find-renames hash-of-B hash-of-H:它们在development上的变化

如果合并基础与此处的两个技巧都很接近,那么这两个差异中的每个差异可能并没有显示太多。将这两组更改组合在一起将变得简单而直接。如果我们更改了README.md的第12行,而他们根本没有碰到README.md,则Git会进行更改,并给我们的README.md版本进行新的合并提交。如果他们碰到README.md的另一行,Git会将我们的两个更改放在一起:都应用于B:README.md以为新提交生成README.md。对所有文件重复此过程。

但是,如果我们和他们俩都碰到相同文件的相同行,则Git通常会声明合并冲突并停止,从而使我们清理混乱。这就是-X theirs选项出现的位置:这是一个扩展选项,传递给-s策略。 2 在这种情况下,{ {1}}策略将recursive扩展选项视为:在发生冲突的情况下,放弃我的更改并使用他们的更改

请注意,如果没有冲突,Git将使用我们的更改!有时候这就是我们想要的。如果 是我们想要的,那么theirs是正确的主意。如果不是我们想要的,-X theirs是错误的主意。所以这就是您需要决定的地方:是使用-X theirs,还是使用我们必须像VonC的答案那样构造的-X theirs


1 它是最低的共同祖先,因为计算机科学家颠倒了他们的树木:

-s theirs

E和F的共同祖先是C,B和A,但C是最低。 F和G的共同祖先是B和A。 B最低。 Git的提交图是有向无环图或DAG,而不是树,因此“ LCA”并不像在树中那样简单,并且图中的两个节点可以有多个LCA(或根本没有LCA) 。对于某些定义,Git明智地处理了所有这些事情。

2 Git将 A | B / \ C D / \ \ E F G 称为策略选项,但这听起来与-X参数完全一样。从字面上看,这是该策略的一种选择,因此Git的选择不佳。我认为扩展选项是一个更好的名称,部分原因是它解释了-s strategy


如果您不想要-X:请参阅我链接的其他答案

无需多说:-X theirs曾经是Git合并策略。不再了。您仍然可以通过多种方式来合成它。我最喜欢的实际上是Michal Soltys' answer中的管道命令。

如果您确实想-s theirs:为什么抱怨冲突?

我在上面提到,合并过程(-X theirs要合并部分)“非常简单……但在某些细节上有些棘手”。您刚刚击中了其中一​​个细节。

Git通过扩散找到的合并基础而获得的大量git merge与您指定的两个提示提交中的每一个相比,有很多情况下文件已被完全删除一侧,但另一侧被修改

git diff

在庞大的匿名...--B--o--o--...--o--o <-- master (HEAD) \ o--o--...--o--o <-- development 链中的某个位置,顶部是“我们”(基本对主),已删除了一些文件。 “他们”(基础与开发)更改了该文件。 Git不知道如何将“已删除”与“已更改”组合在一起。

我将这些冲突称为高级,因为它们是对文件本质或存在的更改,而不是对文件中各个行的更改。好吧,我也这样称呼它,因为Git本身将组合各行的代码部分称为“低级合并驱动程序”,因此它们必须是相反的 high 级别。这类冲突-无论是添加/添加,修改/删除,重命名/删除还是其他冲突-总是会导致o -s recursive停止并让您修复混乱。扩展选项仅提供给策略调用的级别合并驱动程序

事实上,唯一不会停止的策略就是git merge策略。当您使用-s ours策略时,该完全忽略“其”提交中的内容。 Git甚至根本不用理会合并基础,它只是将 our 提交中的内容用作合并结果。

当然,-s ours需要我们的。您想要的东西,至少在这种情况下,甚至在所有情况下,都花了他们的钱。但是,如果您真的想要与-s ours等效,而在这些情况下宁愿-s theirs提取整个文件,则必须解决每个冲突事后。

可以使用脚本(必须自己编写)来执行此操作。不过这有点棘手。诀窍是使用-X theirs:对于Git抱怨的每个文件,您都会看到:

git ls-files --stage

Git将在第1阶段输入一个名称(CONFLICT (modify/delete): vendors/popup/magnific-popup.css deleted in HEAD and modified in development. Version development of vendors/popup/magnific-popup.css left in tree. ),在第3阶段输入另一个名称(它们),但是在第2阶段没有输入(我们的)。要解决此问题,请使用vendors/popup/magnific-popup.css将工作树的第3阶段条目写入零阶段的索引:git add将删除第1阶段和第3阶段的副本。您只需收集所有这些文件名并将它们全部传递给git add

答案 1 :(得分:0)

一个分支只是一个指针,而requestPermission() { this.angularFireMessaging.requestToken.subscribe( <-- THIS LINE (token) => { console.log(token); this.updateToken(token); }, (err) => { console.error(err); } ); } 只是另一个分支。如果您在进行过程中没有合并更改,而master表示项目的当前稳定状态,那么可以避免经历所有冲突的一种选择是:

dev

根据您的远程设置方式,可能需要调整跟踪关系。

答案 2 :(得分:0)

这只会将master设置为与您的development分支相同-在development的最后一次合并之后提交的master中的所有内容都会丢失。

git checkout -B master development