The Git Flow assumes you only have a single supported release, with the master
branch always pointing to the latest release. Since you support multiple releases simultaneously, you cannot copy that workflow 1:1. Nvie's Git Flow is a very good example of a branching strategy, but you must adapt it to your needs. Most importantly, you will have multiple active release branches.
When you identify a bug, you will have to do some testing to determine all affected versions. It is not sufficient to write the fix first, then merge it back into the release branches as a hotfix. Usually, you'll end up with some continuous range of affected versions. Very old versions might not contain the bug, newer versions might have gotten that bug fixed accidentally. You will need to verify the bug on each version so that you can verify it is actually gone after the fix. If you can express the bug as an automated testcase, it's pretty straightforward to find the problematic commit via git bisect
, or to run the test for each release:
for release in 3.8 4.1 4.2
do
git checkout $release
if ./testcase >release-$release.log
then echo "$release ok"
else echo "$release AFFECTED"
fi
done
Now, you used to write the fix on trunk
/master
. This is problematic because the buggy part may have changed between versions, so a patch will not usually apply to an older version. In particular, code in master
might depend on any features available in master, which might not have been present in older versions. It therefore makes a lot of sense that a Git commit references its whole history, not just the change set. When merging back, it will pull in all the history it depends on.
Using cherry-pick
or rebase
ignores this history, and records a new commit with the same changeset, but a different history. As pointed out, this will not work if your codebase has diverged.
The “correct” solution is to write the fix as a hotfix on the oldest affected release. Then, you merge oldest release into the second-oldest release. Usually, a newer release would contain all commits of an older release, so this is OK. If things have changed, you now have a chance to manually resolve the merge conflict. Then you continue merging each release into the next-younger release until you are done. This maintains proper history and avoids a lot of unnecessary work, while only requiring effort that has to be expended anyway. In particular, this incremental merging gets you closer to the current development state in small steps.
As a diagram:
| o normal commit |
| x hotfix |
| ⇘ merging |
3.8 --o-----------------x
\ ⇘
4.1 o--o--o-----------x'
\ ⇘
4.2 o--o--o-----x''
\ ⇘
develop o--o--x'''--o--o
From your update you're asking about a release that has not been deployed to prod. I'm assuming that means that you haven't merged to master or tagged the commit yet.
In git-flow, you would fix any bugs directly on the release branch. See the diagram below:
The git-flow docs I've read say explicitly
any problems are fixed directly in the release branch
I think the rationale is that these bugs should be small things that relate to the release, and it will improve your release test cycle by avoiding bug-fix branches. While the rationale is sound, I believe it's better to make sure the codebase is protected, and code review processes should not be subverted because a release needs to go out the door. Since you have a code review policy, I would recommend doing exactly what you're already doing. Create a bug-fix feature branch off your release branch, commit to the bug-fix branch, and submit a PR from bug-fix branch to release branch--regardless of what the docs say. After all, the branch strategy should be tailored to your needs, not the other way around.
Best Answer
Use a distinct commit to fix the bug in one branch, then cherry-pick that commit into the other branch.