7

This was the initial snapshot of my git repository

enter image description here

On branch master, file m1 contains

L1

On branch dev, file m1 contains

L1
L2

If I try to merge dev from master, it results in a conflict.

$ git checkout master
Switched to branch 'master'

$ git merge dev
Auto-merging m1
CONFLICT (content): Merge conflict in m1
Automatic merge failed; fix conflicts and then commit the result.

$ git diff
diff --cc m1
index 078f94b,9f46047..0000000
--- a/m1
+++ b/m1
@@@ -1,1 -1,2 +1,5 @@@
  L1
++<<<<<<< HEAD
++=======
+ L2
++>>>>>>> dev

Though I didn't modify line 2 of m1 in master, how did it result in a conflict?

To verify actual contents of the file and to be sure if this is caused by white-spaces:

On branch master

git branch
  dev
* master

$ xxd m1
0000000: 4c31 0a                                  L1.

On branch dev

$ git checkout dev
Switched to branch 'dev'

$ xxd m1
0000000: 4c31 0a4c 320a                           L1.L2.

Here's the script I used to create this repo.

#!/bin/bash

mkdir git_demo
cd git_demo
git init

touch m1
git add .
git commit -m "Added file: m1"
# sleep is needed, otherwise a different repo is being created, probably because of *some* filesystem issue!
sleep 1

git branch dev
echo L1 >> m1
git add .
git commit -m "Added line L1 to m1"
# sleep is needed, otherwise a different repo is being created, probably because of *some* filesystem issue!
sleep 1

git checkout dev
echo L1 >> m1
git add .
git commit -m "Added line L1 to m1"
# sleep is needed, otherwise a different repo is being created, probably because of *some* filesystem issue!
sleep 1

echo L2 >> m1
git add .
git commit -m "Added line L2 to m1"
# sleep is needed, otherwise a different repo is being created, probably because of *some* filesystem issue!

gitg --all
git checkout master
git merge dev
sherlock
  • 2,397
  • 3
  • 27
  • 44

3 Answers3

3

The answer is that there is a conflict because there isn't any merge-base commit for the 2 branches.

Here is how to generate the problem in fewer steps.
Create orphan branch (Orphan branch is branch without any history)

enter image description here

You can see here that they are not sharing the same tree

[enter image description here]

enter image description here

enter image description here

CodeWizard
  • 128,036
  • 21
  • 144
  • 167
1

Because the common ancestor is empty.

In master you've added one line to an empty file. In the dev branch you've added two lines to an empty file.

It doesn't matter that one of the lines is in common, you have to choose which side you want to take; the side with one line or the side with two.

Edward Thomson
  • 74,857
  • 14
  • 158
  • 187
  • From this discussion, I can see that git's `merge` behavior is still largely misunderstood, even among veteran users. So many conflicting theories! However, I still can't understand how 3-way merge improves the situation. I thought git should detect no-change between `master`-`ancestor` and `L2` between `dev`-`ancestor`. The later should dominate the earlier. Shouldn't a conflict occur only when there's a change on the same line? – sherlock Dec 22 '15 at 16:05
  • But there is a change between master and the ancestor. The ancestor is an empty file. Had the ancestor been identical to master then you would be correct. – Edward Thomson Dec 22 '15 at 16:07
  • Doesn't git 3-way merge correctly handle when (a) ancestor differs from both the versions to be merged and (b) no change on the same line of the file? – sherlock Dec 22 '15 at 16:14
  • Yes, but again, that's not what you're doing here. In your example, you *are* changing the same lines in each (really, it's not about *lines* but *regions*.) – Edward Thomson Dec 22 '15 at 22:36
  • How is the region boundary determined? A single line, in its own right, could be dealt as a *region*, too. From what you told, it clearly does not seem to be the case. – sherlock Dec 23 '15 at 02:13
  • @Holmes.Sherlock: The boundary is not the line. It's the history. Hence, regardless of weather both lines are identical or not, both lines differ from the original line in history (the original line is empty). There is ONE exception to this (but even then I'm not sure). It is if both files are completely identical. Then git detects that they are identical and no merge is necessary. – slebetman Dec 23 '15 at 08:18
  • @Holmes.Sherlock: FWIW, the correct workflow to avoid merge conflict in this case is: Add line L1 in dev. Merge dev to master. Add line L2 to dev. If you add line L1 to master without using merge you will get merge conflict. – slebetman Dec 23 '15 at 08:21
  • @slebetman Line 2 is empty in `master` as well as in common `ancestor`. If git's merge algorithm is *line*-aware, then it'd reasonably be expected to give precedence to the only change in `dev`. – sherlock Dec 23 '15 at 10:08
  • @Holmes.Sherlock: That's exactly the problem. Git's merge algorithm is not line-aware. It's history aware. – slebetman Dec 23 '15 at 11:20
  • @Holmes.Sherlock: You're still obsessed with L2. That's not what's causing the conflict. It's L1 that's causing the conflict. Even though L1 is identical it comes for different sources (histories). – slebetman Dec 23 '15 at 11:24
  • But, the diff is --- a/m1 +++ b/m1 @@@ -1,1 -1,2 +1,5 @@@ L1 ++<<<<<<< HEAD ++======= + L2 ++>>>>>>> dev Doesn't it mean that the conflict is between `L2` and `empty` line? – sherlock Dec 23 '15 at 12:13
0

This is easy to recreate:

% git init                                                                                                                                                                                                        [8:33:13]
Initialized empty Git repository in /home/martin/tmp/gitte/.git/
% touch m1                                                                                                                                                                                                        [8:33:16]
% git add m1                                                                                                                                                                                                      [8:33:40]
% git commit -m "Added file: m1"                                                                                                                                                                                  [8:33:48]
[master (root-commit) 72a9740] Added file: m1
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 m1
% git checkout -b dev                                                                                                                                                                                           [8:34:05]
Switched to a new branch 'dev'
% echo L1 >> m1                                                                                                                                                                                                   [8:34:08]
% git commit -am "Added line L1 to file m1"                                                                                                                                                                     [8:34:29]
[dev b16538c] Added line L1 to file m1
 1 file changed, 1 insertion(+)
% git checkout master                                                                                                                                                                                             [8:34:33]
Switched to branch 'master'
% echo L1 >> m1                                                                                                                                                                                                   [8:34:38]
% git commit -am "Added line L1 to file m1"                                                                                                                                                                       [8:34:46]
[master 7b952c8] Added line L1 to file m1
 1 file changed, 1 insertion(+)                                                                                                                                                    [8:35:59]
HEAD is now at 7b952c8 Added line L1 to file m1
% gitk                                                                                                                                                                                                            [8:36:04]
% echo L2 >> m1                                                                                                                                                                                                                                                                                                                                                                                                                [8:36:28]
% git commit -am "Added line L2 to file m1"                                                                                                                                                                       [8:36:28]
[master f336d77] Added line L2 to file m1
 1 file changed, 1 insertion(+)
% git merge dev  # merge conflict!

The issue happens because line 2 in both files is different. In your master branch, line 2 is simply an "EOF", while in the dev branch line 2 is "L2".

Martin Konecny
  • 57,827
  • 19
  • 139
  • 159
  • Are you sure? If you see the output from `xxd` on `m1` for both the branches, `0a` appears to be newline character present. There's no separate EOF present in either of the versions of the file. – sherlock Dec 22 '15 at 13:46
  • Please check `xxd` output in my original post. – sherlock Dec 22 '15 at 13:47
  • technically there is no EOF character, so I've clarified my answer. However, you can clearly see the merge conflict is on line #2. Where the file was once considered EOF, is now L2, and Git sees this as a conflict. – Martin Konecny Dec 22 '15 at 13:53
  • 1
    In that case, when a line would have been added or deleted from a file, git would always show a conflict at the end. Isn't it? And the `0a` character (3rd char.) in the `master` should get matched with `0a` character (3rd char.) in the `dev` branch. – sherlock Dec 22 '15 at 14:00
  • It appears to happen only when you change the last line of the file in both branches. In one branch the EOF (not a character) was introduced on line 2, in the other branch L2 was introduced on line 2. – Martin Konecny Dec 22 '15 at 14:06
  • On `master`: `4c31 0a`. On `dev`: `4c31 0a4c 320a`. If you say that `master` contains `0a` at line 2, the `dev` also contains the *exact* same byte at line 2. I wonder if `0a` is given different treatment as LF and EoF characters. – sherlock Dec 22 '15 at 14:25