In version control in commercial development, there are often multiple branches of a project. In my mercurial-managed projects I often have a stable and a trunk version, which descend from some common parent, like this:
V8 (the parent of both V8-stable and V9, revisions 1..100)
|
+--- V8_stable (bug fix branch, revisions 101 to 500 )
|
+--- V9_trunk (trunk, revisions 501 to 1000 )
All the above versions of the project have a file, we’ll call it helloworldapp.py. These samples are in Python because it is the most pseudo-code-like language I know, but the question is not a Python question. Now, In mercurial you can branch using clones, or you can use Mercurial’s built in branching capabilities. In this example, I’m branching using clones of entire repositories.
Here is the V8 helloworldapp.py which is the common parent rev:
# helloworldapp.py rev 100 (v8)
def func1(a,b,c,d,e):
print "stuff"
def func2(a,b,c,d,e):
func1(a,b,c,d,e)
def func3(a,b,c,d,e):
func2(a,b,c,d,e)
def func4(a,b,c,d,e):
func3(a,b,c,d,e)
def func5(a,b,c,d,e):
func4(a,b,c,d,e)
def func6(a,b,c,d,e):
func5(a,b,c,d,e)
def func7(a,b,c,d,e):
func6(a,b,c,d,e)
func7(1,2,3,4,5)
Here is revision 500, part of V8-stable branch:
# helloworldapp.py rev 500 (v8_stable)
def func1(a,b,c,d,e):
print "stuff"
def morestuff():
print "morestuff"
morestuff()
func1(1,2,3,4,5)
Here is revision 1000 part of the V9 trunk branch:
# helloworldapp.py rev 1000 (v9_trunk)
def func1(a,b,c,d,e):
print "stuff"
def func4(a,b,c,d,e):
morestuff()
morestuff()
morestuff()
func1(a,b,c,d,e)
def morestuff():
print "hello"
func4(1,2,3,4,5)
The above files are very short examples of a condition that occurs when branches get out of sync, in file-oriented version control, and which causes me difficulty, when I have to manage the “stable to trunk” merge-ups (via pull and then merge).
Here’s the situation that you end up with when merging. Note that in the real world, the problems are usually magnified by about 100x to 1000x for me. I often end up with 5000+ lines of conflicts that Hg merge cannot solve, in hundreds of files, which require me to merge each one manually in KDiff3.
Aside from not letting branches ever get out of sync ever, what can a Mercurial user do to make merging in such situations easier? In the situation I have illustrated above, “working at line 6 on branch A of a file” is the same as “working at line 6 on branch B of a file”, and the line-by-line merges create merge conflicts, where two users were actually working (in their own minds) on different functions in a file. By the act of deleting a no-longer-used-function from one file, because it’s no longer needed, you now almost ensure more chance of merge conflicts. You almost have to institute rules like “no moving or deleting lines in upstream stable branches, ever”. I know that if you were using a Smalltalk image-based version control system, the accident of “what line is it on” would no longer enter into it. If the diff-tool used by Mercurial understood Python, it could do a better job at Python code, at the expense of making it probably not work well at all on C# code. So this is the state of the art in file-based programming and DVCS tools. Fine.
So, in the merge situation pictured here,

(click here for larger version)
*
What I see is merge conflicts where I wish it could figure out that I either want all or none of the “morestuff” function:

What I wish I got (automatically):

I have posted sample repositories on bitbucket as:
helloworldapp_v9
helloworldapp_v8_stable
To try this “most simple example I could construct of what happens when you are trying to how to resolve merge conflicts”, clone one of the above, and then pull from the other, and then merge. In the end my question is: How does a professional user of Mercurial deal with this kind of situation? Understand that this example is contrived but shows some of the complexity that users of DVCS tools encounter, but in the real world, these conflicts are often far more complex than the simple case I am showing here. I have learned to deal with most small and simple conflicts fairly quickly using TortoiseHG and KDIFF3, but I find that merge-hell is much less of a problem than I remember it being with Subversion or Perforce, but still, I don’t think that either (a) the programmer (me) or (b) my tools have done all that can be done to help manage and understand this complexity. I have a feeling that alternative solutions and tools and working strategies for using Mercurial exist that I have not yet tried or learned about. Maybe MQ and Rebase?
Actually, in my experience, the real world doesn’t usually look like that for two reasons:
We merge much more frequently (from v8 to v9 in your example), so that Mercurial knows incrementally how things are to fit together between the two branches. Five hundred changesets is a lot of changes to piece together in one step (and I’ve done it that way before!).
When merging our branches, our changes don’t collide as brutally as your example. With less collision and more context surrounding each change, the merge tools do a much better job at putting them together.
We do have collisions and need to figure out how to put them together manually, which can be a big headache if we put it off. Once we do that, though, the common ancestor is now that merge and we don’t need to revisit that collision again. By merging more frequently you decrease the number of changeset since the common ancestor.
I’d be interested in re-running your example with periodic merges from V8_stable into V9_trunk. I’d also be interested if you added some text between function defs to give hg some more context to separate out the changes.
Having attempted to try the more frequent merges with your example, it didn’t work very well. If your versions diverge that much (only 15%-25% the same), they become fundamentally different codebases and merging doesn’t make sense anymore. At that point, I try to factor out the pieces into separate files to allow both versions (and my sanity!) to co-exist in peace.