1

This question follows this topic. I am trying to understand what merge do to avoid unwanted mistakes during it.

I have two branches:master,b. I saw an strange result of doing git merge. You can see output of git log before merging, here:

* 148c970 (HEAD, master) rename 1 in master to 1_master
| * ad18d9b (b) rename 1 in b to 1_b
|/  
* 15cd89b add 1 in master

So I had a file with name 1 in master and b. Then rename it in master to 1_master and to 1_b in b. Then I did git merge:

git merge b

I guess it causes a conflict, because merge base must be 15cd89b and both branches introduce changes on 1, which they don't match.

But merging did not cause any conflict and after merging I saw both 1_b and 1_master in working directory.

Note:

I think both torek and twalberg in linked answers forecast conflict in above situation.

Community
  • 1
  • 1
hasanghaforian
  • 13,858
  • 11
  • 76
  • 167
  • How did you rename the file? Using git mv or just renaming it in the file system and add+deleting the file in git? – Pod Mar 13 '16 at 12:50
  • @Pod That won't make any difference. [Here](https://git.wiki.kernel.org/index.php/GitFaq#Why_does_Git_not_.22track.22_renames.3F) and [here](http://stackoverflow.com/q/1094269/5830574) are very good explanations. – PerlDuck Mar 13 '16 at 14:12

2 Answers2

2

If I attempt to reproduce your issue I get a rename/rename conflict, as I would expect. First, I created a repository and some files (with unique contents):

$ mkdir rename-exp
$ cd rename-exp/
$ git init
Initialized empty Git repository in /home/torek/tmp/rename-exp/.git/
$ cat > 1
This is file "1" in its initial state.
The file is being used
for a test of what happens with
a rename vs rename conflict
in Git.
The file needs some contents
so that git can detect the
rename when we actually
do the rename.
$ cat > 2
This is file "2".         
We give it some contents
so that it has a unique SHA-1.
$ git add .
$ git commit -m initial
[master (root-commit) a4b6f52] initial
 2 files changed, 12 insertions(+)
 create mode 100644 1
 create mode 100644 2

Next, I create branch b and rename. Note that it will not matter whether I do the rename with git mv or with mv followed by git rm --cached and git add (but git mv is easier, so I use it). It is, however, important that git be able to detect the rename. This requires that either the files match 100% exactly (the easy case) or that they match "at least 50%" (this threshold can be adjusted, but 50% is the default) when diff-ed and that the old name (1) is no longer present in both trees at the time of the git merge.

$ git checkout -b b
Switched to a new branch 'b'
$ git mv 1 1_b
$ git commit -m 'rename 1 to 1_b in branch b'
[b cc104b1] rename 1 to 1_b in branch b
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename 1 => 1_b (100%)

Then I do the same rename, with a different name-target, in master:

$ git checkout master
Switched to branch 'master'
$ git mv 1 1_master
$ git commit -m 'rename 1 to 1_master in master'
[master b891757] rename 1 to 1_master in master
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename 1 => 1_master (100%)

Finally, I run git merge:

$ git merge b
CONFLICT (rename/rename): Rename "1"->"1_master" in branch "HEAD" rename "1"->"1_b" in "b"
Automatic merge failed; fix conflicts and then commit the result.
$ 

You'll need to show your steps (and perhaps your git version as well) in order for me to see why your merge thought each change was delete-and-create (with non-conflicting creates) rather than rename.

Other variables that affect this:

  • any argument to -X rename-threshold during the merge (this is how you tune the 50% default);
  • the value from git config --get merge.renameLimit
  • the value from git config --get diff.renameLimit, if there is no setting for merge.renameLimit.
torek
  • 448,244
  • 59
  • 642
  • 775
  • I guess the problem was in about file `1` which was empty. So git did not detect it is renamed when tried to compute changes. Do you think my guess is right? – hasanghaforian Mar 15 '16 at 07:58
  • Most likely, yes. Two empty files would compare equal, but if you have additional empty files, git would not know to decide that file `1` matches `1_b` and not, say, `1_c`, etc. Or it may just not bother with empty files since they're not very interesting. – torek Mar 15 '16 at 17:51
1

I think that is because Git doesn't really know the concept of renaming a file. See the Git FAQ.
For Git it is just add 1_master followed by remove 1. What you did is

on branch master:

  • add file 1_master
  • remove file 1

and on branch b:

  • add file 1_b
  • remove file 1

So in summary (i.e. merge) this gives:

  • add file 1_master
  • add file 1_b
  • remove file 1

No conflict. Both branches apply the same operation to file 1: removing it.

Things would change, if you'd edited file 1 on one of your branches. Then Git wouldn't know whether to apply the edit or the deletion.

PerlDuck
  • 5,610
  • 3
  • 20
  • 39