7

我想从一个存储库中挑选多个提交到另一个存储库。我按照此 Stack Overflow 帖子提供的说明进行操作:

/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/patch但没有git mergetool发现git status这个冲突。

4

2 回答 2

5

TL;博士

您的 Git 没有足够的信息将补丁转换为三向合并。如果您的索引中没有不完整的三向合并,那么您就没有什么git mergetool可咬的了。您可能必须手动应用补丁。

(记住,这里的索引也叫暂存区缓存,是用来解决三路合并时的冲突的。在不涉及三路合并的情况下,索引只存储你的文件正在为下一个构建git commit。在冲突合并期间,索引存储更多文件。)

如果您git fetch从一个存储库(in 中的一个1)进入另一个(2),您可能能够git cherry-pick提交有问题的提交,正如max630 在评论中建议的那样。也就是说,在存储库 2 中,您可以将存储库 1 添加为远程:

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

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

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

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

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

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

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

如果可能的话,这条索引行至少会提供 Git构建完整的三向合并所需的信息。添加--full-index到格式补丁选项会使index行更长:

index 7001dbbf88b7ea5822eb0b798ac983505c57b3dc..c828d37345224550540a1665aaed2566d5bcb40e 100644

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

这两个哈希 ID 是存储在存储库中的文件的blob 哈希 ID—— “之前”和“之后”文件的实际内容。此行后面的 diff hunks 给出说明:如果您更改原始 blob(文件)中的这些行,使用这些替换行,您将把原始 blob(具有左侧哈希的内容)转换为新的 blob,其内容由右侧哈希命名。

当您将此差异提供给git apply, 2时,文件中的文件可能HEAD不再匹配,甚至在某些部分与补丁中的“原始 blob”非常相似。在这种情况下,上下文行将不匹配和/或“之前”部分不会出现在文件中的任何位置。直接应用补丁变得不可能。

如果你提供了--3wayor-3标志——并且这样做了git apply——Gitgit am现在可以使用该index行中的信息。由于第一个哈希是生成更改集的存储库中实际文件内容的 blob 哈希,因此您自己的 Git 可以查看您自己的存储库以查看您是否有具有该哈希 ID 的 blob。如果是这样,已经拥有原始文件。3 Git 可以提取文件并对其进行修补,以生成“修补后”版本。

Git 现在拥有该文件的所有三个版本:基本版本,通过“之前”哈希 ID 获得,幸运的是,在您的存储库中找到;“他们的”版本,通过将补丁应用到基础版本而获得;以及“我们的”版本,即当前或HEAD提交中的文件。因此,Git 现在可以将所有三个版本都填充到您的索引中,并且现在可以进行三向合并。

另一方面,该行中的 blob 哈希 ID 可能与存储库中的任何对象都不index匹配。在这种情况下,您没有文件的“之前”版本。不可能进行三向合并。或者,您有一个缩短的 blob 哈希值与存储库中的多个 blob匹配但这种可能性不大,但这种可能性不大。在这种情况下,您可能拥有该文件的“之前”版本,但 Git 不确定并且不会尝试识别这些 blob 中的任何一个是否是正确的。

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


1这也适用于一组补丁,但格式补丁指令显示它只是一个补丁。

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

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

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

于 2018-04-06T15:11:04.267 回答
3

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

于 2018-09-17T12:17:57.407 回答