This blog post serves as a documentation for a Git workflow that I successfully use for my Open Source projects (e.g. coala) as well as my clients. It’s focused on two things:
- Code quality, because we need it. Otherwise our stuff will break.
- Simplicity, because we’re humans and we don’t want to use something as complicated as Git flow. (I have seen a lot of people claiming to use Git flow, however when we talked about it it almost always turned out they don’t actually use it. :))
It gives general guidelines and I encourage people to change the workflow according to their special needs – however make sure that everything you do goes towards simplificy and quality and happens for a good reason.
The following paragraphs will define the most simple and minimal approach which is a base case of how this workflow works, the extensions paragraph defines some extensions which help you dealing with several common usecases. You will likely end up using the base workflow with one or two extensions.
The last paragraph will recommend some tooling which allows you to run this workflow more efficiently.
Branch names are important because they influence how we think about the workflow. The main branch for Git repositories is master. Master is supposed to be always stable and the main point for developers to start with. The respect for a branch named master is higher than for e.g. develop and you will yield higher quality results by just naming it like that.
For development you will want to go with user owned branches. If I name my branches feature/newui, the name contains less information than me naming it sils/newui, sils being my user identification. Any developer knows who to contact if there is a stale branch or any problems – the owner of that branch.
As an owner of a branch, I can also reset my branch to a new commit that has nothing to do with the previous history. It’s my branch and it’s my responsibility.
Great. I have my owned branch, I developed a crazy new thing and I want it to be in master! How does it work?
Do the natural thing. Submit a Pull Request, Merge Request, patches on BugZilla or whatever review UI you already use.
Start reviewing: my strong recommendation is to make good commits and review every commit on it’s own. Make sure that every commit only changes one thing and is as small as possible. Reviewers will find more bug and you will have saved a lot of time on the long run. “Reduce technical debt.” Of course you will also want to use continuous integration and code analysis on your project to save you lots of review time and enable people to find and fix issues earlier. You can use the git rebase –interactive for fixing up your commits – don’t be afraid, after you lean it once it’ll come in handy in a lot of situations.
Many workflows would now propose to do a merge commit. I recommend to do a fast forward or implement a semi-linear workflow – why? If you worked with merge commits for a longer time you probably saw failing builds on master or other critical branches even if you had CI on all branches – merge commits are changes. If you don’t review them (and that’s a hard thing to do) they may bite you. What does this mean?
Before doing a merge you have to rebase your commits onto the latest version of master. The continuous integration will be retriggered and your builds verify your code again. You should also check manually if the commits you added underneath your existing ones could do any harm! After doing that you can either do a fastforward (git merge –ff-only) or a merge commit (git merge –no-ff) if you want to keep history of your PRs/MRs. I recommend doing the fastforward and thinking in changes, not in features. This purely psychological thing can change the way you develop source code. Your builds will not fail of deterministic reasons anymore.
I recommend doing continuous releases from your master branch. Either push your website to your server or your package as a prerelease to PyPI.
If you manually want to trigger releases, set up your CI to do it for you on your command right from master. (E.g. using the “when: manual” in GitLab CI or when tagging a commit.)
If that is sufficient for you, you won’t need any other branch than master and user owned branches.
The following paragraphs explain how you can extend your workflow.
You may have the need to be able to fix any production issues really quickly. You will want to bypass code review. You might even want to bypass continuous integration. The solution is simple:
Just set up automatic deployment for hotfix/… branches.
The most important thing however is not to use master! Master is always stable and reviewed. You deploy a hotfix *temporarily* and pause all other development until a clean equivalent of the hotfix is merged/fastforwarded to master. This way you don’t get your master broken but you’ll be able to temporarily deploy potentially dirty hacks when needed.
If you want to maintain bugfix releases featuring only selected bugfix commits you will want to branch off a release/… branch when doing a release. Usually you’ll want to name it after the major and minor but not include the micro as your branch will move over your micro releases. (E.g. release/0.8 is good.) Whenever you want to do a bugfix release, just cherry pick your commits onto that branch and trigger a release when needed.
Apply the same code review policies as for master. Doing automatic prereleases may be awesome for the people using your software, being able to get the latest stuff from master in no time.
Long story short: keep away from GitHub. GitHub forces you into their workflow using merges, cluttering history, compromising your code quality (at the advantage of being a bit simpler for them to implement and for you to use).
The best tool I found so far for this is the GitLab Enterprise Edition, which is sadly not free software. The recommended setup is:
- Protect the master branch. Nobody can push. Everybody can merge.
- Allow merges only when builds pass.
- Allow merges only when at least one (potentially more) nonauthor approved a merge request.
- Set merges to fastforward only. GitLab will offer coders a rebase button even so you don’t have to do it manually every time.
- Automatic deployment or when: manual for master/release/hotfix branches.
- Set up GitLab CI to build your stuff and test it, if you’re deploying with docker, test in docker!
- Use static code analysis like coala in your GitLab CI.
- Enforce a minimal test coverage, ideally your coverage should always grow or stay. That’s a good way to handle legacy projects as well as mature well tested ones.