I have a pretty complicated git tree for my application, with a large number of branches. Version #7 is in development at the moment, but throughout the life of the application, not only has it been released in prior versions, but customisations of those prior versions have been released separately.
I have created a new feature for version #7. But the powers that be also want that feature to be incorporated into the customisations of prior versions. How best to approach this?
To illustrate
1-----2----3----4----5----6--------7---------7
\ \ \ \ /
\ \ \ Feature
\ \ \
\3A \ \
\3B \ \
\4C \
\4D \
\6E
\6F
So I want my feature to get into 3A, 3B, 4C, etc. There may be more than 2 of each version. Bringing everyone up to version 7 is not an option.
The way I see it I have two options. I can't do a simple merge obviously.
Cherry-pick all the commits from the feature into 3A, 3B, etc. individually. Not ideal because a lot of the code that the feature is built on doesn't exist in the earlier versions. This leaves me with a lot of sorting out and conflict resolution to do on each "merge". But if I have to do it this way, it would be best to only do it once for each version (i.e. once I have 3A figured out, a simple way of applying the exact same changes to 3B would be great - maybe I could then just cherry-pick the commits to add the changes to 3A into 3B).
Using the pull request to put the Feature back into #7, manually make all the changes in a branch of 3A, which I can then merge back into 3A and 3B (they are similar enough). Do the same with 4C & 4D, and do the same again with 6E and 6F. I'm tending towards this option at the moment but it's still a good bit of manual work.
Is there a better strategy than either of these?
tl;dr: Go with your option 2, and the more you can condense it the better.
As a rule of thumb, whenever possible, prefer merging over cherry-picking. Of course sometimes you can't merge and you must cherry-pick.
A concept exists, when fixing old bugs, of branching off of the commit that created the bug, fixing it, and merging that into any branch (or commit/tag) that needs it. The benefit of this is if that branch can be merged in cleanly to other branches, you don't need multiple cherry-picks resulting in multiple copies of a similar commit sprinkled throughout your repo.
Note this same concept can be applied to any change, regardless of whether it's a bug, feature, etc.
A minor tweak to this concept is if you know in advance which target commits you will be merging into, then you can start from the merge-base of the target commits. For example, in your diagram, you would consider branching off of commit 3
, because that is the most recent commit that is reachable by all targets you'll need to merge into.
Where this idea breaks down is when you can't go cleanly into all targets.
It sounds like you won't be clean because:
...a lot of the code that the feature is built on doesn't exist in the earlier versions.
Here you're actually hinting at a secondary problem which is the feature is designed based on newer code that doesn't even exist in the old code yet. This means you can't create the new feature starting from commit 3
, not only due to potential conflicts, but also because of missing dependencies.
This is where your option #2 comes in, which can be restated as:
Use the fewest number of separate branches necessary to merge into all targets.
Maybe you'll land on one branch per major release version like you suggested.
Note that anytime you need to cherry-pick during this process, I recommend using the -x
option so that all of the additional copies of the original commits are notated as such.
Side Note: It may help to think of your Option #1 as a worst case scenario which is a subset of option #2, where you end up needing a separate branch per release for some reason.