Search code examples
gitrebase

How do I resolve this patch release problem with without using git cherry-pick?


I've read @raymond-chen's Stop cherry-picking series but can't wrap my head around if the following problem can be solved without using cherry-pick:

Git flow

The timeline is as follows:

  • We branch off Master branch to prepare release v1.0.0 of our software at point M1
  • We develop two features in parallell: F1 and F2 on separate feature branches. F2 goes into the release while we hold off F1 and merge to master.
  • After release of v1.0.0 and subsequently merge of F2 back to master we develop a feature F3 and merge it back to master at M4
  • At time T1 mangement decide that we need feature F3 shipped to production ASAP in a patch release v1.0.1 based off v1.0.0. Feature F1 must not be included in that release

How can I acheive this without cherry-picking?

In retrospect feature F3 should have been branched from F2, but that's too late now. We don't want to rewrite git commit history on master branch.


Solution

  • I agree with the article's sentiment that you should generally try to avoid cherry-picking, when a better alternative exists. That being said, there's nothing inherently wrong with cherry-picking, but when you do it in long-lived shared branches, you create duplicate copies of the commits which can cause (a slight amount of) confusion when viewing the history, and sometimes lead to additional conflicts or even unintended side effects. At a minimum, if cherry-picking will result in duplicate commits in long-lived branches, I recommend using the -x option so that when viewing the history, one of the commits will show the trailer stating that the commit was cherry-picked. Note that when cherry-picking commits from unmerged feature branches this isn't necessary.

    In your particular case, I can think of 3 options for achieving your goal:

    1. Revert F1 on master. For some reason you don't want this feature included, so take it out. Whether you choose this option might depend on what your master branch is supposed to represent. If it represents what you're releasing, then this is a good option. You can always add F1 back in later when it's ready to release. (Perhaps by reverting the revert.)
    2. Cherry-pick F3 over to your new hotfix branch. Sometimes cherry-picking is the best choice, and that's OK. Note this is what Raymond Chen's article suggests doing in your scenario. (Here he suggests cherry-picking the right way to help you avoid cherry-picking the wrong way.)
    3. You could do something fancy with merges, but this is so confusing I wouldn't recommend it:
    git switch -c hotfix F2
    git merge -s ours M2 # this will bring in M2 without taking the changes
    git merge M4
    # now make your hotfix changes
    

    That solves the immediate problem but creates a new one when you merge it into master, since it effectively has the same result as reverting F1 (or M2) but in a far more confusing way.

    Side Note: you stated:

    In retrospect feature F3 should have been branched from F2, but that's too late now.

    That may be true, but if master represents releasable code, then perhaps if you could do it over you would have held off on merging F1 into master. If that's true, then option 1 of reverting F1 might be best, for now.

    Tip: if you decide to revert F1, and F1 was just an abbreviation for multiple commits on a feature branch, you can either revert each commit in the reverse order that they occurred, or you can revert the merge commit M2. If there's more than 1 commit I almost always prefer to revert the merge commit.