public inbox for git@vger.kernel.org 
 help / color / mirror / Atom feed
From: Luca Balsanelli <lucabalsanelli@gmail•com>
To: Elijah Newren <newren@gmail•com>
Cc: git@vger•kernel.org
Subject: Re: Different behaviour for --find-renames between git diff and git merge?
Date: Tue, 16 Dec 2025 14:15:08 +0100	[thread overview]
Message-ID: <61700785-5421-4fa8-8277-c0837b09a737@gmail.com> (raw)
In-Reply-To: <CABPp-BH1qgQNHJzJZ05Ckru2PdYxRnWfQ3xVPrqGG5F56bX1aw@mail.gmail.com>

On 16/12/25 01:57, Elijah Newren wrote:
>> Even though the `git diff master~1 master` doesn't detect the rename
>> (the content changed too much compared to the empty file or one was
>> empty (although it says it defaults to include empty files as rename
>> source or destinarion)), the rename should be detected between the two
>> heads, even when merging. I tried to read at 'git/diffcore-rename.c' but
>> I'm not very good at C and it would require me a great effort to fully
>> understand it.
>>
>> So, why `git merge branch` is not detecting the rename and not resolving
>> the conflict automatically? Does it use a different diff machinery
>> compared to `git diff`?
> Merging never diffs the endpoints, and shouldn't either.  It basically
> does two diffs, each from the merge-base to the end-point in question.
>
> If you only diffed the endpoints, and one side renamed file A->B, how
> do you differentiate between A->B and B->A?  In other words, you may
> know there was a rename, but you can't tell what it was renamed from
> and which filename should be the final one.  You can only tell if you
> look at the merge-base and determine that the file started out named
> as A, and thus that B should be the final name.
>
> If you only diffed the endpoints, and one side renamed file A->B,
> while the other side renamed A->C, you'd be misled into thinking this
> was a normal rename (you'd only see e.g. B->C) and be unaware of the
> conflict, which is problematic.
>
> If you only diffed the endpoints, and one side renamed file A->B,
> while the other side renamed C->B, by diffing the endpoints you can't
> even tell there's a rename; you simply have a file named B that was
> totally rewritten.  But it gets subtly worse in special cases that
> might really confuse end users: if they modified A or C on the sides
> of history that didn't rename those files, those changes would not be
> propagated and combined with the ultimate B, and they'd be left to
> pick up the pieces and try to combine things.
>
> Further, it's just semantically wrong to diff the endpoints because of
> the underlying concept of a 3-way merge: If you were merging D & E and
> simply diffed D & E to do so, you won't know whether differing lines
> were added or removed by recent commits.  For example, you might
> notice an "import" or "include" statement that one side has that the
> other doesn't.  But did one side add that import statement?  Or did
> the other side remove it?  You can't tell by looking at the endpoints;
> you have to compare the endpoints to the merge-base to find out which
> things were added or removed.  So, fundamentally, a 3-way merge thinks
> in terms of diffing the merge-base to the endpoints, not diffing the
> endpoints.
>
>
> So, in summary, no, merge does not use a different diff machinery.
> You are just diffing the wrong commits to see what it sees.  Combine
> that with the fact that you have a funny special case where both sides
> drastically change the file in a way where the new versions happen to
> be similar to each other while not similar to the original, causes the
> behavior you are seeing.

Thank you. I understand.

Moreover, deepening the rename topic actually made me forget something 
about the merge topic. In fact, even if the rename was detected in some 
way or even if I didn't rename one side at all, the `git merge branch` 
would still be unable to resolve the conflict automatically, since both 
were modified in different ways, even if in similar ways. But similarity 
is not enough. This confounded me.

In the following example, I start from an empty file and I modify it on 
one side of the history and move (rename) it on the other side. The 
rename between `branch` and the merge base is detected. So, can you tell 
me why in the following case the rename is not detected during the merge?

    git switch -c master root

    touch aaa
    git add aaa
    git commit -m 'aaa'

    git switch -c branch
    echo -ne 'A\nB\nC\n' > aaa
    git add aaa
    git commit -m 'A\nB\nC\n > aaa'

    git switch master
    mkdir dir
    mv aaa dir/
    git add aaa dir/
    git commit -m 'aaa -> dir/'

    git merge --no-edit branch

Sorry if I'm pedant and thank you in advance.


  reply	other threads:[~2025-12-16 13:15 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-12-12 18:04 Different behaviour for --find-renames between git diff and git merge? Luca Balsanelli
2025-12-13  1:57 ` Elijah Newren
2025-12-15 14:02   ` Luca Balsanelli
2025-12-16  0:57     ` Elijah Newren
2025-12-16 13:15       ` Luca Balsanelli [this message]
2025-12-16 19:44         ` Elijah Newren

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=61700785-5421-4fa8-8277-c0837b09a737@gmail.com \
    --to=lucabalsanelli@gmail$(echo .)com \
    --cc=git@vger$(echo .)kernel.org \
    --cc=newren@gmail$(echo .)com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox