Git – Why doesn’t git merge adjacent lines without conflict

gitmerging

I recently learned that when merging two branches in git, if there are changes on two adjacent lines git declares this a conflict. For example, if file test.txt has this content:

Line 1: A
Line 2: B
Line 3: C
Line 4: D

and in branch master we change this to

Line 1: A
Line 2: B1
Line 3: C
Line 4: D

while in branch testing we change this to

Line 1: A
Line 2: B
Line 3: C1
Line 4: D

and then attempt to merge testing into master, git declares a merge conflict. My naive expectation was that the merge would happen without conflict and yield this:

Line 1: A
Line 2: B1
Line 3: C1
Line 4: D

I am sure there is a good reason why git doesn't merge this way. Can someone explain this reason?

Best Answer

Is this git-only behavior?

After discussion with a colleague, I just tried, and SVN handles it without problem: you get the 2 lines modified.

The merge capabilities of several VCS are tested here for bazaar, darcs, git and mercurial: https://github.com/mndrix/merge-this

It seems only darcs successfully merge the "adjacent lines" case.

Applying adjacent changes to files is not a difficult problem. I really think this behavior has been chosen on-purpose.

Why would someone decide that modifying adjacent lines produces a conflict?

I would think this is to force you to look at it.

int max = MAX_ITEMS;
for(unsigned int i = 0; i < max; i++)
    do_stuff(i);

Modif number 1, on master:

int max = MAX_ITEMS/2; // Do stuff only on the first half
for(unsigned int i = 0; i < max; i++)
    do_stuff(i);

Modif number 2, merged from a branch:

int max = MAX_ITEMS;
for(unsigned int i = 0; i < max/2; i++) // max/2: only on 1st half
    do_stuff(i);

After merge, you don't want that:

int max = MAX_ITEMS/2; // Do stuff only on the first half
for(unsigned int i = 0; i < max/2; i++) // max/2: only on 1st half
    do_stuff(i);

Seeing this behavior as a feature

You can turn the git merging behavior to an advantage. When you need to keep 2 lines consistent but you can't detect it (at compilation time, early in your tests or else), you can try to join them.

Rewrite this...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    // Need to do something else
    do_something_else(r);

...to this:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    do_something_else(r); // Need to do something else

So when you merge Modif 1...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i)/2; // we need only the half
    do_something_else(r); // Need to do something else

... with Modif 2...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    if(r < 0) // do_stuff can return an error
        handle_error(r);
    do_something_else(r/2); // Need to do something else

..., git will produce a conflict, and you will force you to look at it.

Related Topic