I have a Git diff created with
git diff --full-index --ignore-submodules > mypatch.diff
git apply --3way mypatch.diff
error: patch failed: dir/file:189
error: repository lacks the necessary blob to fall back on 3-way merge.
error: dir/file: patch does not apply
When the patch does not apply cleanly, fall back on 3-way merge if the patch records the identity of blobs it is supposed to apply to, and we have those blobs available locally, possibly leaving the conflict markers in the files in the working tree for the user to resolve.
You probably need to run
git fetch (and you may need to tweak your
fetch refspecs, and/or use
The blob(s) that Git needs are specific versions of the files (pathnames are irrelevant at this point).
For instance, consider this bit of patch:
diff --git a/fmt.py b/fmt.py index 2069319..0c8a68f 100755 --- a/fmt.py +++ b/fmt.py @@ -207,7 +207,8 @@ def main():
Let's take a look at commit
7124b135...'s version of
fmt.py, and then its parent's version:
$ git rev-parse 7124b135:fmt.py 0c8a68f9dc05a7399d06693c0b2761fb43ed0b58 $ git rev-parse 7124b135^:fmt.py 2069319dae4bd87cb6e1b7c18d86ebededeb00c8
Now look back at that
index ... line:
index 2069319..0c8a68f 100755
There are those blob IDs. Git is comparing blob
2069319... against blob
0c8a68f..., which are the versions in the parent of the commit being shown, and the commit itself.
If I were to send this patch to you, and you asked your Git to apply it and it did not apply cleanly, Git's "fall back to 3-way merge" requires that it find or construct three versions of the file: your current one, the one identified by
2069319, and my version.
Clearly your Git has your current version, so there's no problem there. Clearly it does not have my version, so it must construct it. The way it can construct it is to find version
The complaint you are seeing is because your Git cannot find, in your repository, anything identified by the SHA-1 ID on the left of the
.. in the
index <sha1>..<sha1> <mode> line.
(If your Git could find that blob, it would extract it to a temporary file, apply the patch to that temporary file, and hence have my version; and then it would have the three versions it needs to do a 3-way merge.)
If the patch is against a commit that is currently in a repository that you can
git fetch from, you should be able to run
git fetch and obtain that commit, which will obtain all the trees and blobs associated with that commit.
git fetch needs to pick up that commit. The commits that
git fetch normally picks up are all the ones in
refs/heads/* on the remote, as controlled by the
fetch = line(s) associated with that particular remote, that you don't already have.1
It's possible that the commit(s) in the remote that retain this blob there are not under
refs/heads/* (e.g., if it's only in a stash now, or attached to an outdated tag or a branch that was rebased). In this case,
git fetch probably won't bring over the necessary blob. There is a way to get it if you can log in on the remote: just make a branch or tag that points to a commit that leads to the blob in question. (Finding such a commit can be hard, unless your patch has the commit ID right in it, then it's easy.) Then direct your Git to fetch that branch or tag from the other Git, and you'll get the necessary blob.
1Note that if you've made a shallow clone, Git won't proceed past your existing commits. Your commits end at some cut-off point, which is what makes your clone shallow. If the missing blob is on an ordinary branch after all, but is "below" a cutoff, you will need to deepen your repository, either with a deeper
--depth= argument, or by adding
--unshallow to your fetch command, so as to deepen it "all the way".
Computing the correct depth is impossible on the shallow-clone side. (The count is: "How many commits down do I have to dig from a branch tip, to get to the target commit?" What you have in your repository stops before we get that far, so we don't know how far it is. We could count how far down all of your branches go, and pick a number bigger than that, which would definitely deepen your repository—but we don't know if that's the right number.)
It is possible on the other repository, but if you have direct access to that repository, you don't have to count, you can just make a branch or tag name pointing to the commit.
What this means in the end that the easiest thing, by far, is to just use
git fetch --unshallow.