从会话中解决冲突

Resolve conflicts from am session

我想从一个存储库中挑选多个提交到另一个存储库。我遵循了 this Stack Overflow post 提供的说明:

/path/to/2 $ git --git-dir=/path/to/1/.git format-patch --stdout sha1^..sha1 | git am -3

发生了冲突:

Applying: commit-name-xxx
fatal: sha1 information is lacking or useless (path/to/conflicted/file).
error: could not build fake ancestor
Patch failed at 0001 commit-name-xxx
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

我试图通过 运行 解决冲突:

 git mergetool --tool=DiffMerge

但回复是:

No files need merging

我也 运行 git status 但回复是:

On branch develop
You are in the middle of an am session.
  (fix conflicts and then run "git am --continue")
  (use "git am --skip" to skip this patch)
  (use "git am --abort" to restore the original branch)

nothing to commit, working directory clean

我不确定理解这里发生了什么:我的第一个命令告诉我他们有冲突并且它已移至 .git/rebase-apply/patchgit mergetoolgit status 正在查找此冲突。

TL;DR

您的 Git 没有足够的信息将补丁变成 three-way 合并。如果您的索引中没有不完整的 three-way 合并,您的 git mergetool 就没有任何东西可咬。您可能需要手动应用补丁。

(请记住,索引 这里也称为 暂存区缓存,它用于解决 three-way 合并期间的冲突。在不涉及 three-way 合并的情况下,索引仅存储您为下一个 git commit 构建的文件. 在冲突合并期间,索引存储 甚至更多 个文件。)

如果您 git fetch 从一个存储库(1 中的那个)进入另一个存储库(2),您可以 git cherry-pick 有问题的提交, 作为 。也就是说,在存储库 2 中,您可以将存储库 1 添加为 remote:

git remote add <name> <path-to-repo-1>

然后从它 git fetch 就像从任何其他远程一样,或者您可以使用旧的(Git 1.5 样式)git fetch <path> 语法临时获取所有可访问的对象repo-1 然后 cherry-pick 通过哈希 ID。

如果这仍然不起作用(但会起作用),或者由于某些其他原因不方便,您必须手动应用补丁。考虑使用 git apply --reject 然后手动清理。

这个错误信息告诉我们——好吧,好吧,告诉——发生了什么:

fatal: sha1 information is lacking or useless (path/to/conflicted/file).

您正在使用 git format-patchgit am 从一个 传输 一个补丁 1 Git 存储库到另一个,与人们更通常使用(或过去使用)git format-patch 在没有其他网络连接的站点之间通过电子邮件发送补丁程序的方式相同。当 Git 制作这样的补丁时,它在提交本身的 change-set 中包含每个文件补丁上方的 index 行:

diff --git a/Documentation/RelNotes/2.17.0.txt b/Documentation/RelNotes/2.17.0.txt
index 7001dbbf8..c828d3734 100644

该索引行提供了——至少是潜在地——Git 需要的信息,以便构建 一个完整的three-way 合并,如果可能的话。添加 --full-index 到 format-patch 选项使 index 行更长:

index 7001dbbf88b7ea5822eb0b798ac983505c57b3dc..c828d37345224550540a1665aaed2566d5bcb40e 100644

现在两个哈希值明显更强大了;这在某些情况下会有所帮助。但是他们是什么?

这两个哈希 ID 是存储在存储库中的文件的 blob 哈希 ID——"before" 和 "after" 文件的实际内容。此行后面的 diff hunks 给出 说明: 如果您更改原始 blob(文件)中的这些行,使用这些替换行,您将转换原始 blob — 带有 left-side hash—进入新的 blob,其内容由 right-side hash 命名。

当您将此差异提供给 git apply2 时,HEAD 中的文件可能不再匹配,甚至与一些部分,补丁中的"original blob"。在这种情况下,上下文行将不匹配 and/or "before" 部分将不会出现在文件中的任何位置。无法直接应用补丁。

如果您向 git apply 提供了 --3way-3 标志——并且 git am 这样做了——Git 现在可以使用index 行。由于第一个散列是生成 change-set 的存储库中 实际文件内容 的 blob 散列,您自己的 Git 可以查看您自己的存储库以查看如果您有一个带有该哈希 ID 的 blob。如果是这样,已经有了原始文件。3 Git可以直接提取那个文件并将其修补到位,以生成 "after patch" 版本。

Git 现在拥有文件的 所有三个 版本:基本版本,通过 "before" 哈希 ID 获得,并且偶然地在 你的 仓库; "theirs" 版本,通过将补丁应用于基础版本获得;和 "ours" 版本,这是当前或 HEAD 提交中的文件。所以 Git 现在可以将所有三个版本都填充到您的索引中,现在 three-way 合并是可能的。

另一方面,index 行中的 blob 哈希 ID 可能与存储库中的 no 对象匹配。在这种情况下,您没有文件的 "before" 版本。不可能进行三向合并。或者,有可能 4 您有一个缩短的 blob 散列与存储库中 多个 blob 匹配。在这种情况下,您 可能 拥有文件的 "before" 版本,但 Git 不确定并且不会尝试识别其中任何一个斑点是正确的。

在任何情况下,因为您的 Git 没有足够的信息来尝试 three-way 合并,它不会费心去尝试,让您处于这种情况。使用 git fetchgit cherry-pick 你可以获得真正的 three-way 合并。历史甚至不需要相关,因为 cherry-pick 强制合并基础成为被选择的提交的父级。


1这个我也ks 用于一组补丁,但 format-patch 指令显示它只是一个补丁。

2请注意,git am 本质上只是一个包装器,它在每个补丁上运行 git apply,然后是结果的 git commit

3请记住,Git 的运行假设是因为您正在向 git am 提供补丁,所以 您不没有其他存储库的副本。其他人已通过电子邮件将补丁发送给您。只有 他们 拥有该存储库;您只有 您的 存储库。这在这里不是真的——你有 both 个存储库——但是 Git 不知道!

4机会取决于存储库中 blob 对象的数量,以及缩短的散列的长度。 Git 现在有代码可以自动选择合适的缩写哈希长度,但这基于存储库中对象的数量 在其中生成差异 ,而不是基于接收存储库中的对象数。如果接收存储库大得多,发件人可能无法提供足够长的散列。 Git 的旧版本也没有这种自动计算,默认情况下无条件地使用 28 位哈希;可能太短了。

另一种对我来说效果更好的解决方案是使用 patch --merge 而不是任何 git 工具。