git/list[1] front-page[2] threads[3] people[4] search[5] about
 

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

From
Elijah Newren <newren@gmail.com>
Date
Jul 4, 2022, 19:23 UTC
Message-ID
<CABPp-BEkEKQZR=1duxSS4AVv9u5tnPk5CaLCM_fQWPbAqfSMxw@mail.gmail.com>
In-Reply-To
<CAMhVC3Z92hBsK0FbE4E_LV1CSxjR1zmu+MO8=iwR02BKqnwgUA@mail.gmail.com>

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.
Previous: Yuri KanivetskyNext: Junio C Hamano
Message 2 of 4 in “Why `git am -3` apply patches that don't normally apply?”
  1. Yuri KanivetskyJul 3, 2022
  2. Elijah NewrenJul 4, 2022
  3. Junio C HamanoJul 5, 2022
  4. Yuri KanivetskyJul 6, 2022

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.