【问题标题】:Merging two branches, how do I accept one branch for all conflicts合并两个分支,如何为所有冲突接受一个分支
【发布时间】:2018-12-02 03:41:23
【问题描述】:

我将两个分支合并在一起,比如说 branchA 和 branchB。他们有大约 100 个冲突的文件。

branchB 的所有工作都已被接受,并且是我需要的 100%。我不想强制推送 branchB 或任何东西。

有没有一种方法可以将两者合并,只要说任何冲突都接受 branchB 上的内容,这样我就不必打开文件或“他们的”或“我们的”每个文件。

正在尝试使用 -s 和 -x 选项从 branchB 拉取 branchA 进行递归合并,但这似乎没有按预期工作

谢谢

【问题讨论】:

标签: git git-merge


【解决方案1】:

根据您的要求,答案很简单:

git checkout branchA
git merge -X theirs branchB

在你盲目地应用它之前,请确保你知道你在做什么!您还说您使用了-s-x,但git merge 没有-x 选项。 (也许您的意思是-X。显示您使用的实际命令,以及至少一些结果,可能会有所帮助。)如果您遇到了我所说的高级冲突 em>,-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 brachA; git merge branchB 时,你有你的 Git find 为你提交 *——这是你们俩开始的地方,无论它在历史上可能有多远——然后你就有了你的 Git 运行两个 diff 命令:

git diff --find-renames <hash-of-*> <hash-of-A>
git diff --find-renames <hash-of-*> <hash-of-B>

如果可以的话,Git 现在将尝试合并两组更改,将它们应用于提交 * 中的任何内容。如果 Git 可以自己完成所有这些,它会进行最终的合并提交——一个几乎普通的提交,除了它有两个父指针,指向提交 A 和提交 @ 987654343@。这会将名称 branchA 更改为也指向新的提交,给出:

          o--o--A
         /       \
...--o--*         M   <-- branchA (HEAD)
         \       /
          o--o--B   <-- branchB

与新提交 M 关联的快照 是 Git 将您的更改(*A)与它们的更改(*B)组合的结果到*。 (在此过程中,任何中间提交都将被完全忽略。Git 只对申请 * 两个变更集的联合感兴趣。)

*-vs-A 中某些文件的某些更改与*-vs-B 中某些文件的某些更改发生冲突时,Git通常会因合并冲突而停止。大写的 -X 选项让您告诉 Git,如果发生冲突,它应该更喜欢“我们的”(*-vs-A)或“他们的”(@ 987654360@-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 更改集更改了第 3 行,所以该更改不会冲突,因此 Git 将应用它。使用-X theirs,Git 将更喜欢他们在第 20 行的更改,即使将第 3 行更改为“棕色”,它现在也将包含“大黄牛”字样。

鉴于-X theirsGit 认为这是正确的解决方案:您的 无冲突更改带到第 3 行,他们的 对第 20 行的更改发生冲突。这很可能是错误的,但这就是 Git 会做的。有时最好让冲突发生,然后自己去检查,看看正确的解决方案是什么;但是如果你有很好的测试,你应该可以捕捉到大多数 Git 无法解决的问题,所以-X theirs(或-X ours)是一个有用的工具。

高级别的冲突

如果您已经使用-X theirs 并且仍然存在冲突,那么您所拥有的就是我所说的高级冲突。请注意,上面的两个git diff 命令,要查找从*A 以及从*B 的更改集,都使用--find-renames。这意味着在 *A 之间,Git 可能会决定您(或用户 A 无论如何)重命名 README.txtREADME.rst。用户B 也将其重命名为read-me.rst。 Git 不知道要使用哪个 name-X theirs 也没有告诉它:使用他们的名字。 Git 只是因冲突而停止。

如果您都添加了同名的新文件,或者如果您修改了某个文件并且他们将其删除,或者反之亦然,则会发生类似的高级冲突。前者是添加/添加冲突,后者是修改/删除冲突。还有几个这样的冲突。所有这些都意味着 Git 要么不知道要保留哪些文件,要么要使用哪个名称

在所有这些情况下,Git 都会因合并冲突而停止,就像您没有使用 -X 时一样。当 Git 确实以这种方式停止时,它会将 所有 文件留在索引/暂存区域中,使用 阶段编号 来识别哪个是哪个。例如,如果 README.txt 中存在冲突,则索引中现在有 三个副本 README.txt

  • :1:README.txt 是来自基本提交的版本,提交 *
  • :2:README.txt 是来自HEAD 提交的版本,提交A
  • :3:README.txt 是他们提交的版本,提交 B

对于添加/添加冲突,版本 1 不存在(文件不在基础中)。对于修改/删除冲突,版本 2 或 3 都不存在(文件已在 AB 中删除)。

例如,您可以在这些名称上使用 git showgit show :1:README.txt 以查看基本版本。

工作树一个README.txt 的副本,这通常是 Git 在组合三个输入时的最大努力,可能带有冲突标记。

您现在的任务是为文件创建阶段零条目:0:README.txt,清除阶段 1-2-3 条目。最简单的方法通常是编辑工作树文件并运行git add README.txtgit 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

这些将 commit 文件从 commit A 或 commit B 复制到第 0 阶段的索引中,然后从索引复制到工作树.复制到阶段 0 使文件准备好提交,因为它已经清除了插槽 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 --

(只要文件名中没有空格)。

【讨论】:

  • 请注意,这通常不会正确尊重您希望签出的分支中已删除的文件,即使已删除文件中没有冲突。
  • @ely:如果您的意思是git ls-files -u 脚本:是的,如果在修改/删除高级冲突之后有未合并的文件,您可能需要检查未合并的文件占用的暂存槽。但是,如果文件X 不在branchB 中(它在branchA 端进行了修改,并在branchB 端删除)你至少会看到来自git checkout branchB -- X 的错误。
  • 我可以确认您在您提到的最后一个案例中没有看到错误。 git checkout master 然后git checkout origin/other-branch -- . 这会将other-branch 中的任何内容覆盖到发出命令的本地工作master 分支中,但它会默默地留下master 上存在但在other-branch 上删除的任何文件,并且它们不会出现在 git status 的输出中。
  • @ely:我不推荐git checkout other-branch -- .,而是明确命名单个文件。这就是xargs 的用途。
  • 这通常是棘手的,例如,当解决可能有数千个不同文件冲突且没有特定模式的分支之间的冲突时,您可以在文件名上形成以区分要保留的文件。在其他情况下,您可能需要一个分支的尖端真正成为另一个分支的尖端,这意味着您实际上需要第二个分支的全部内容,并且您不希望任何可能保留文件方面的尝试合并解决方案添加到计算的合并分辨率的第一个分支。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多