Having read my last post regarding version, you have no doubt jumped in your time machine, gone back a few years or months and implemented a sound versioning policy on all the projects you work on; your colleagues think you are even more amazing than you were before, and life is generally coming up roses.
However, back in the present day a problem has presented itself. While the code in the trunk has improved in leaps and bounds in terms of both high- and low-level architecture, features, modularisation, and to a lesser degree for this example, testing and documentation a parallel path of development has formed in one of the branches.
As previously discussed, branches are best used for fixing bugs in specific versions. More than occasionally, a branch becomes the site of new development in order to cut out the overhead of migrating customers to newer versions – I’ve seen various reasons for this, but they’re of no importance as it’s the outcome that counts.
Depending on the timescale involved, and the amount of active development in both the trunk and the branch, the result may be two versions of the same project that are in essence both the latest version but are partially or totally incompatible with each other.
The problem can be alleviated to a greater or lesser degree by porting new features from the branch into the trunk. If this is done diligently, this ceases to be a problem. It’s still not a good situation to be in, but it is one less problem on your plate…for now, at least.
If porting doesn’t happen – due to perceived time contraints, difficulty in doing a quick port because of architectural changes, laziness, whatever – you’re heading into territory where the merge from hell lives. Let’s take the example where a project which in the branch is a monolithic project built with Ant now exists in the trunk as a modular Mavenized project which has been refactored to the nth degree to improve many aspects; we now have two incompatible codebases that require reconciling.
There are a few approachs that can be taken here. The brute force approach is to examine the branch compared to the trunk to build up a profile of files that have been added or removed. You can finesse this process a little by comparing the branch to the trunk at the time of branching to get some addition information – but any major strucural changes to the trunk (like that described above) will negate this. Once you’ve compiled your exhaustive list of files that have been added, removed or updated you can go through and copy each file from the trunk to the branch – ensuring in the process that you’re not introducing any bugs, incorrect assumptions, duplicate functionality, etc. Also, make sure that you’re matching the new architecture and coding guidelines if you don’t want a rap on the knuckles from the other developers.
Another approach is to merge at the functional level, in other words port the features but not necessarily the code. This may feel like a counter-intuitive approach given the features already exist in perfectly good code just over there, but digging those features out of the branch could well be as intensive – and probably a hell of a lot more frustrating – than implementing them afresh in the the trunk.
If all this seems like a lot of hard work – it is. It’s far better to never get into this situation in the first place, but we live and work in a world where things have not always been as pragmatically optimised as we would like. As we move towards cleaner, better tested and architecturally sound projects there are occasionally a few pit stops along the way.