I don’t understand why git offers conflict how to rename / delete conflict?
Git discovered a rename. More precisely, he discovered one, but not the other.
I used your example script and got the same result:
CONFLICT (rename/delete): f/a deleted in hotfix and renamed in HEAD. Version HEAD of f/a left in tree. Automatic merge failed; fix conflicts and then commit the result.
There are two keys here: how git merge merges and how git diff works. Start with the first one.
How merging works (like a verb)
The purpose of the merger is to combine two sets of changes on two different lines of development (usually this is done by two different people, but in our case, made by one person "in two different hats", one for a while). These changes must necessarily begin with some common starting point, which git calls the merge base.
To perform this merge, git must find the merge base. For regular merges like this, a merge base is a commit that is shared between the HEAD (current commit) and the target commit. In my specific case, a target called hotfix allows commit hash b45a155... :
$ git rev-parse hotfix b45a15547101d836d84dbdf4758d71dc91c93353
and HEAD is 2ca7d2d... :
$ git rev-parse HEAD 2ca7d2d15d4d537edb828a7f3bfff3a2182630ec
The merging base of these two commits is the initial commit d763d32 add a on master :
$ git merge-base --all HEAD hotfix d763d32af0cafdb0378b96b25e56fd70d63213d1 $ git log --graph --decorate --oneline --all * b45a155 (hotfix) move a to f on hotfix with different content | * 2ca7d2d (HEAD -> master) move a to f on master |/ * d763d32 add a on master
We really don't need all these hashes, but sometimes it's nice to see them in a specific form. The fact is that we have two different sets of changes: “what we did”, going from d763d32 to 2ca7d2d , and “what they did”, going from d763d32 to b45a155 .
We can find the first of these things by running git diff :
$ git diff d763d32 2ca7d2d
This is "what we did." I will show it in a moment.
Next, we (or Git) can find the second of these things by running git diff again:
$ git diff d763d32 b45a155
How git diff makes differences
This explanation becomes long and confusing in great detail when it matters, and ultimately it really matters. Let me pass it on to fooobar.com/questions/1259949 / .... So git will try to determine the renames. Whether he can detect them depends on many details.
In our particular case, however, what happens is that git detects renaming when it goes from merging to the tip of the wizard:
$ git diff hotfix...master diff
The three-dot syntax describes git diff in order to find the base for merging the two specified commits (tip hotfix and tip master ), then diff, which combines the base with the second commit, i.e. tip of the wizard. He discovered a rename: the source file a was 100% identical to the new f/a file. Therefore, we have a renaming.
The second diff, though ... ah, there is a problem:
$ git diff master...hotfix diff
The old content a in the merge base is too different from the new content f/a in the accumulation declaration. git not only did not find the rename, it will never find the rename: the file is too different. This is not like the original in the least.
Rename detection usually works better than this.
In practice, when files are renamed, they usually retain a lot or even all of the original contents, as happened in the change of merge with the captain base. However, if they do not save "enough", git will not detect the renaming.
Fortunately, git has been around for a long time, since I wrote this answer back in January 2012. It still applies today - you can use -X rename-threshold=number to adjust the rename detection threshold level during merge - and almost everyone has git newer than 1.7.4. However, its flaws are still applicable. You can also read this other answer that I wrote in April 2016.
If you have many files and need automatic detection of renaming, you may need to prove yourself. If you have only one file, you can simply merge the file manually by doing your own “rename detection” using git merge-file to create a merge result. Just extract the three revisions (base, HEAD and others) into temporary files and use git merge-file to combine these three to get the desired result. Replace git with a slightly lame version with the correct one, and git add and you will go well.