合并两个分支,我如何接受所有冲突的一个分支
Merging two branches, how do I accept one branch for all conflicts
我正在将两个分支合并在一起,比如说 branchA 和 branchB。他们有大约 100 个冲突的文件。
branchB 已经接受了所有的工作,并且是我需要的 100%。我不想强制推送 branchB 或任何东西。
有没有一种方法可以将两者合并,如果有任何冲突,请接受 branchB 上的内容,这样我就不必打开文件或 "theirs" 或 "ours" 每个文件。
正在尝试使用 -s 和 -x 选项从 branchB 拉取 branchA 进行递归合并,但这似乎没有按预期工作
谢谢
根据您的要求,答案是简单的序列:
git checkout branchA
git merge -X theirs branchB
在你盲目应用之前,确保你知道你在做什么!您还说您使用了 -s
和 -x
,但是 git merge
没有 -x
选项。 (也许你的意思是 -X
。显示你使用的 实际命令 ,以及至少一些结果,可能会有所帮助。)如果你得到我所说的 高级别冲突,-X
选项无济于事,您需要进行一些手动清理,但您可以自动执行部分甚至全部清理工作。
合并要素
记住 git merge
所做的是 合并 变化。为此,它需要找到一个共同的起点。假设您是在 branchA 上工作的 Alice 或 Adam,或者在 branchB 上工作的 Bob 或 Barbara。用户 "A" 从某个提交 *
开始并进行了更多提交 o
并以提交 A
:
结束
o--o--A <-- branchA
/
...--o--*
同时,用户 "B" 从 完全相同的提交 开始,即标记为 *
的提交,并进行了一些提交:
...--o--*
\
o--o--B <-- branchB
例如,在 运行ning git fetch
之后,您的存储库中现在拥有的是完整的提交集:
o--o--A <-- branchA
/
...--o--*
\
o--o--B <-- branchB
当你 运行 git checkout brachA; 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 可以自己完成所有这些,它会进行最终的 merge 提交 ——一个几乎普通的提交,除了它有两个父指针,指向两个提交 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,如果发生冲突,它应该选择 "our"(*
-vs-A
) 或 "their" (*
-vs-B
) 变化。但是,如果 没有 冲突,Git 将继续进行 两个 更改。这适用于 单个文件 。举个例子可能会有帮助。
假设 *
-vs-A
变更集包含文件 README.txt
:
的这些说明
将第 3 行的单词 "yellow" 更改为 "brown"。(它实际上看到整行都已更改,但让我们在这里使用 "word"。)
将第 20 行的单词 "the yellow cow" 更改为 "a brown cow"。
假设 *
-vs-B
变更集包含文件 README.txt
:
的这些指令
- 将第 20 行的单词 "the yellow cow" 更改为 "a large yellow cow"。
因为两个变更集都试图更改第 20 行,所以这里会发生冲突。因为只有 one change-set 更改第 3 行,那个更改不会冲突,所以 Git 会应用它。对于 -X theirs
,Git 将更喜欢他们在第 20 行的更改,即使它已将第 3 行更改为 "brown".
,但现在会有 "the large yellow cow" 字样
鉴于 -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 不知道使用哪个 name,-X theirs
也没有告诉它:使用他们的名字。 Git 因冲突而停止。
如果你们都添加了一个同名的新文件,或者如果你们修改了某个文件而他们删除了它,或者反之亦然,则会发生类似的高级冲突。前者是add/add冲突,后者是modify/delete冲突。还有几个这样的冲突。所有这些都意味着 Git 不知道 保留哪个文件 或 使用哪个名称.
在所有这些情况下,Git 将因合并冲突而停止,就像您没有使用 -X
时一样。当 Git 确实以这种方式停止时,它将 all 文件留在索引/暂存区中,使用 stage numbers 来识别哪个一个是哪个。例如,如果 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
命令将工作树中的任何内容复制到阶段零,如果阶段 1-3 存在则删除它们。
你也可以运行:
git checkout --ours README.txt
或:
git checkout --theirs README.txt
这将分别从索引复制版本 2 或版本 3 到工作树。请注意,这不会影响索引内容!它仅从 索引中提取 ——请记住,这也称为暂存区,但其中的三个条目 none 尚未暂存到工作树。
你也可以运行:
git checkout branchA -- README.txt
或:
git checkout branchB -- README.txt
这些从 commit A
或 commit B
复制 committed 文件到阶段的索引中零,然后从索引到工作树。复制到零阶段使文件准备好提交,因为它已经清除了插槽 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 --
(只要文件名中没有白色-space)。
我正在将两个分支合并在一起,比如说 branchA 和 branchB。他们有大约 100 个冲突的文件。
branchB 已经接受了所有的工作,并且是我需要的 100%。我不想强制推送 branchB 或任何东西。
有没有一种方法可以将两者合并,如果有任何冲突,请接受 branchB 上的内容,这样我就不必打开文件或 "theirs" 或 "ours" 每个文件。
正在尝试使用 -s 和 -x 选项从 branchB 拉取 branchA 进行递归合并,但这似乎没有按预期工作
谢谢
根据您的要求,答案是简单的序列:
git checkout branchA
git merge -X theirs branchB
在你盲目应用之前,确保你知道你在做什么!您还说您使用了 -s
和 -x
,但是 git merge
没有 -x
选项。 (也许你的意思是 -X
。显示你使用的 实际命令 ,以及至少一些结果,可能会有所帮助。)如果你得到我所说的 高级别冲突,-X
选项无济于事,您需要进行一些手动清理,但您可以自动执行部分甚至全部清理工作。
合并要素
记住 git merge
所做的是 合并 变化。为此,它需要找到一个共同的起点。假设您是在 branchA 上工作的 Alice 或 Adam,或者在 branchB 上工作的 Bob 或 Barbara。用户 "A" 从某个提交 *
开始并进行了更多提交 o
并以提交 A
:
o--o--A <-- branchA
/
...--o--*
同时,用户 "B" 从 完全相同的提交 开始,即标记为 *
的提交,并进行了一些提交:
...--o--*
\
o--o--B <-- branchB
例如,在 运行ning git fetch
之后,您的存储库中现在拥有的是完整的提交集:
o--o--A <-- branchA
/
...--o--*
\
o--o--B <-- branchB
当你 运行 git checkout brachA; 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 可以自己完成所有这些,它会进行最终的 merge 提交 ——一个几乎普通的提交,除了它有两个父指针,指向两个提交 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,如果发生冲突,它应该选择 "our"(*
-vs-A
) 或 "their" (*
-vs-B
) 变化。但是,如果 没有 冲突,Git 将继续进行 两个 更改。这适用于 单个文件 。举个例子可能会有帮助。
假设 *
-vs-A
变更集包含文件 README.txt
:
将第 3 行的单词 "yellow" 更改为 "brown"。(它实际上看到整行都已更改,但让我们在这里使用 "word"。)
将第 20 行的单词 "the yellow cow" 更改为 "a brown cow"。
假设 *
-vs-B
变更集包含文件 README.txt
:
- 将第 20 行的单词 "the yellow cow" 更改为 "a large yellow cow"。
因为两个变更集都试图更改第 20 行,所以这里会发生冲突。因为只有 one change-set 更改第 3 行,那个更改不会冲突,所以 Git 会应用它。对于 -X theirs
,Git 将更喜欢他们在第 20 行的更改,即使它已将第 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 不知道使用哪个 name,-X theirs
也没有告诉它:使用他们的名字。 Git 因冲突而停止。
如果你们都添加了一个同名的新文件,或者如果你们修改了某个文件而他们删除了它,或者反之亦然,则会发生类似的高级冲突。前者是add/add冲突,后者是modify/delete冲突。还有几个这样的冲突。所有这些都意味着 Git 不知道 保留哪个文件 或 使用哪个名称.
在所有这些情况下,Git 将因合并冲突而停止,就像您没有使用 -X
时一样。当 Git 确实以这种方式停止时,它将 all 文件留在索引/暂存区中,使用 stage numbers 来识别哪个一个是哪个。例如,如果 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
命令将工作树中的任何内容复制到阶段零,如果阶段 1-3 存在则删除它们。
你也可以运行:
git checkout --ours README.txt
或:
git checkout --theirs README.txt
这将分别从索引复制版本 2 或版本 3 到工作树。请注意,这不会影响索引内容!它仅从 索引中提取 ——请记住,这也称为暂存区,但其中的三个条目 none 尚未暂存到工作树。
你也可以运行:
git checkout branchA -- README.txt
或:
git checkout branchB -- README.txt
这些从 commit A
或 commit B
复制 committed 文件到阶段的索引中零,然后从索引到工作树。复制到零阶段使文件准备好提交,因为它已经清除了插槽 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 --
(只要文件名中没有白色-space)。