threads / discuss / 58100

Why `git am -3` apply patches that don't normally apply?

Subject: Why `git am -3` apply patches that don't normally apply?

## tl;dr

4 messages between Jul 3, 2022 and Jul 6, 2022.

replies: 3people: 3as markdown or json

Yuri Kanivetsky· Jul 3, 2022, 12:11 UTC · lore
Hi,

From what I can see w/o `-3` `git am` follows the `patch`'es behavior. And the `patch`'es behavior is if there are lines in a patch that doesn't match the lines in the source file, it fails. For example, a source file:

11 2 3

A patch:
 1
 2
-3
+33

But `git am -3` will apply such a patch. That is, `patch` sees that the first line changed and that prevents it from applying the patch, but `git am -3` decides that it's okay. Why is that?

A script that reproduces the issue:
    set -eux
    mkdir a
    (cd a
    git init
    echo '1
    2
    3' > a
    git add a
    git commit -m 1,2,3
    sed -Ei 's/3/33/' a
    git add a
    git commit -m '3 -> 33'
    git format-patch -1 HEAD)
    mkdir b
    (cd b
    git init
    echo '11
    2
    3' > a
    git add a
    git commit -m 11,2,3
    git remote add a ../a
    git fetch a
    cat a
    cat ../a/0001-3-33.patch
    git --no-pager log --oneline --graph --all
    git am "$@" ../a/0001-3-33.patch  # try it w/ and w/o -3
    git --no-pager log --oneline --graph --all)

A couple of links, just in case, which have more information and show where it comes from:

https://stackoverflow.com/a/25847019/52499 https://gist.github.com/x-yuri/9907d5b9cbf29e84d510902a776741df https://gist.github.com/x-yuri/57d08f6afb10a89856d6e6a62abe30d4

Regards, Yuri

Elijah Newren· Jul 4, 2022, 19:23 UTC · re: Yuri Kanivetsky · lore

Re: Why `git am -3` apply patches that don't normally apply?

On Sun, Jul 3, 2022 at 6:09 AM Yuri Kanivetsky <yuri.kanivetsky@gmail.com> wrote:

Show 20 quoted lines
>
> Hi,
>
> From what I can see w/o `-3` `git am` follows the `patch`'es behavior.
> And the `patch`'es behavior is if there are lines in a patch that
> doesn't match the lines in the source file, it fails. For example, a
> source file:
>
> 11
> 2
> 3
>
> A patch:
>
>  1
>  2
> -3
> +33
>
> But `git am -3` will apply such a patch.

Will apply the changes, but they might result in conflicts; it depends on what you apply it to.

> That is, `patch` sees that
> the first line changed and that prevents it from applying the patch,
> but `git am -3` decides that it's okay. Why is that?

Because `git am -3` is doing a three-way merge, as requested, where it is no longer using a patch with changes and a context region but instead has three full files that it can three-way merge. Let's dive into your example to explain...

Show 16 quoted lines
> A script that reproduces the issue:
>
>     set -eux
>
>     mkdir a
>     (cd a
>     git init
>     echo '1
>     2
>     3' > a
>     git add a
>     git commit -m 1,2,3
>     sed -Ei 's/3/33/' a
>     git add a
>     git commit -m '3 -> 33'
>     git format-patch -1 HEAD)

So you have a repository with two commits. Also, format-patch puts references to the before-and-after blob hashes of the file 'a' into the diff.

Show 11 quoted lines
>     mkdir b
>     (cd b
>     git init
>     echo '11
>     2
>     3' > a
>     git add a
>     git commit -m 11,2,3
>
>     git remote add a ../a
>     git fetch a

So 'b' is a repository with 3 commits; 1 that you added to it directly, and 2 that you fetched into it from your other repository.

If you had not done the fetch from 'a' into 'b', attempting to "git am -3" the patch you created earlier would fail.

>     cat a
>     cat ../a/0001-3-33.patch
>     git --no-pager log --oneline --graph --all
>     git am "$@" ../a/0001-3-33.patch  # try it w/ and w/o -3
As per the documentation of -3:
        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.
format-patch records the identity of the blobs, and your fetch made
sure your 'b' repository has them.  So, it knows the base version of
the file has the following complete contents
    1
    2
    3
and the version from the patch has the following complete contents
    1
    2
    33
The version in HEAD you are using in the three-way merge has the
following contents
    11
    2
    3
Note that here we are not dealing with limited context, but with the
full copies of the files.  Now, doing a three-way merge, we see that
the line with '2' is common in all three versions.  The first line was
changed from 1->11 on the HEAD side (and left alone on the other
side), and the last line was changed from 3->33 on the other side
(while being left alone on the HEAD side).  The three-way merge of
these is simply
    11
    2
    33
So `git am -3` succeeds without conflicts for this case.
Junio C Hamano· Jul 5, 2022, 18:09 UTC · re: Elijah Newren · lore

Re: Why `git am -3` apply patches that don't normally apply?

Elijah Newren <newren@gmail.com> writes:
Show 6 quoted lines
> As per the documentation of -3:
>
>         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.

I suspect that we would need to update this part, as the order of "fall back" was swapped sometime in the recent past (and we have fixed a few bugs that were introduced as fallouts).

Your description on how -3 works when it does the three-way based on the concrete example was very clear and easy to understand, by the way.

Thanks for writing it up.
Yuri Kanivetsky· Jul 6, 2022, 00:19 UTC · re: Junio C Hamano · lore

Re: Why `git am -3` apply patches that don't normally apply?

Yep, thanks for the detailed explanations. It let me improve my answer on Stack Overflow:

https://stackoverflow.com/questions/25846189/git-am-error-patch-does-not-apply/72797965#72797965

Although... it'll unlikely receive a lot of views :( But whatever, it can't be helped.

Regards, Yuri

On Tue, Jul 5, 2022 at 9:09 PM Junio C Hamano <gitster@pobox.com> wrote:
Show 19 quoted lines
>
> Elijah Newren <newren@gmail.com> writes:
>
> > As per the documentation of -3:
> >
> >         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.
>
> I suspect that we would need to update this part, as the order of
> "fall back" was swapped sometime in the recent past (and we have
> fixed a few bugs that were introduced as fallouts).
>
> Your description on how -3 works when it does the three-way based on
> the concrete example was very clear and easy to understand, by the
> way.
>
> Thanks for writing it up.

← back to recent threads