TL;DR
Your Git does not have enough information to turn the patch into a three-way merge. Without an incomplete three-way merge sitting in your index, there is nothing for your git mergetool
to bite into. You may have to manually apply the patch.
(Remember, the index here is the thing also called the staging area and the cache, and it is used to resolve conflicts during three-way merges. In cases that don't involve three-way merges, the index stores only the files you are building up for the next git commit
. During a conflicted merge, the index stores even more files.)
If you git fetch
from one repository (the one in 1
) into the other (2
), you may be able to git cherry-pick
the commit in question, as max630 suggested in a comment. That is, while in repository 2, you can either add repository 1 as a remote:
git remote add <name> <path-to-repo-1>
and then git fetch
from it as you would from any other remote, or you can use the old (Git 1.5 style) git fetch <path>
syntax to temporarily acquire all reachable objects from repo-1 and then cherry-pick by hash ID.
If this still does not work (but it will), or is inconvenient for some other reason, you will have to manually apply the patch. Consider using git apply --reject
followed by manual cleanup.
Long
This error message tells us—well, OK, tells me—what is going on:
fatal: sha1 information is lacking or useless (path/to/conflicted/file).
You are using git format-patch
and git am
to transport one patch1 from one Git repository to another, the same way that people more typically use (or used, in the past) git format-patch
to email patches between sites that have no other network connectivity. When Git makes such a patch, it includes within the change-set for the commit itself an index
line above each file patch:
diff --git a/Documentation/RelNotes/2.17.0.txt b/Documentation/RelNotes/2.17.0.txt
index 7001dbbf8..c828d3734 100644
This index line delivers—at least potentially—the information that Git needs to construct a full three-way merge, if that's possible. Adding --full-index
to the format-patch options makes the index
line longer:
index 7001dbbf88b7ea5822eb0b798ac983505c57b3dc..c828d37345224550540a1665aaed2566d5bcb40e 100644
Now the two hashes are significantly beefier; this can help in some cases. But what are they?
These two hash IDs are the blob hash IDs of the files stored in the repository—the actual content of the "before" and "after" files. The diff hunks that follow this line give instructions: if you change these lines in the original blob (file), using these replacement lines, you will turn the original blob—the content with the left-side hash—into the new blob whose content is named by the right-side hash.
When you feed this diff to git apply
,2 it's possible that the file in HEAD
no longer matches, or even resembles all that much in some parts, the "original blob" in the patch. In this case, the context lines won't match up and/or the "before" section will not appear anywhere in the file. A direct application of the patch becomes impossible.
If you have supplied the --3way
or -3
flag to git apply
—and git am
does so—Git can now use the information in the index
line. Since the first hash is the blob hash of the actual file content in the repository that produced the change-set, your own Git can look in your own repository to see if you have a blob with that hash ID. If so, you have the original file already.3 Git can just extract that file and patch it in place, to produce the "after patch" version.
Git now has all three versions of the file: the base version, obtained through the "before" hash ID and, fortuitously, found in your repository; the "theirs" version, obtained by applying the patch to the base version; and the "ours" version, which is the file in the current or HEAD
commit. So Git can now stuff all three versions into your index, and now a three-way merge is possible.
On the other hand, it's possible that the blob hash ID in the index
line matches no object in your repository. In that case, you don't have the "before" version of the file. It's impossible to do a three way merge. Or, it's possible, however unlikely,4 that you have a shortened blob hash that matches more than one blob in your repository. In this case, you might have the "before" version of the file, but Git doesn't know for sure and will not attempt to identify whether any of those blobs are the correct one.
In any case, because your Git does not have enough information to attempt a three-way merge, it doesn't bother trying, leaving you in this situation. Using git fetch
and git cherry-pick
you can get a true three-way merge after all. The histories need not even be related, as cherry-pick forces the merge base to be the parent of the commit being picked.
1This also works for a set of patches, but the format-patch directives show that it's just one patch.
2Note that git am
is essentially just a wrapper that runs git apply
on each patch, followed by git commit
of the result.
3Remember, Git is operating on the assumption that because you are feeding a patch to git am
, you don't have a copy of the other repository. Someone else has emailed you a patch. Only they have that repository; you have only your repository. This is not true here—you have both repositories—but Git doesn't know that!
4The chance depends on the number of blob objects in your repository, and the length of the shortened hash. Git now has code to automatically choose an appropriate abbreviated hash length, but this works based on the number of objects in the repository in which the diff is being generated, not on the number of objects in the receiving repository. If the receiving repository is significantly larger, the sender might not offer a long enough hash. Older versions of Git don't have this automatic computation either, and by default just use 28 bits of hash unconditionally; that may be too short.
git-am
seems to use very different implementation than cherry-picking, and by my experience it works much worse. Is it really not possible to fetch from the original repository and then use regular cherry-pick? – Hobbism