根据您的问题,答案是简单的序列:
git checkout branchA
git merge -X theirs branchB
在你盲目应用之前,请确保你知道自己在做什么!你还说你使用了
-s
和
-x
,但是
git merge
没有
-x
选项。(也许你想说的是
-X
。展示你实际使用的命令和至少一些结果可能会有所帮助。)如果你遇到我所谓的高级冲突,
-X
选项无法帮助你,你需要进行一些手动清理,但你可以自动化部分或全部操作。
合并要点
记住,
git merge
所做的是
合并更改。为此,它需要找到一个共同的起点。假设你是Alice或Adam,正在branchA上工作,或者是Bob或Barbara,在branchB上工作。用户“A”从某个提交
*
开始,并进行了一些提交
o
,最终以提交
A
结束:
o--o--A <-- branchA
/
...--o--*
与此同时,用户"B"从同样标记有*
的精确相同的提交开始,并进行了一些提交:
...--o--*
\
o--o--B <-- branchB
现在,您的存储库中运行 git fetch
之后拥有的是完整的提交集:
o--o--A <-- branchA
/
...--o--*
\
o--o--B <-- branchB
当你运行
git checkout branchA; git merge branchB
时,Git会为您
查找提交记录
*
-- 这是您所共同起始的提交记录,无论它在历史中有多远。然后,Git会运行两个
diff
命令:
git diff --find-renames <hash-of-*> <hash-of-A>
git diff --find-renames <hash-of-*> <hash-of-B>
如果可能,Git将尝试将这两组更改合并,并将它们应用于提交*
中的任何内容。如果Git能够自行完成所有这些操作,则会生成一个最终的合并提交——几乎是普通提交,但它有两个父指针,分别指向提交A
和提交B
。这将更改名称branchA
,使其也指向新提交,即:
o--o--A
/ \
...--o--* M <-- branchA (HEAD)
\ /
o--o--B <-- branchB
与新提交的
M
相关联的快照是Git将您的更改(
*
vs
A
)与他们的更改(
*
vs
B
)应用于
*
的结果。在此过程中,任何中间提交都将被完全忽略。Git只对应用于
*
的两个更改集的并集感兴趣。
当
*
-vs-
A
中的一些文件的更改与
*
-vs-
B
中的一些文件的更改冲突时,Git通常会停止合并并显示冲突。大写的
-X
选项允许您告诉Git,在发生冲突的情况下,它应该优先考虑"我们"(
*
-vs-
A
)或"他们"(
*
-vs-
B
)的更改。但是,如果没有冲突,则Git将继续采用两个更改。这适用于单个文件内。以下是一个示例。
假设
*
-vs-
A
更改集包括对文件
README.txt
的以下指令:
- 将第3行的“yellow”更改为“brown”。(实际上它看到整行都已更改,但在这里我们使用“单词”)
- 将第20行的“the yellow cow”更改为“a brown cow”。
假设
*
-vs-
B
更改集包括对文件
README.txt
的以下指令:
- 将第20行的“the yellow cow”更改为“a large yellow cow”。
由于两个更改集都试图更改第20行,所以此处会出现冲突。因为只有一个更改集更改了第3行,所以该更改不会冲突,因此Git会应用它。使用
-X theirs
,Git将优先考虑第20行上的他们的更改,这将导致该行现在具有“the large yellow cow”的单词,即使它已将第3行更改为“brown”。
给定
-X theirs
,
Git认为以下是
正确的解决方案:将您未发生冲突的第3行更改与他们发生冲突的第20行更改合并。这很可能是错误的,但这就是Git将要做的。有时候最好让冲突发生,然后自己去检查正确的解决方案;但如果您有良好的测试,那么您应该捕获大多数逃离Git的问题,因此
-X theirs
(或
-X ours
)是一个有用的工具。
高级别冲突
如果您已经使用了-X theirs
,但仍然存在冲突,则您所拥有的是我所说的高级别冲突。请注意,上面两个git diff
命令,以查找从*
到A
和从*
到B
的变更集,都使用--find-renames
。这意味着在*
和A
之间,Git可能会决定您(或用户A)将README.txt
重命名为README.rst
。用户B
也重命名了它,但名称为read-me.rst
。Git不知道使用哪个名称,-X theirs
也没有告诉它:使用他们的名称。Git只会停止并显示冲突。
类似的高级别冲突发生在以下情况下:您和他们都使用相同的名称添加了一个新文件,或者如果您修改了某个文件并且他们删除了它,反之亦然。前者是add/add冲突,而后者是modify/delete冲突。还有几种这样的冲突。所有这些情况下,Git要么不知道保留哪个文件,要么不知道使用哪个名称。
在所有这些情况下,Git都将停止合并冲突,就像您没有使用-X
一样。当Git以这种方式停止时,它将保留索引/暂存区中的所有文件,并使用阶段编号来标识每个文件。例如,如果在README.txt
中存在冲突,则现在索引中有三份README.txt
的副本:
:1:README.txt
是基础提交(提交*
)中的版本。
:2:README.txt
是HEAD
提交(提交A
)中的版本。
:3:README.txt
是他们的提交(提交B
)中的版本。
对于add/add冲突,版本1不存在(文件不在基础版本中)。 对于modify/delete冲突,版本2或3中的任何一个都不存在(文件已在A
或B
中删除)。
您可以使用这些名称运行git show
:git show: 1:README.txt
,例如查看基础版本。
工作树中有一个README.txt
的副本,这通常是Git尽力将三个输入组合起来的最好方式,可能带有冲突标记。
现在您的任务是为该文件创建一个零级条目:0:README.txt
,清除1-2-3级条目。 最简单的方法通常是编辑工作树文件并运行git add README.txt
。 git add
命令会将工作树中的内容复制到第0阶段,并删除1-3级别(如果它们存在)。
您还可以运行:
git checkout --ours README.txt
或者:
git checkout --theirs README.txt
这将从索引中复制版本2或版本3到工作树。请注意,这不会影响索引内容!它只是从索引中提取(记住,这也称为暂存区,但其中没有任何一个被暂存)到工作树。
您还可以运行:
git checkout branchA -- README.txt
或者:
git checkout branchB -- README.txt
这些命令将从提交 A
或提交 B
中复制已提交的文件到索引的零阶段,然后再从索引复制到工作树。将文件复制到零阶段使其准备好被提交,因为它已经清除了1-3号槽位的条目。
你也可以运行:
git rm README.txt
这将从所有阶段和工作树中删除文件。(仅当正确的合并结果应缺少文件README.txt
时才应执行此操作。)
请注意,您可以从您编写的脚本中执行任何这些操作。您可以使用git ls-files --stage
查看索引。在合并过程中,冲突的文件及其非零阶段号会出现在此处。或者,您可以使用git ls-files -u
仅显示未合并的(阶段大于零)条目。
因此,如果您在使用-X theirs
时遇到合并冲突后运行git ls-files -u
,则会看到仍然存在问题的文件集,所有这些文件都是由高级冲突引起的。然后,您可以从中决定是否可以执行一次性操作来提取所有这些文件的版本。如果可以,请编写一个简短的命令或脚本,将所有这些文件名传递给git checkout branchB
,例如:
git ls-files -u | cut -d$'\t' -f 2 | uniq | xargs git checkout branchB --
只要文件名中没有空格,就可以执行该操作。
theirs
的更改? - A l w a y s S u n n y-x
和-s
提供了什么?我认为git merge other_branch -s theirs
应该会做你想要的事情。 - Hawkins