Releasing and hotfixing with single git master branch

Jan 08, 2020Git, Gitflow, CI/CDhttps://git.io/JvE0Q

People know gitflow, and they like it. It's a complicated guidelines about git branching, tagging and releasing strategies. Maybe it's good thing when it first came out - in 2010 - when GitHub wasn't that popular, Travis CI hadn't been founded and Jenkins was not called "Jenkins".

So let's take a look at what we have on the table today that were not there or not so popular that everyone can easily setup ten years ago:

  • Continues integrations tools working together with GitHub: most of them regard git as first class citizen by implementing features around commits, branches and tags. They also interact tightly with GitHub (or other platforms such as GitLab) with web hooks and GitHub APIs.
  • Continues delivery with automated end-to-end testing frameworks: every features and even every piece of code should be shipped and released in production asap, leading to the fact that most branches are short-lived and shall be merged asap.
  • Docker and Kubernetes: dependencies can be easily defined, managed and cached, the whole running environment can be easily replicated, built up and teared down.
  • Microservices and Single Page Applications: resource consumptions of services or applications are minimal and cheap. So it's possible to have a deployment for every single branch or even every single commit.

With those tools and concepts in mind, gitflow clearly becomes over-complicated and outdated. Because:

  • Gitflow is decentralization friendly, just as git itself. However, with continues delivery in mind, each developer shouldn't hold changes on their own machine for too long.
  • Most of the branch operations in gitflow are happening locally on developers machine, and pull request which is the most important step for CI tools is missing.
  • The develop branch is basically useless if you can programmatically spin up deployments for each branch. And more long-lived branches means more maintenance overhead, more chances of merging features to the wrong branch, and more confusions for new comers to get to know where to start.
  • The release branch is also an anti-pattern of CD. By following CD, changes should be release quickly and automatically. There should be no human interference during the release phase (human make mistakes, machines doesn't), not to mention that gitflow encourages developers to commit more changes on release branch before it's actually released.
  • hotfix branch is a good idea but when it comes to finishing hotfixing, gitflow becomes cumbersome again because of the develop branch.

Enough talking about problems, here comers my solutions:

  • Keep only one long-lived branch, by default it's called master.
  • All the rest of the branches are short-lived, and they all checkout from master branch, no matter it's feature, bugfix or hotfix.
  • Every time when a feature, bugfix or hotfix branch is ready to merge back to master branch, a pull request should be created, and CI starts to run all the tests, including unit test, e2e test, code coverage check and code quality check. And CI also create a sandbox deployment of the incoming branch for reviewers or QA to validate the functionalities.
  • When all the tests, checks and reviews are passing, and the branch merged into master branch, then CI create new git tag one the latest commit to mark it as release candidate, as well as changelog, release note, etc. Then CD took over the baton and start automated releasing.
  • Depends on release policy, the releasing can be either deployment directly to production environment or can be creating a staging deployment and notify product owners to sign off, and the last step of the signing off should trigger the production deployment automatically.
  • As for hotfix, it's every similar to bugfix but the steps to generate release candidates are slightly different, as shown in the graph below:

(Generated with gitgraph.js)

For the bugfix and feature branch, git tag (release candidate) v1.0.1 and v2.0.0 are generated as soon as they are merged into master branch.

Assume that v1.0.1 has been deployed in production, but v2.0.0 has just been deployed in staging. Now there's a issue been found in production need to be fixed quickly but new features introduced by the feature branch haven't been signed off yet.

Now here's the steps to do the hotfix:

  • Create a new branch hotfix from the current production version (v1.0.1).
  • Apply the hotfix commit: h45h6. It can also be cherry-picked from commits that have already been merged to master branch.
  • Create a pull request to master branch (not shown in the graph).
  • Since the base of hotfix is different from the head of master , a merge commit (h45h7) from master has to be created and conflicts (if any) need to be resolved in that commit.
  • As soon as all the testing, checking and reviewing are passing, the hotfix now can be merged into master.
  • Once merged, a new merge commit (h45h8) is created and the release script will be running based on this commit:
  • Get the head of the former hotfix branch:
export HOTFIX_HEAD=$(git rev-parse --short HEAD^2)
  • Get the last non merge commit (which is h45h6)
git rev-list --abbrev-commit --max-count=1 --no-merges $HOTFIX_HEAD
  • Create a new tag on this branch. And since it's patched from v1.0.1, the version should be v1.0.2.
  • Back to master branch, create a new tag of v2.0.1 on the latest master branch.

After all the steps, now we got 2 tags. And then 2 builds or releases can be triggered on each tag. v2.0.1 can be deployed in staging environment automatically, and v1.0.2 can be safely deployed to production environment since it doesn't include any new features introduced since v2.0.0.

The steps might look complicated, however, once it's automated, the developers just need checkout a new branch, push changes, create PR and merge it. The less manual steps, the less chance of making mistake.


Further readings

Powered by Gatsby. Theme inspired by end2end.

© 2014-2020. Made with by mdluo.