8

enter image description here

We have to support multiple releases, so we are following git branching model similar to the above diagram. Now the problem is that we have to do lots of merging here example for fix/011, we are merging it three different branches(master, release/1.0 and release/2.0). Also there is question of from where to create this fix/011 branch from master, release/1.0 or release/2.0? Creating it from master brings unwanted features, creating it from any release branch brings merging overheads and conflicts and cherry pick back to master is error-prone.

Is there any better approach? We can't follow single release branch approach because our different customers are on different releases and many times they don't want to do major upgrade to get fix for small issue(and there are pricing aspects as well)

We are using github.

One good option I found is forking fix(/feature) branch from all branches (refer: https://docs.microsoft.com/en-us/azure/devops/repos/git/git-branching-guidance?view=azure-devops#port-changes-back-to-the-master-branch) but our developers didn't find it much different from exiting approach and they feel is complex.

5 Answers 5

9

The thing to remember is that merging a fix also merges all its ancestors, so if you want to keep code in 2.0 out of 1.0, your merges should always go from 1.0 to 2.0, and never the other way around. That means fixes need to be made (or rebased onto) the oldest branch possible, then merged into the second oldest, then into the third oldest, and so forth. This takes a lot of discipline, as usually developers prefer to work on the bleeding edge, and only think about the maintenance branches as an afterthought.

7

Unfortunately you can't do this with branching. In fact you should not be doing those red line merges at all.

As soon as you decide that the release X is not going to be getting updates from master you can't merge your fixes back from that branch. You have to write the fix for each branch separately.

What you can do to avoid retyping, is divide your code into versioned libraries and update releases to use fixed versions of those libraries. Hopefully you would find that this reduces code overlap and the functionality of the libraries wouldn't change overtime. So Release 1 and Release 2 could both use Library v1.2

4

You'll always have merging overhead when needing to support multiple versions, what you could do however is instead of merging your specific bugfix branches into each release branch is:

  • Create a bugfix branch (for example bugfix/001) from the release branch of the lowest version that has the bug (for example release/1)
  • Merge the bugfix branch back into the release branch (bugfix/001 into release/1)
  • Merge the release branch into the next release branch (release/1 into release/2)
  • Repeat "Merge the release branch into the next release branch" as long as you have next release branches
  • Merge your latest release branch into master

This way you can for example create a few bugfix branches from release/1 and merge them back into release/1 and don't have to do multiple merges into upper branches as you will at some point merge release/1 (with all the applied bugfixes) into release/2

If you don't want to merge release branches in other release branches (because for example you create tags on the release branch and want to know what the latest tag is on the branch you're working on) you can create fix branches along side the release branches and merge the fix (for example fix/1) branches into their own release branch (release/1) and into their next fix branch (fix/2). Instead of creating bugfix branches from the release branch you then create them from the fix branch.

2

Since you're asking for a better alternative you should IMHO consider trunk-based development (TBD), or at least the release branch strategy it uses:

  • no merges involving release branch(es) whatsoever, in any direction. So none of the merge issues you mention :)

    Note: I mean major/multi-changeset merges here, not individual changeset (cherry-pick) merges.

  • any change from another branch needed in a release branch is individually cherry-picked and verified just like any other change (the fact that it works in the other branch doesn't mean much, it can still cause breakages because of the different branch context)
  • you can also have child release branches for minor releases off the major ones, as well as hot-fix twigs, if you really need/want to - as many as you can afford (the higher their number the thinner you'd be spreading your resources, it can get costly).

This is how it's illustrated in What is Your Branching Model?:

enter image description here

But it does require a mental shift for development branch users.

Personally I believe TBD is the only solution that can scale for large projects, for which branch merges typically translate into Integration/Merge Hell.

Having a good CI/CD pipeline in place at least on the master branch is pretty much mandatory, especially for larger projects. Release branches can benefit from having one each just as well!

3
  • 2
    I think they do essentially have a trunk based build. I think the problem is that they are maintaining two distinct lineages.
    – Kain0_0
    Commented Mar 27, 2019 at 4:30
  • Thanks @dan-cornilescu, we are sort of following this model, we actually merge pull request in master and then we merge(CP or copy pull request) it to other release branches wherever applicable. We feel that cherry pick is not neat and creating multiple pull requests(one per branch) is too much overhead.
    – banjara
    Commented Mar 27, 2019 at 4:49
  • @banjara: well, you wanted multiple release branches... The more you have, the thinner you spread. Reduce their number until the overhead appears bearable. I wouldn't consider doing multi-changeset merges across release branches, that's a recipe for disaster. Commented Mar 27, 2019 at 10:37
1

Bad Choices

I do not think that there is a good branching/merging/commit control strategy that fits well here.

Certainly you can:

  • merge the change forward from the oldest common ancestor, but this produces truly weird commit histories, and requires some measure of discipline.
  • Cherry pick commits, losing the context of their development, and producing a truly large headache should version 1 or 2 ever need to be merged back into master.
  • not support patching/improving the older releases, though this has obvious business implications.

Divide and Conquer

When the problem becomes painful usually the best strategy is to divide it into smaller more manageable problems.

  • split the project into 2 different products, give them separate repos and fix the bugs twice.
  • refactor the codebase pulling out common code into libraries which can be patched independently.
  • split the UI's off, and homogenise the back-end - offering two skins.

Buisness case

Why do you need to maintain separate versions? The answer to that will be interesting.

  • If its about money, consider giving them a discount to upgrade and allow simplifications to occur.
  • If its about the UI and user training. See if you cannot offer the old ui over the newer engine.
  • If its about the back-end database or some such. Try and address the issue. Is it to risky to migrate? Do you no longer support their DB engine? These could be addressed, removing the need for a second version.
  • Is it that version 1 provides a different set of features in contrast to version 2? Perhaps splitting it into two products backed by some common libraries?
  • Is it because you are selling support? (no joke) Hike the support fee to offset your pain points. Or if you are less barbaric hike the fee for new features/bug fixes that aren't affecting security and primary product features - and live with the pain.

Not the answer you're looking for? Browse other questions tagged or ask your own question.