21

We work as follows in our team:

  • we have only a master branch in our GitHub repo, it's not stable - thinks get pushed there each day; for stable releases we use tags (for development, we use private forks on GitHub)
  • we release a new minor version every 3 weeks, which contains bugfixes and new features (say 1.2.4, 1.2.5, 1.2.6...)
  • we also have to maintain each minor old version for a limited amount of time (few months), so when someone uses 1.2.4 while the newest version is 1.2.7, and they find a bug, they can request us to fix the bug on the branch they use. Then we release a patch version, 1.2.4A.
  • patches are rather exceptional. We usually do no more than 1-2 patches for the minor release. For most of the releases we don't do patches.

The question is, what's the best strategy to fix the bug on master and the old branch at the same time?

I can think of the two main strategies:

  1. Fix the bug on master, then checkout v1.2.4, then cherry-pick the appropriate commit (suppose the bugfix is one commit which always holds) and tag the resulting commit as v1.2.4A.

  2. Checkout v1.2.4, fix the bug and commit, tag the commit as v1.2.4A, and to incorporate it to master, do a merge.

I'm rather in favor of the first version (cherry-picking), but I would like to hear the other's comments about pros and cons.

Of course, things can get complicated when the commits in the middle introduce some major changes that can result in the fact that one can't create a commit which will work in both 1.2.4 and in master (for instance when some function name changed or more complicated things). But the more common situation is that the fix can be ported without problems.

Advantages of cherry-picking from master:

  • I think the history is more "eatable" with cherry-picking. Consider this:

    | <- bugfix done on master
    |
    |
    | <- v1.2.7
    ...
    |
    |
    |
    |
    |
    |
    |
    |
    |
    |  - <- v.1.2.4A (cherry-picked from master)
    | / 
    | <- v1.2.4
    

    vs this:

    | <- bugfix merged to master
    |\ 
    | \
    |  |
    |  |   <- v1.2.7
    ...
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
    |  |
    |  - <- v.1.2.4A (direct bugfix)
    | / 
    | <- v1.2.4
    

    Think of having dozens of commits in between. Consider having multiple patches applied like this in parallel. Half of the screen will be polluted.

  • Let's say I fixed an issue on v1.2.4, but in a few days someone asks me for a patch on v1.2.3. Cherry-pick is the most sensible way to do it.

Are there any advantages of merging in our case that I overlooked? I can understand it preserves the connection between the two commits better than cherry-picking, but we keep a documentation of releases and all of this is also tracked there.

jakub.g
  • 38,512
  • 12
  • 92
  • 130
  • Cherry-picking can be done in both directions. – madth3 Nov 07 '12 at 17:55
  • Yep, true, I ask mainly about benefits of cherry-pick vs. merge. Regarding whether to cherry-pick from master as a backport to the old one, or from the old one to the master, is the other story. I think backporting is more logical and this is how I see it's done in various projects. But this is kind of philosophical question. – jakub.g Nov 07 '12 at 19:21

2 Answers2

29

In open source projects that I've worked on, the consensus seems to be that fixes should land on master first, be tested there, and only then be back-ported to older releases. You can see this in how the Linux kernel does stable releases, for example: Developers submit patches for mainline but nominate them for inclusion in stable as well.

When cherry-picking in this situation, you probably want to use the -x flag:

When recording the commit, append a line that says "(cherry picked from commit ...)" to the original commit message in order to indicate which commit this change was cherry-picked from. This is done only for cherry picks without conflicts. ... [If] you are cherry-picking between two publicly visible branches (e.g. backporting a fix to a maintenance branch for an older release from a development branch), adding this information can be useful.

Jamey Sharp
  • 8,363
  • 2
  • 29
  • 42
  • Also note that: `-r` It used to be that the command defaulted to do `-x` described above, and -r was to disable it. Now the default is not to do `-x` so this option is a no-op. – Christophe Roussy Dec 14 '15 at 09:16
  • I would recommend this for projects where most users are living on master or close. – sevo Jul 28 '16 at 17:25
15

Your strategy 2, first fixing the bug on the prior release branch, e.g. v1.2.4 and then merging that change to your development trunk, is suggested by gitworkflows(7):

Rule: Topic branches

Make a side branch for every topic (feature, bugfix, …). Fork it off at the oldest integration branch that you will eventually want to merge it into. [emphasis added]

Many things can then be done very naturally:

To get the feature/bugfix into an integration branch, simply merge it. If the topic has evolved further in the meantime, merge again. (Note that you do not necessarily have to merge it to the oldest integration branch first. For example, you can first merge a bugfix to next, give it some testing time, and merge to maint when you know it is stable.)

One reason that this tends to work well is that in my experience, stuff gets added more frequently than it gets removed, so by making the change in the older branch, you avoid depending on any new functions etc. which may be available in the development trunk.

However you have to think about which branch you are targeting for the fix when you make the change, instead of just doing it on master and then deciding where to merge it.

Both strategies are viable and there are benefits to each.

Community
  • 1
  • 1
Colin D Bennett
  • 11,294
  • 5
  • 49
  • 66
  • 1
    Upvoted. What if bug is irreproducible, alternative feature is recommended or software behaves differently on master? In those cases it makes zero sense to fix on master. For Linux, backwards compatibility is crucial so cherry-pick approach is fine. – sevo Jul 28 '16 at 17:24