Why we have to choose between safety and tidiness?
Quick note before I begin. GitFlow, was first published by Vincent Driessen in 2010. He suggests a simpler workflow — GitHub Flow — for the teams that do not have to support multiple versions at the same time. For further reading see the post from the author himself.
Most of the teams around the world have worked, work or will work with GitFlow. A good understanding of branch management and conflicts resolution can save a lot of trouble. The latter is the topic that I will focus the most in this article.
Long living branches
- master — this is where stable production ready code lands. No direct commits are allowed. If you want to configure Continuous Delivery for your project, master branch is exactly the place (read how to hook on commits). Each change on this branch must be tagged with version number.
- develop — this is a construction area. All new features are merged to it. This branch should be buildable all the time. If it doesn’t (for any reasons), fix it asap.
- branches off from develop
- merges to develop
- naming: anything but master, develop, release or hotfix; most commonly devName/JiraTicketNo
- branches off from develop
- merges to master and develop
- naming: release, rc
- branches off from master
- merges to master and develop or release (if exists)
- naming: hotfix
This is the most important topic to cover. They will occur eventually — even in one-man projects; that’s right, I work with a great developer who did do just that 🙂 Anyway, I want to focus mostly on it because it’s a very marshy ground and can bring tragic effects to the project.
I know the feature branch may look like it doesn’t follow GitFlow rules, but let me explain it.
Plans are useless, but planning is indispensable. — Dwight Eisenhower
Sometimes you take a bug ticket into a sprint and this bug turns out to be a big refactoring that lasts for couple of sprints. When refactoring gets big enough, this branch becomes a stand-alone version and is not included in any future sprints. It must pass all regression testing and is considered as a hotfix.
We all desire a harmony and… Let me stop you right there. Projects that run in teams are messy. Tools that we have can be a pain in ass too. Privileges, configurations, … There are just too many things that can go bad or make us workaround.
Even simple decision as which variant of merge to choose when working with GitFlow can be a bone of contention. Which one do you prefer?
Each decision has its effects. Let’s discuss what they are.
Simple fast-forward merge is the most messy. Depending on situation on merging branch it can look like below. Probably that is why there’s no such option on GitHub.
On the right we have a take over of other branch’s history. Merge is quick, requires no other action. Whereas merging is effortless, searching for feature in commits that introduced it is exhausting.
Merge no fast-forward
This one always requires us to create a merge commit. No fast-forward option is set by default when working with GitHub and in long run, causes the least pain. This is preferred by Vincent in his GitFlow workflow. It displays entire history of merges. There’s one caveat though which I’ll explain in section GitFlow with GitHub.
A project history given by squashing is tempting to be used as a default option. It’s just a straight line with commit dots — fantastic! But when something looks nice, it must be bitter. One merge squash can create a conflict where we expect none.
GitFlow with GitHub
GitHub gives us great options like e.g. protecting branches from direct commits. It’s a good practice, in my opinion, to protect two main branches master and develop. But let’s remember it’s a double-edged sword — it requires other peers to review the code — apart from blind approves 😉 — but prevents us from having a clean history.
Clean GitFlow — expected result is as as presented. All the branches get updated by merge with no fast-forward method. The history is clean. No extra merging required. Conflicts are resolved within this merge. Unfortunately, having protected branches prevents us from such operation.
Merge no fast-forward — besides visual representation in a git history, this method turns out to be safe. As developers working with someone else’s businesses I suggest we stick to things that are safe rather than pretty. With this method we can very thoroughly walk through the history and study what was happening on each step.
Merge squash — It’s safe to use this method for merging feature branches to develop. It makes develop branch look clean — like really clean.
Unfortunately I cannot think of any other usage for merge squash when using GitHub’s Pull Request system.
It would be very helpful to have merge options on GitHub PRs. Defining merge strategy would solve the issue with ugly graph when backporting (image on the left). After we updated release branch with changes from hotfix we pulled changes from develop by using merge squash (develop -> release). This way we resolved the conflicts on our release branch and we should be able to merge it back to develop without any problems — in theory. It fact, commits on develop branch still cause conflicts — which shouldn’t matter anymore and we should just override changes on develop with changes from release branch. We could do it with a command below, but protected branches won’t let us push these changes to a remote.
git checkout develop
git merge -s recursive -X theirs release
Merge fast-forward — if GitHub allowed fast-forward option we would be able to get a situation like this one. Fast Forward in this case “swaps” branch develop with release. The merge commit message is “Merging from develop into release” and that stays in the history, but there’s a saying:
One picture is worth more than a thousand words.
Clean GitFlow with GitHub is either unsafe or impossible — which affects history graphs. We should be aware of strengths and weaknesses of tools (and libraries) we use. Work out with your team a preferred solution and make sure everyone follows the same rules.