Team Foundation Server has never been friendly when it comes to the complicated love triangles that inevitably rise from wanting to merge between three different branches. The only option we have is, at some point, to create a common merge point using a baseless merge. These tend to be pretty finicky and if you don’t plan on it up front, are nearly impossible to deal with.
Our typical use for three way merging arises when we branch from the trunk into an internal developer branch, and at the same time create a partial branch (just views and content) for a third party design group who we want isolated from the rest of the team. We need to be able to merge our internal code changes to and from their branch. These scenarios are usually project-based, and we get by with the fact that we can create both the design branch and the internal branch at the same time. When you have that synchronization, a baseless merge can be done between the two new branches to get a merge history which sets you up for a project lifetime filled with happy merges.
However, we ran into another scenario the other day and I didn’t know whether we’d be able to handle it. Our client has a large codebase and our general strategy is to keep the Trunk in synch with what’s live or approved to go live (though we’re contemplating another “Production” branch which may make things easier). During the October to December timeframe, the busy season hits the websites as customers buy their product and development on our side slows down, at least on the day to day small projects. We can’t leave large projects in the trunk during this time and risk accidental deployment.
We will have several large projects going on which won’t be released for several months. One of these projects is to finally upgrade all our disparate systems to .Net 4 and MVC 3. At the same time, we’ll have at least one more large project separate from the upgrade, but looking to use a lot of the fun new MVC 3 functionality. We need a three way merge. We may have other projects coming down the pipe during these months as well, so I wanted to find a way to use baseless merging to assure that all new projects could be merged with the Upgrade branch without polluting the trunk.
Future projects won’t have the same exact starting point as the designer branch scenario, but I found a way to mimic starting from the same point. It goes something like this.
Assume you have Trunk and Upgrade branches which were branched months ago and each has a lot of changes since then. You need to branch from Trunk into the new project branch, Foo, but then to also get the updates from the Upgrade branch.
- View history on the Upgrade branch and find the changeset at which it was branched. We’ll call that X.
- Branch from the Trunk to create Foo, but branch from changeset X.
- From the command line, do a baseless merge from Upgrade to Foo, specifying changeset X and including the /discard option. This causes it to only create the merge history, which is fine because the code is identical when you specify changeset X.
- Now you’ve got your merge history. You can merge up from the trunk to get the latest of its code, and you can merge from the Upgrade code to get the latest of its code, and everyone’s happy.
And there you have it. Of course, now that I think about it, I’m wondering whether I’m over-complicating things. I bet I could get the same end result by merging the latest from Trunk to Upgrade, branching from Trunk to Foo at latest, then baseless merging from Upgrade to Foo and accepting all edits. For some reason, the bullet list above seems cleaner to me but they probably boil down to the same thing.
I guess the moral of the story is this: Baseless merging is going to be a nightmare if you don’t plan for it up front. The whole reaching into history for a common merge point probably has other uses as well, and I wonder whether something like this could also be useful to bring together two separate branches which weren’t baseless merged up front. I’ll investigate that another time. In the meantime, I’ll dream wishful dreams of distributed repositories like git and Mercurial, which live in a land where this type of thing is supposedly mundane.