views:

187

answers:

1

I'm writing some sort of new adminstration dashboard for our cms (which runs on asp.net webforms). Some of our older servers can only handle .NET 2.0 for various reasons so I'd have to rewrite my code which uses lambda expressions etc. for that.

I wonder how you would use a dvcs like mercurial to develop those two versions concurrently.

My current codebase and mercurial repository is targeted at .NET 3.5. I'm relatively new to mercurial and I guess I'd have to branch the codebase?

Any best practices or tutorials?

+11  A: 

Yes, you can use Mercurial for this. Here is how it would work.

Let's say that your current clone is called new-dot-net since it support the new .Net version. You make a clone of it and call it old-dot-net or something like that. The two clones are now identical and both target .Net 3.5.

Now carefully make small changes in old-dot-net in order to make it .Net 2.0 compatible. When you make the changes the two clones will start to diverge:

new-dot-net: ... [a] --- [b]

old-dot-net: ... [a] --- [b] --- [c] --- [d]

Here you made [c] and [d] changesets to add the .Net 2.0 compatibility. Notice how the old-dot-net clone contains more changesets than new-dot-net since it has the backwards compatibility changes that you dont want to see in new-dot-net. As you continue working, it is important to think of this: net-dot-net will contain a subset of the changesets in old-dot-net. The changes flow from new-dot-net to old-dot-net, but never in the opposite direction.

Let's say you make a new change in new-dot-net. You make the change in new-dot-net and the situation now looks like this:

new-dot-net: ... [a] --- [b] --- [x]

old-dot-net: ... [a] --- [b] --- [c] --- [d]

You now want to back-port the change to old-dot-net as well, you change to old-dot-net and pull from net-dot-net:

% cd old-dot-net
% hg pull ../new-dot-net

This will create a new head in old-dot-net:

                             [x]
                            /
old-dot-net: ... [a] --- [b] --- [c] --- [d]

since the [x] changeset has [b] as it's parent changeset. You now have multiple heads and have to merge to reduce the number of heads. By merging you create a new changeset which is your way of saying "this is how [x] and [d] should be combined". If the [x] changeset only touches code which is not also touched in [c] and [d], then the merge should just work. Otherwise you'll be presented with a merge tool and have to resolve the conflict. You commit the merge as chageset [e]:

                             [x] --------------.
                            /                   \
old-dot-net: ... [a] --- [b] --- [c] --- [d] --- [e]

And you're done -- you have now incorporated the [x] change into your .Net 2.0 compatible code.

You repeat this every time there has been a change in new-dot-net. Let's say that more features are added:

new-dot-net: ... [a] --- [b] --- [x] --- [y] --- [z]

After pulling them into old-dot-net you get

                             [x] --------------.---- [y] --- [z]
                            /                   \
old-dot-net: ... [a] --- [b] --- [c] --- [d] --- [e]

And you now merge [e] and [z]:

                             [x] --------------.---- [y] --- [z]
                            /                   \               \
old-dot-net: ... [a] --- [b] --- [c] --- [d] --- [e] ----------- [f]

The important parts to remember are these:

  • make any new features in new-dot-net.
  • pull changes into old-dot-net
  • never push from old-dot-net to new-dot-net.

Should you at some point find that a change in new-dot-net is not needed in old-dot-net, then you still need to pull it in and merge it. But you will then do a dummy merge. If the heads are [w] and [g], and you want keep [g], then do

% HGMERGE=true hg merge -y
% hg revert --all --rev g
% hg commit -m 'Dummy merge with y.'

The trick is to do the merge without caring about the results, then revert all changes, and commit the unchanged working copy as the merge. That way you tell the world that "the combination of [w] and [g] is [g]", i.e., you throw away the changes in [w]. New changes made in new-dot-net after [w] can then be merged like normal.

Martin Geisler
so you wouldnt use branches for this? why not?
kitsune
It makes no difference -- there is a 1-1 relationship between (named) branches and clones in Mercurial. So for the purpose of back-porting changes it doesn't make things easier or harder. With clones it might be easier to compare files from two clones (by opening them both in your editor) and the separation makes explanations like the above easier, IMHO.
Martin Geisler
thanks a lot for this
kitsune
This is a great explanation of the dummy merge technique. I'm going to bookmark this. Thanks again!