Git 强制合并 dev 分支到 master 分支(覆盖所有本地文件)
Git force merge dev branch to master branch (overwrite all local files)
我在 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
我尝试对主分支中的所有文件执行 rm
➜ project git:(master) git reset --hard HEAD
➜ project git:(master) rm -rf *
➜ project git:(master) git merge -s recursive -X theirs development
仍然遇到同样的冲突。我做错了什么?
编辑:
这是一张图表,可以更好地理解正在发生的事情。有的时候还推送了一些不相关的历史。
分支只是一个指针,master
只是另一个分支。如果您没有在进行过程中合并更改,并且 dev
代表项目的当前稳定状态,那么一个可以避免您经历所有冲突的选项是:
git branch -m master <new-name>
git branch -m dev master
可能需要调整跟踪关系,具体取决于您如何设置任何遥控器。
这只会将 master
设置为与您的 development
分支相同 - 在 development
的最后一次合并之后提交的 master 中的任何内容都将丢失。
git checkout -B master development
我认为您根本不想要这种合并。我认为您想要的是 "theirs strategy" 合并,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
导致 G
到 F
到 B
。提交 B
在 两个 分支上,并且在图形术语中,它是 最低共同祖先 .1 (非正式地,它是最接近两个分支提示的那个。"Closest" 在复杂的图形中分解,但对于像这样的简单图形,这就是最低共同祖先的意思。)这使它成为最好的共享提交,因此合并基础。
现在发生的事情在一般情况下非常简单,但在某些细节上变得棘手。 Git 从 合并基础 提交开始 "desired result"。也就是说,Git开头的内容是commitB
中快照的内容。对于这些内容,Git 需要应用组合 你所做的任何更改以及他们所做的任何更改 取得。所以 Git 需要 运行 两个 git diff
命令:
git diff --find-renames <em>hash-of-B</em> <em>hash-of-E</em>
:我们在 master
上所做的更改
git diff --find-renames <em>hash-of-B</em> <em>hash-of-H</em>
:他们在 development
上改变了什么
如果合并基础接近两个提示,就像这里一样,可能这两个差异中的每一个都没有显示那么多。将两组更改结合起来将简单明了。如果我们更改了 README.md
的第 12 行,而他们根本没有触及 README.md
,Git 会接受我们的更改,让我们的 README.md
版本进入新的合并犯罪。如果他们触及 README.md
的不同行,Git 会将我们的两个更改放在一起:两者都应用于 B:README.md
以生成新提交的 README.md
。对所有文件重复此过程。
如果我们和他们都触及相同文件的 相同 行,但是,Git 通常会声明合并冲突并停止,留下我们收拾残局。这就是 -X theirs
选项的用武之地:这是一个 扩展选项 ,传递给 -s
策略。2在这种情况下,recursive
策略将 theirs
扩展选项视为含义:在发生冲突的情况下,丢弃我的零钱并使用他们的。
请注意,如果没有冲突,Git将使用我们的更改!有时这就是我们想要的。如果是我们想要的,-X theirs
是正确的想法。如果不是我们想要的,-X theirs
就是错误的想法。所以这就是您需要决定的地方:我们是使用 -X theirs
,还是使用必须像 VonC 的答案那样构建的 -s theirs
?
1它是最低的共同祖先,因为计算机科学家将他们的树倒过来绘制:
A
|
B
/ \
C D
/ \ \
E F G
此处E和F的共同祖先是C、B、A,但C是最低的。 F和G的共同祖先是B和A; B最低。 Git的commit graph是有向无环图或DAG,而不是树,所以"LCA"不像树那么简单,可以有多个LCA(或者没有LCA在all) 给定图中的两个节点。 Git 明智地处理所有这些,对于明智的定义。
2Git 调用 -X
一个 策略选项 ,但这听起来与 -s <em>策略</em>
参数。它实际上是该策略的一个选项,因此 Git 的名称选择不当。我认为 eXtended option 是一个更好的名字,部分原因是它解释了 -X
.
如果您不想-X theirs
:请参阅我链接的其他答案
没什么好说的:-s theirs
曾经是 Git 合并策略。它不再是了。您仍然可以通过多种方式合成它。我最喜欢的实际上是 Michal Soltys' answer.
中的管道命令变体
如果你确实想要-X theirs
:为什么会抱怨冲突?
我在上面提到合并过程——git merge
的合并 部分——是"pretty simple ... but gets sticky in some details."你刚刚点击了其中一个细节。
Git 通过区分它找到的合并基础获得的大量 git diff
s,与您指定的两个提示提交中的每一个相比,有很多文件是 一侧完全删除,但另一侧修改:
...--B--o--o--...--o--o <-- master (HEAD)
\
o--o--...--o--o <-- development
在那个巨大的匿名链中的某个地方 o
沿着顶部提交,"we"(基本 vs 主)删除 一些文件。 "They"(基础与开发)更改 该文件。 Git 不知道如何结合 "deleted" 和 "changed"。
我称这些 高级别 冲突,因为它们是对文件的本质或存在的更改,而不是对文件中各个行的更改。好吧,我也这么称呼,因为Git本身就调用了"low level merge driver"结合个别行的代码部分,所以这些肯定是相反的,high水平.这些类型的冲突——无论是 add/add、modify/delete、rename/delete 还是什么——总是 导致 -s recursive
git merge
停下来让你收拾残局。扩展选项仅提供给策略调用的 low 级别合并 driver。
事实上,不会停止的唯一策略是-s ours
策略。当您使用 -s ours
策略时,那个 完全忽略 "their" 提交中的内容。 Git 甚至根本不关心合并基础——它只是使用 我们的 提交中的内容作为合并结果。
当然,-s ours
需要我们的。你想要的东西需要 他们的,至少对于这个特殊情况,也许对于所有情况。但是如果你真的不想想要-s theirs
的等价物,而是-X theirs
在这些情况下获取他们的整个文件,你就无法解决每个问题这些冲突是事后发生的。
您可以使用脚本(您必须自己编写)来完成。虽然这有点棘手。诀窍是使用 git ls-files --stage
:你会看到,对于 Git 抱怨的每个文件:
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.
Git 将在阶段 #1 中有一个条目用于名称 (vendors/popup/magnific-popup.css
),在阶段 #3 中有另一个条目(他们的),但在阶段 #2 中没有条目(我们的) .要解决此问题,请使用 git add
将工作树中的第 3 阶段条目写入第 0 阶段的索引:git add
将删除第 1 阶段和第 3 阶段的副本。您可以收集所有此类文件名并将它们全部传递给 git add
.
我在 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
我尝试对主分支中的所有文件执行 rm
➜ project git:(master) git reset --hard HEAD
➜ project git:(master) rm -rf *
➜ project git:(master) git merge -s recursive -X theirs development
仍然遇到同样的冲突。我做错了什么?
编辑:
这是一张图表,可以更好地理解正在发生的事情。有的时候还推送了一些不相关的历史。
分支只是一个指针,master
只是另一个分支。如果您没有在进行过程中合并更改,并且 dev
代表项目的当前稳定状态,那么一个可以避免您经历所有冲突的选项是:
git branch -m master <new-name>
git branch -m dev master
可能需要调整跟踪关系,具体取决于您如何设置任何遥控器。
这只会将 master
设置为与您的 development
分支相同 - 在 development
的最后一次合并之后提交的 master 中的任何内容都将丢失。
git checkout -B master development
我认为您根本不想要这种合并。我认为您想要的是 "theirs strategy" 合并,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
导致 G
到 F
到 B
。提交 B
在 两个 分支上,并且在图形术语中,它是 最低共同祖先 .1 (非正式地,它是最接近两个分支提示的那个。"Closest" 在复杂的图形中分解,但对于像这样的简单图形,这就是最低共同祖先的意思。)这使它成为最好的共享提交,因此合并基础。
现在发生的事情在一般情况下非常简单,但在某些细节上变得棘手。 Git 从 合并基础 提交开始 "desired result"。也就是说,Git开头的内容是commitB
中快照的内容。对于这些内容,Git 需要应用组合 你所做的任何更改以及他们所做的任何更改 取得。所以 Git 需要 运行 两个 git diff
命令:
git diff --find-renames <em>hash-of-B</em> <em>hash-of-E</em>
:我们在master
上所做的更改
git diff --find-renames <em>hash-of-B</em> <em>hash-of-H</em>
:他们在development
上改变了什么
如果合并基础接近两个提示,就像这里一样,可能这两个差异中的每一个都没有显示那么多。将两组更改结合起来将简单明了。如果我们更改了 README.md
的第 12 行,而他们根本没有触及 README.md
,Git 会接受我们的更改,让我们的 README.md
版本进入新的合并犯罪。如果他们触及 README.md
的不同行,Git 会将我们的两个更改放在一起:两者都应用于 B:README.md
以生成新提交的 README.md
。对所有文件重复此过程。
如果我们和他们都触及相同文件的 相同 行,但是,Git 通常会声明合并冲突并停止,留下我们收拾残局。这就是 -X theirs
选项的用武之地:这是一个 扩展选项 ,传递给 -s
策略。2在这种情况下,recursive
策略将 theirs
扩展选项视为含义:在发生冲突的情况下,丢弃我的零钱并使用他们的。
请注意,如果没有冲突,Git将使用我们的更改!有时这就是我们想要的。如果是我们想要的,-X theirs
是正确的想法。如果不是我们想要的,-X theirs
就是错误的想法。所以这就是您需要决定的地方:我们是使用 -X theirs
,还是使用必须像 VonC 的答案那样构建的 -s theirs
?
1它是最低的共同祖先,因为计算机科学家将他们的树倒过来绘制:
A
|
B
/ \
C D
/ \ \
E F G
此处E和F的共同祖先是C、B、A,但C是最低的。 F和G的共同祖先是B和A; B最低。 Git的commit graph是有向无环图或DAG,而不是树,所以"LCA"不像树那么简单,可以有多个LCA(或者没有LCA在all) 给定图中的两个节点。 Git 明智地处理所有这些,对于明智的定义。
2Git 调用 -X
一个 策略选项 ,但这听起来与 -s <em>策略</em>
参数。它实际上是该策略的一个选项,因此 Git 的名称选择不当。我认为 eXtended option 是一个更好的名字,部分原因是它解释了 -X
.
如果您不想-X theirs
:请参阅我链接的其他答案
没什么好说的:-s theirs
曾经是 Git 合并策略。它不再是了。您仍然可以通过多种方式合成它。我最喜欢的实际上是 Michal Soltys' answer.
如果你确实想要-X theirs
:为什么会抱怨冲突?
我在上面提到合并过程——git merge
的合并 部分——是"pretty simple ... but gets sticky in some details."你刚刚点击了其中一个细节。
Git 通过区分它找到的合并基础获得的大量 git diff
s,与您指定的两个提示提交中的每一个相比,有很多文件是 一侧完全删除,但另一侧修改:
...--B--o--o--...--o--o <-- master (HEAD)
\
o--o--...--o--o <-- development
在那个巨大的匿名链中的某个地方 o
沿着顶部提交,"we"(基本 vs 主)删除 一些文件。 "They"(基础与开发)更改 该文件。 Git 不知道如何结合 "deleted" 和 "changed"。
我称这些 高级别 冲突,因为它们是对文件的本质或存在的更改,而不是对文件中各个行的更改。好吧,我也这么称呼,因为Git本身就调用了"low level merge driver"结合个别行的代码部分,所以这些肯定是相反的,high水平.这些类型的冲突——无论是 add/add、modify/delete、rename/delete 还是什么——总是 导致 -s recursive
git merge
停下来让你收拾残局。扩展选项仅提供给策略调用的 low 级别合并 driver。
事实上,不会停止的唯一策略是-s ours
策略。当您使用 -s ours
策略时,那个 完全忽略 "their" 提交中的内容。 Git 甚至根本不关心合并基础——它只是使用 我们的 提交中的内容作为合并结果。
当然,-s ours
需要我们的。你想要的东西需要 他们的,至少对于这个特殊情况,也许对于所有情况。但是如果你真的不想想要-s theirs
的等价物,而是-X theirs
在这些情况下获取他们的整个文件,你就无法解决每个问题这些冲突是事后发生的。
您可以使用脚本(您必须自己编写)来完成。虽然这有点棘手。诀窍是使用 git ls-files --stage
:你会看到,对于 Git 抱怨的每个文件:
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.
Git 将在阶段 #1 中有一个条目用于名称 (vendors/popup/magnific-popup.css
),在阶段 #3 中有另一个条目(他们的),但在阶段 #2 中没有条目(我们的) .要解决此问题,请使用 git add
将工作树中的第 3 阶段条目写入第 0 阶段的索引:git add
将删除第 1 阶段和第 3 阶段的副本。您可以收集所有此类文件名并将它们全部传递给 git add
.