Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mercurial merge awesomeness - what am I missing?

I've been using Mercurial for a while, and there's one "fact" that is given many times.

In fact, it hit me while watching a video made by Fogcreek yesterday, this video: Fog Creek Kiln: Unlock the power of DVCS for your company that there seems to be something that doesn't work for me here.

At around 1:39 in that video and onwards it makes a point of saying that while other version control systems tracks revisions (ie. snapshots), DVCS' like Mercurial track changesets (ie. what happened between the snapshots.)

This gives them an edge in merging scenarios, and then it shows an example. If you move a function in one branch, and change the same function in another branch, Mercurial is able to merge that.

And I've seen this mentioned elsewhere, though I can't find any direct links now.

This doesn't seem to work for me.


Edit: This is a problem with the default "beyondcompare3" merge tool configuration for TortoiseHg. I added the configuration below to my Mercurial.ini file, and now it works as expected. Sure, it'll punt to the GUI tool if it can't automerge, but now the merge described in this question here runs without any prompts and just does the right thing out of the box

[ui]
merge = bc3

[merge-tools]
bc3.executable = C:\Program Files (x86)\Beyond Compare 3\bcomp.exe
bc3.args = $local $other $base $output /automerge /reviewconflicts /closescript
bc3.priority = 1
bc3.premerge = True
bc3.gui = True

To test this, I committed this file to a repository:

void Main()
{
    Function1();
    Function2();
}

public void Function1()
{
    Debug.WriteLine("Function 1");
    for (int index = 0; index < 10; index++)
        Debug.WriteLine("f1: " + index);
}

public void Function2()
{
    Debug.WriteLine("Function 1");
}

Then in two different parallel changesets branching out from this one, I did the following two changes:

  1. I moved the Function1 function to the bottom of the file
  2. I changed the message inside Function1

I then tried to merge, and Mercurial gives me a merge conflict window, trying to figure out what I did.

Basically, it tries to change the text in Function2, which is now in the position that Function1 was before it was moved.

This was not supposed to happen!


Here's the source files for reproducing my example:

Batch file for producing repository:

@echo off

setlocal

if exist repo rd /s /q repo
hg init repo
cd repo

copy ..\example1.linq example.linq
hg commit -m "initial commit" --addremove --user "Bob" --date "2010-01-01 18:00:00"

copy ..\example2.linq example.linq
hg commit -m "moved function" --user "Bob" --date "2010-01-01 19:00:00"

hg update 0
copy ..\example3.linq example.linq
hg commit -m "moved function" --user "Alice" --date "2010-01-01 20:00:00"

The 3 versions of the file, example1.linq, example2.linq and example3.linq:

Example1.linq:

<Query Kind="Program" />

void Main()
{
    Function1();
    Function2();
}

public void Function1()
{
    Debug.WriteLine("Function 1");
    for (int index = 0; index < 10; index++)
        Debug.WriteLine("f1: " + index);
}

public void Function2()
{
    Debug.WriteLine("Function 1");
}

Example2.linq:

<Query Kind="Program" />

void Main()
{
    Function1();
    Function2();
}

public void Function2()
{
    Debug.WriteLine("Function 1");
}

public void Function1()
{
    Debug.WriteLine("Function 1");
    for (int index = 0; index < 10; index++)
        Debug.WriteLine("f1: " + index);
}

Example3.linq:

<Query Kind="Program" />

void Main()
{
    Function1();
    Function2();
}

public void Function1()
{
    Debug.WriteLine("Function 1b");
    for (int index = 0; index < 10; index++)
        Debug.WriteLine("f1: " + index);
}

public void Function2()
{
    Debug.WriteLine("Function 1");
}
like image 305
Lasse V. Karlsen Avatar asked Nov 17 '10 10:11

Lasse V. Karlsen


1 Answers

Well, you're currently hitting one of the limitation of, basically, ANY current VCS (DVCS or not, it does not matter).

The thing is that VCS are currently language agnostic, the base of their merge algorithm is a textual diff. It means that they are looking for what change and what is the related context.
The important part here is the context. It is nothing more that some lines before and after the changes you made.

This means that they are really bad at dealing with code re-organisation inside of a same file, because you're basically screwing all the context they can rely on.
Typically, in your example, by switching the two functions, you not only completely inverted the context between the two changesets, but worse, by having no extra lines after the latest function, you reduced implicitly the context of the latest change, diminishing the chances that a merge algorithm got able to figure out what you really did.

I currently only know about one diff tool, from msft, for XML, that is trying to deal with the semantic of your change and not just its textual representation.
I also know that the guys of PlasticSCM are trying to implement such feature for some mainstream languages, but it is really a place where there is room for improvement.

like image 181
gizmo Avatar answered Oct 15 '22 07:10

gizmo