nanuqcz
nanuqcz

Reputation: 1424

git am error: "patch does not apply"

I am trying to move several commits from one project to the second, similar one, using git.

So I created a patch, containing 5 commits:

git format-patch 4af51 --stdout > changes.patch

Then move the patch to second project's folder and wants to apply the patch:

git am changes.patch 

...but it gives me error:

Applying: Fixed products ordering in order summary.
error: patch failed: index.php:17
error: index.php: patch does not apply
Patch failed at 0001 Fixed products ordering in order summary.
The copy of the patch that failed is found in:
   c:/.../project2/.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".

So I opened the index.php, but nothing changed there. I assume some >>>>>>> marks etc., like when resolving merge conflict, but no conflict was marked in the file. git status gave me also empty list of changed files (only changes.patch was there). So I run git am --continue, but another error appears:

Applying: Fixed products ordering in order summary.
No changes - did you forget to use 'git add'?
If there is nothing left to stage, chances are that something else
already introduced the same changes; you might want to skip this 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". 

I am using Windows 7 and newest git version "1.9.4.msysgit.1"

P.S. After few hours of googling, I found few solutions, but nothing works for me:


git am -3 changes.patch 

gives strange "sha1 information" error:

Applying: Fixed products ordering in order summary.
fatal: sha1 information is lacking or useless (index.php).
Repository lacks necessary blobs to fall back on 3-way merge.
Cannot fall back to three-way merge.
Patch failed at 0001 Fixed products ordering in order summary.
The copy of the patch that failed is found in:
   c:/.../project2/.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 am changes.patch --ignore-whitespace --no-scissors --ignore-space-change

gives first error as above: "error: patch failed: index.php:17", but no conflict marks in index.php was added.

Upvotes: 106

Views: 164971

Answers (9)

x-yuri
x-yuri

Reputation: 18943

tl;dr Temporarily add the source repository (git remote add NAME ../some/path), fetch the objects (git fetch NAME), then cherry-pick the commit.

There's a difference between applying a patch and doing a 3-way merge. With a patch you have a file that you want to patch and a diff that contains changes surrounded by their contexts. If a context changes, git (or patch for that matter) can no longer apply the patch because it assumes, "As long as the context matches, that's the target lines." (It's a bit more elaborate than that, but let's keep it simple.)

Let's say you want to patch (revision A, or HEAD):

aa
b
c
d
e

with (a simplified patch):

 a
 b
-c
+cc
 d
 e

Here git doesn't know what the file looked like before applying the patch (the file based on which the patch was created). Let's call the revision before applying the patch B, and after applying the patch C. Putting it in terms of revisions, git doesn't know what revision B of the file looked like. On one hand it could assume that before revision A you did a -> aa, and it should apply the patch. On the other hand revision B could look like this:

aa
b
c
d
e
a
b
c
d
e

Which means that before revision A you deleted the last 5 lines (a-e) and the patch should not be applied.

That's what happens when you don't pass -3 to git am. When you do pass -3 it tries to do a 3-way merge. And if it has enough information, it either succeeds, or leaves you with the context markers. So, what exactly does it need? It needs to know what the file looked like (full contents of the file) in all 3 revisions. For that git format-patch provides each file with 2 hashes: a hash of the file contents before the change (a blob hash), and another one after the change (see the index line). Now, if git am -3 can find the blobs by their hashes in your (target) repositony, it knows what the file looked like in all 3 revisions, and can do a 3-way merge.

For example, revision A (HEAD):

aa
b
c
d
e

revision B (before the patch):

a
b
c
d
e

revision C (after the patch):

a
b
cc
d
e

Knowing this it sees that you changed the first line, the patch changes the third, which can be applied w/o a conflict.

Now, how to make revisions B and C available for git? One way would be to temporarily add the source repository (a remote) where you created the patch (git remote add NAME ../some/path), and do git fetch NAME. After applying the patch (git am -3 PATCH) you can delete the remote (git remote rm NAME) if it's no longer needed. But then... you can just cherry-pick the commit.

One can apply changes w/o adding a remote, but that doesn't look too practical.

Here's a reply on the mailing list, and my experiments. Just in case.

Upvotes: 1

x-yuri
x-yuri

Reputation: 18943

With patches git is more picky compared to cherry-pick/rebase/merge conflicts. E.g. given the following file:

line 1
line 2
line 3

and the following changes (this is not a diff, it's an illustration of changes in a diff-like form):

 line 11
 line 2
-line 3
+line 33

With rebase/merge there will be no conflict, with am there will. Steps to reproduce can be found here.

As such, one option would be to change the patch until it applies.

Upvotes: 0

torek
torek

Reputation: 489083

What is a patch?

A patch is little more (see below) than a series of instructions: "add this here", "remove that there", "change this third thing to a fourth". That's why Git tells you:

The copy of the patch that failed is found in:
c:/.../project2/.git/rebase-apply/patch

You can open that patch in your favorite viewer or editor, open the files-to-be-changed in your favorite editor, and "hand apply" the patch, using what you know (and Git does not) to figure out how "add this here" is to be done when the files-to-be-changed now look little or nothing like what they did when they were changed earlier, with those changes delivered to you as a patch.

A little more

A three-way merge introduces that "little more" information than the plain "series of instructions": it tells you what the original version of the file was as well. If your repository has the original version, your Git software, working in your repository, can compare what you did to a file, to what the patch says to do to the file.

As you saw above, if you request the three-way merge, Git can't find the "original version" in the other repository, so it can't even attempt the three-way merge. As a result you get no conflict markers, and you must do the patch-application by hand.

(There are cases where the "a little more" part is missing. The extra information is supplied by the Index: line in the patch, e.g.:

diff --git a/go.mod b/go.mod
index 1fefa60..38a3a41 100644

The second line may read, e.g.:

index 1fefa6021dcd205c1243e236d686595920d9621b..38a3a41434fda3a68ce3356092a89afca81eb614 100644

in more-complete cases. Note the two hash IDs, separated by two dots. The one on the left is the one your Git software will use, to attempt to find a file in your own repository that has the given hash ID. Applying the supplied patch to that file, which must exist, must work and must produce a file whose hash ID will be that on the right, and doing all of this gives Git all the additional information required.)

Using --reject

When you have to apply the patch by hand, it's still possible that Git can apply most of the patch for you automatically and leave only a few pieces to the entity with the ability to reason about the code (or whatever it is that needs patching). Adding --reject tells Git to do that, and leave the "inapplicable" parts of the patch in rejection files. If you use this option, you must still hand-apply each failing patch, and figure out what to do with the rejected portions.

Once you have made the required changes, you can git add the modified files and use git am --continue to tell Git to commit the changes and move on to the next patch.

What if there's nothing to do?

Since we don't have your code, I can't tell if this is the case, but sometimes, you wind up with one of the patches saying things that amount to, e.g., "fix the spelling of a word on line 42" when the spelling there was already fixed.

In this particular case, you, having looked at the patch and the current code, should say to yourself: "aha, this patch should just be skipped entirely!" That's when you use the other advice Git already printed:

If you prefer to skip this patch, run "git am --skip" instead.

If you run git am --skip, Git will skip over that patch, so that if there were five patches in the mailbox, it will end up adding just four commits, instead of five (or three instead of five if you skip twice, and so on).

Upvotes: 128

srs
srs

Reputation: 2671

I had the same problem. I had used

git format-patch <commit_hash>

to create the patch. My main problem was patch was failing due to some conflicts, but I could not see any merge conflict in the file content. I had used git am --3way <patch_file_path> to apply the patch.

The correct command to apply the patch should be:

git am --3way --ignore-space-change <patch_file_path>

If you execute the above command for patching, it will create a merge conflict if patch apply fails. Then you can fix the conflict in your files, like the same way merge conflicts are resolved for git merge

Upvotes: 26

MikeBeaton
MikeBeaton

Reputation: 3892

This kind of error can be caused by LF vs CRLF line ending mismatches, e.g. when you're looking at the patch file and you're absolutely sure it should be able to apply, but it just won't.

To test this out, if you have a patch that applies to just one file, you can try running 'unix2dos' or 'dos2unix' on just that file (try both, to see which one causes the file to change; you can get these utilities for Windows as well as Unix), then commit that change as a test commit, then try applying the patch again. If that works, that was the problem.

NB git am applies patches as LF by default (even if the patch file contains CRLF), so if you want to apply CRLF patches to CRLF files you must use git am --keep-cr, as per this answer.

Upvotes: 8

I faced same error. I reverted the commit version while creating patch. it worked as earlier patch was in reverse way.

[mrdubey@SNF]$ git log 65f1d63 commit 65f1d6396315853f2b7070e0e6d99b116ba2b018 Author: Dubey Mritunjaykumar

Date: Tue Jan 22 12:10:50 2019 +0530

commit e377ab50081e3a8515a75a3f757d7c5c98a975c6 Author: Dubey Mritunjaykumar Date: Mon Jan 21 23:05:48 2019 +0530

Earlier commad used: git diff new_commit_id..prev_commit_id > 1 diff

Got error: patch failed: filename:40

working one: git diff prev_commit_id..latest_commit_id > 1.diff

Upvotes: 0

Sandeep K V
Sandeep K V

Reputation: 97

Had several modules complain about patch does not apply. One thing I was missing out was that the branches had become stale. After the git merge master generated the patch files using git diff master BRANCH > file.patch. Going to the vanilla branch was able to apply the patch with git apply file.patch

Upvotes: 0

Pini Cheyni
Pini Cheyni

Reputation: 5437

I had this error, was able to overcome it by using : patch -p1 < example.patch

I took it from here: https://www.drupal.org/node/1129120

Upvotes: 8

wulfgarpro
wulfgarpro

Reputation: 6934

git format-patch also has the -B flag.

The description in the man page leaves much to be desired, but in simple language it's the threshold format-patch will abide to before doing a total re-write of the file (by a single deletion of everything old, followed by a single insertion of everything new).

This proved very useful for me when manual editing was too cumbersome, and the source was more authoritative than my destination.

An example:

git format-patch -B10% --stdout my_tag_name > big_patch.patch
git am -3 -i < big_patch.patch

Upvotes: 8

Related Questions