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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sun, Jul 3, 2022 at 6:09 AM Yuri Kanivetsky
<yuri.kanivetsky@xxxxxxxxx> wrote:
>
> 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...

> 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.

>     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.



[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux