views:

1089

answers:

14

You have several maintenance branches for existing releases of your software. Some developers are making direct changes in the maintenance branches, and merging periodically into the trunk. Now comes an extensive refactoring in the trunk codeline, scheduled for an upcoming major release. But this makes the maintenance branches fundamentally incompatible with the code in the trunk, as they might depend on code that does not exist anymore, for example.

How do you deal with this situation in practise?

+1  A: 

At the point that your maintenance branches are no longer compatible with the main trunk, it would be time to create new branches for that purpose. That is, at the start of the big project, you make sure all your developers are aware that new functionality is coming in the main trunk, so that they can make a better choice of where to implement fixes. Presumably, if the code changes occurring in the main trunk are so significant as to render the maintenance non-supportable, then the maintenance should be incorporated into the main trunk.

Elie
The maintenance branches probably represent actual shipped code and thus can't be abandoned. Fixes must be applied to those branches because there is a customer who needs that fix and is running that version of the code.
Mr. Shiny and New
+1  A: 

Create a maintenance branch and have it act as a buffer between trunk and the version-branches.

Changes to the version-branches go into the maintenance branch, and then propegate into trunk only if they can and vice versa.

I don't think there's a silver bullet, though. As branches diverge more and more, they will grow incompatible and so you have to consider for how long you will support them. Otherwise you might end up fixing bugs more than once but slightly differently for the various branches.

Christian Vest Hansen
Similar to Elie's answer!
Jenko
+19  A: 

I would consider it the responsibility of the branch maintenance developer to merge the appropriate change into the current state of the trunk. There are several possibilities:

  1. The code in the trunk has not changed and the patch applies without conflict.
  2. The code in the trunk has changed and the patch applies, but with a manual merge needed.
  3. The code in the trunk has completely changed and the patch cannot apply. The developer must evaluate whether the same defect exists in the trunk, and apply an equivalent fix if needed.

Cases 1 and 2 are the usual maintenance development paths. Case 3 is the case you are considering, where the trunk code cannot accept the maintenance patch in any form. If the developer cannot himself determine whether the same problem might exist in the trunk, then he should enter an issue into the issue tracking system. This issue would direct the trunk developers to consider the reason for the patch in the maintenance branch and whether the same defect might still exist. Entering a new issue for a possible defect in the trunk should be a last resort for the maintenance developer.

One benefit of having maintenance developers try to apply patches to the updated trunk is to increase their familiarity with the new code base. Eventually, they will run out of maintenance work and will need to work with the new trunk. Having at least a basic level of familiarity will be of great benefit.

Greg Hewgill
Lots of open possibilities. Great answer Greg!
Jenko
Of course one COULD apply the refactoring to the maintanance branches as well. But in 99 out of 98 cases this will be to much work.
Jens Schauder
@Jens: Unless you plan to _develop_ the "maintenance" branches, only fix what's broken. Your customers want stability, not clean code.
Mr. Shiny and New
I thought this answer was so obvious at first, until I read all the other answers. Appalling! So many wrong wrong answers. I'd vote this one up TWICE if I could.
Mr. Shiny and New
A: 

I can only echo what others have said, while stressing the real pain in the a$$ that patch queues can become.

If you have a predefined (and iron clad) merge window, you should only have two weeks of hell to deal with.

Tim Post
+2  A: 

The only answer I've been able to come up with is to create a maintenance-trunk branch just before you start refactoring. You maintain this new branch as if it were a trunk, merging changes to and from release branches as normal. Going forward, you then have to be careful about mixing changes from the old and new source bases.

The other alternative is to try something like MolhadoRef (blog article about MolhadoRef and Refactoring-aware SCM), if you can find a ready-for-production equivalent system that meets your needs. This is, in theory, refactoring-aware source control. I haven't looked into it in a while but last I recall it was still pretty far from being anything more than a research paper and proof-of-concept.

Mike Burton
+1  A: 

This is ultimately a question about team communication, rather than a simple branching/merging question.

The first step, as in all such cases, is realizing that you have a problem. This is something that you've done.

Then, you need to alert the whole team to the issue.

Once you've done that, I think there are two different paths:

  1. If the maintenance branches are used infrequently, say the released code is fairly mature and bug-free, you can decide on a code freeze. Each developer must finish what he/she is working on by 32 Octember, and merge those changes back into the trunk. The branches should then be either closed or frozen. Then, work can continue in the trunk, and the new software can be released.

  2. If there are frequent or urgent changes and fixes in the branches, this issue is more complicated. There still needs to be a code freeze, or the trunk will be clobbered multiple times. But here, developers still need to fix things in the interim and get them out to customers. I suggest that every change in the branches after the trunk code freeze should be recorded in the bug tracking database (a must in every situation) with a special indication that this was fixed in branch N but not yet merged to the trunk. This requires careful logging so that every relevant detail is remembered.

    After the trunk is refactored, but before it is cleaned, buffed, tagged, and released, review the bug database, particularly the items fixed in the branches but not the trunk. Are they still relevant? Now is the time to change the code again, if necessary. It may mean double work for a short while, but hopefully the code is much more maintainable now.

    Once all the known issues are fixed, the new version can be released, and the old branches can be closed.

JXG
Presumably the old branches can not be closed when a new version is released, because the old versions are still supported for customers using those versions.
Mr. Shiny and New
+1  A: 

In practice, you may have to do extra work to make your new changes backwards compatible.

  • Step 1: Start refactoring the component. With each step, keep the old interface around, but have it migrate calls to the new implementation. Note that this can be done in several steps as the new interface/API is built up. Unit tests should be able to verify that the migration from old to new works correctly, but this step will most likely still incur testing/QA overhead.

  • Step 2: The new version is live in production; make sure everyone knows about it. At this point, no new features are added to the old version, and all new (or changed) callers use the new version.

  • Step 3: Find everything (use tools to do this) that calls the old interface, and change everything to call the new interface. This probably incurs a lot of testing/QA overhead, too. Each caller can be committed/released one at a time, though.

  • Step 4: At this point, the new version is live, and there are no callers left that access the old version. Delete it safely.

Note that where the API is public and you don't control the people who call it (companies like Microsoft, for example), you might never be able to go past step #2.

This process can be slow, and it requires a lot of discipline, communications, and testing. But in cases where the alternative is playing catch-up/integration forever, it might be a reasonable option.

Sean Reilly
+1  A: 

This may be a very work intensive suggestion, but the first thing that comes to mind for me is merging everything back to the trunk. Everyone's changes get merged back to the trunk copy and keep them all together. Then, refactor on the trunk however you want. Now you have a working trunk, with all the fixes put together.

Unfortunately, this would mean that any fixes in the maintainance branches would need to get thrown together and into the central trunk. I realize this would be a whole lot of work, but I think that this would allow everything to be refactored, and any improvements in the maintainance branches would belong in the main branch. I may be naive about this, but I haven't really worked on a production project, and also don't know exactly what's on the maintainance branches. I figure this would make the trunk fully updated and all of your maintainance improvements would be integrated into the trunk.

I figure doing it this way would maximize the quality of all of your branches and have your refactoring spread over all of the branches you would branch after the refactoring. This would be a good way to bring your team together for all of the merging, as well.

Seburdis
+1  A: 

I see two separate ways to tackle this:

1.

Significant changes to the trunk (like a major refactoring) shouldn't be done in the trunk. They should be done in a branch and merged back into the trunk when they are stable enough.

Periodically the changes to the trunk should be merged with the other maintenance branches. The reason for only merging the refactoring into the trunk when it's stable is because these will then get merged into the maintenance branches. If however there is no opportunity to make these changes stable then option 2 would be better.

After changes to the maintenance branches have been made they can then be merged back into the trunk.

2.

Create a branch of the maintenance branches (one branch for each). This will be used for merging the trunk with each maintenance branch. (Note that the use of SVN externals or equivalent should be used in order to limit the number of maintenance branches).

Do all your refactoring in the trunk and merge this into the branches of the maintenance branches. When you release or think that the trunk is stable then merge these branches of the maintenance releases back into their respective branches. These can then in turn be merged back into the trunk.

In effect each maintenance branch becomes a "sub trunk".

Note that this scenario highlights the trade-off between future maintenance and upfront maintenance. The more branches and differences you have in your code the more upfront maintenance is required. The good part is that incremental maintenance is much easier.

Jonathan Parker
A: 

Do you have to have that many branches being worked on?

Was work on the trunk only started when it did because the project plan said the current release would be ready to ship, therefore it was shipped?

Have you got lots of maintenance branches because customers are refusing to upgrade to the latest release for some reason? If so address the reason.

Do you have too many old releases becouse the gap before the next main release is too great?

Do you charge the customers that will not upgrade more for maintenance, as it cost you more?

Response to comment:

Microsoft still supports Windows XP even though Vista is out

Is very true, However Microsoft does not still support Window XP SP1 even though XP SP3 is out.

This is not black and white, even if you can’t stop supporting old version, you may be able to reduce the number of old versions you support. The problem is that Sales/Support likes to say yes, but development gets the pain, so you need to get your sales/support people on side.

Ian Ringrose
"Address the reason for customers not upgrading" is not really relevant advice. There might be lots of reasons, #1 being "The customer wants that and that's what they are paying for". This is the bread and butter of software development. Microsoft still supports Windows XP even though Vista is out.
Mr. Shiny and New
+1  A: 

Given that a lot of the cost of fixing a bug is reproducing the problem and testing the fix. Can you write an automated test that will work in all the branches, even if the code fix has to be done differently for each branch?

Ian Ringrose
A: 

I think your best option is to have iterative refactoring. Instead of doing all of the refactoring in one big shot on a private branch, do it one phase at a time. Make a few sets of changes on the branch and then when you know they are stable, merge them to the trunk. Developers working on other branches would be responsible for continually keeping their branch up to date with the trunk.

Merging a small set of changes very often is going to be a lot less working than merge large branches that differ quite a bit. The more often you merge, the less work you will have to do in the end.

Chris Dail
A: 

In our project, we don't primarily fix changes in version maintenance branches. If there's a bug and

  1. it occurs in both the trunk and the main branch, we fix it in the trunk and then merge the change to the maintenance branch (which can einther happen cleanly, or require more work, in which case we decide whether isn't not better to have the bug fixed in the newer release only).
  2. it is only in the maintenance branch, there is probably some king of fix in the trunk and we go to scenation number one.
che
A: 

As Greg pointed there are several possible scenarios.

I'd add a case (2.5) where manual merge is required, but since you've moved a method away from its original location and then applied some changes, it becomes hard to merge, specially if the "base" code was also modified in the "maintenance" branch. This is not as uncommon as it sounds, in fact moving a method to a different location and applying a small fix is pretty common.

We've developed a tool called Xmerge (cross-merge) which is a first step towards refactor-aware merging. It's not yet automatic, but it helps dealing with tough merges involving moved code. It's described here and is already integrated in Plastic SCM 2.7.

We're working on: automatic move detection and also being able to "cross merge" towards multiple destination files (you move code to another file, which is also pretty common).

pablo