Subject: Re: Different behaviour for --find-renames between git diff and git merge?

From: Luca Balsanelli <[email-masked]>
Date: Mon, 15 Dec 2025 15:02:09 +0100

On 13/12/25 02:57, Elijah Newren wrote:
> On Fri, Dec 12, 2025 at 10:06 AM Luca Balsanelli
> <[email-masked]> wrote:
>> Hi,
>>
>> I'm scratching my head to understand why on the following case `git
>> diff` and `git merge` give a different interpretation about a rename.
> I don't see any difference...

Sorry, my email was not clear. There is still something that is not 
convincing me though. I will reformulate my question at the end, after I 
reply 'inline' to all the (true) considerations. I consider myself 
decently educated on git, but probably I still miss some understanding 
of the merge procedure.

>> git switch master
>> touch aaa
>> git add aaa
>> git commit -m 'aaa'
>>
>> git switch -c branch
>> echo -en 'A\nB\nC\n' > aaa
>> git add aaa
>> git commit -m 'A\nB\nC\n > aaa'
>>
>> git switch master
>> echo -en 'A\nB\n' > aaa
>> mkdir dir
>> mv aaa dir/
>> git add aaa dir/
>> git commit -m 'A\nB\n > aaa -> dir/'
>>
>> The `|merge.renames` config variable is true. Changing `git diff
>> --find-renames=50%` (the default) or `git merge -s ort -X
>> find-renames=50%` ||to something lower does not change the following.
>> |
>>
>> `git diff` prints
> Actually, it doesn't; more on that below...

I forgot to specify that I was intending to diff the two heads, that is 
`master` and `branch`. So it was

git switch master

git diff branch

>> diff --git a/aaa b/dir/aaa
>> similarity index 71%
> Did you not follow your own recipe? Maybe you inserted an extra space
> or left off the 'n' in 'echo -en' when you ran this? The number
> should have been 66%.

I don't know what I did but there were additional newlines. So, yes, the 
similarity index is 66% (which is still above to the default 50% to 
detect renames for both `git diff` and `git merge`).

>> rename from aaa
>> rename to dir/aaa
>> index bbd2b90..986ad36 100644
>> --- a/aaa
>> +++ b/dir/aaa
>> @@ -1,4 +1,3 @@
>> A
>> B
>> -C
>>
>> that is the similarity index is 71% and it detects the rename.
> At this point, if you actually run `git diff` you see the following:
>
> $ git diff
> $
>
> i.e. nothing. I suspect you gave `git diff` additional arguments but
> didn't tell us. Let's look at a few options:
>
> $ git diff master~1 master
> diff --git a/aaa b/aaa
> deleted file mode 100644
> index e69de29..0000000
> diff --git a/dir/aaa b/dir/aaa
> new file mode 100644
> index 0000000..35d242b
> --- /dev/null
> +++ b/dir/aaa
> @@ -0,0 +1,2 @@
> +A
> +B
> $
>
> So, on master, aaa was deleted, and dir/aaa was added.
>
> $ git diff master~1 branch
> diff --git a/aaa b/aaa
> index e69de29..b1e6722 100644
> --- a/aaa
> +++ b/aaa
> @@ -0,0 +1,3 @@
> +A
> +B
> +C
> $
>
> On branch, aaa was modified.
>
> $ git diff branch master
> diff --git a/aaa b/dir/aaa
> similarity index 66%
> rename from aaa
> rename to dir/aaa
> index b1e6722..35d242b 100644
> --- a/aaa
> +++ b/dir/aaa
> @@ -1,3 +1,2 @@
> A
> B
> -C
> $
>
> So, only if you diff the endpoints of the two branches do you see a
> rename; if you look from the merge base to either branch, there isn't
> one.

Yes, the above is all true. As I said above, I forgot to specify the 
argument: `git switch master; git diff branch`.

>> `git merge branch`, instead, gives
>>
>> CONFLICT (modify/delete): aaa deleted in HEAD and modified in
>> branch. Version branch of aaa left in tree.
>> Automatic merge failed; fix conflicts and then commit the result
> Yes, this exactly matches what diff showed above -- on HEAD (master),
> 'aaa' was deleted, and on branch, 'aaa' was modified.
>
>> Why it is that? I always supposed that the rename detection was the same
>> for `git diff`, `git merge`. Reading the documentation I do not find any
>> hint why `git diff` and `git merge` are behaving differently.
> Hope that helps...

I would expect that `git merge branch` would detect a rename and the 
conflict resolved automatically. The 'ort' strategy (the default one), 
"can detect and handle merges involving renames." and the default 
similarity threshold is the same for `git diff` and `git merge`. I 
understand that the merge procedure involves finding a merge base, but 
still the rename should be detected between the two heads.

I was reading commit `90d43b07687fdc51d1f2fc14948df538dc45584b` of the 
git source code (which I found using `git log --grep '--rename-empty'`). 
It says (among other things)

This patch lets callers specify whether or not they interested in
using empty files as rename sources and destinations. The default is
"yes", keeping the original behavior. It works by detecting the
empty-blob sha1 for rename sources and destinations.

It is related, but I don't think it is relevant to this specific case. 
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`?