I recently had a client reach out to me to help her fix merge conflicts on a master branch and she was initially a bit lost in how to fix it properly. The solution is to tweak the git-branching model for better results.
It’s a big and old codebase, and the company has been growing quickly, so more power to her for stepping up her game.
In the past few months, she and the company have gone from 1-2 developers per project, to 5 or more people touching the code and deploying to several development, testing, and staging servers- not to mention a load-balanced elastic production server.
In fact, many people who work in agencies are flying by the seat of their pants, doing whatever they can to deliver work on time and they hardly have the time- and much less the energy- to work on ‘best practices.’
That’s usually where I come in. I bring enterprise-level knowledge into the agency model.
The current condition:
The current master branch was getting all kinds of merge commits- some from a release branch, others from feature branches. Sometimes, causing merge conflicts which were not always resolved in the best way.
My rule of thumb: REBASE BEFORE MERGE!!!!
I cover this one further down below, but if your higher branch (master/RC) is receiving code that is currently BASED on the most recent commit in the branch (master), then you will not have merge conflicts to resolve anyway.
Here is what I wrote to her in response:
As a best practice, there are a couple of steps that I suggest changing in the process.
Keeping master branch clean
First, at minimum, begin to only merge in the contents of one branch to master. In this case, it would be release that is the one to focus on.
Right away, this will improve your process and require less work overall.
The reason for this is to think of the branches like an upside-down tree (or a pyramid).
Master is at the top, singular point of the tree, and each branch under it gets increasingly more stuff merged to it.
Master would only get hotfixes and release branches, so two points of entry.
Then, release branches get features and bugs merged to it, so more points of entry still.
Keep master branch for production code only
Secondly, and this change would have to start after this release because there is already work on master: only merge a release branch to master after release passes approval and is truly ready to deploy to production.
In short: “master should always match production.”
Biggest reason is to keep straight where everything is- and not insignificantly, if you have some kind of catastrophic failure of master, you could use a SQL backup and the repo and deploy master code straight to prod.
In short, aside from critical hotfixes, only UAT-Approved releases should ever get merged to master- and that would be once per release number.
Very few merge commits on master ever, and you would only see –no-ff merge commits, never individual code changes.
Add a Release Candidate branch as the gatekeeper between Development and UAT (User Acceptance Testing)
There’s another level you can do now, and that is to use a third branch in between this kind of release branch- which changes rapidly – and master.
If you add a “release candidate” (RC) branch, and rename this one to develop then this is the one that gets messy- and only QA-approved, working code that is ready to go to UAT gets pushed to the RC branch.
Develop branch can get messy- which is fine, because that’s exactly how you are using the current release branch.
In this way, you separate and simplify the codebase as it gets closer to master, and this aids significantly in ensuring that nothing gets to master that would ever have conflicts in production.
How to fix and prevent your merge conflicts
Back to your specific question, I would recommend hard-resetting the master branch back to the commit in the screenshot below [no screenshot here in the blog for privacy reasons].
Then, rebase release-vX.0.0 on master and fix conflicts there first.
No merge to master (or RC) should ever create conflicts, so if it does, then cancel the merge on master, and rebase the branch with the new code on master first, and then do the merge merge.
Another way of thinking about it is- all your work should be done on develop (in this case, we’re calling it release-v4.0.0 ) and that includes getting the new code up-to-date with the receiving branch master.
Rule of Thumb: If you have to think during a merge, then you’re just asking for trouble :wink:.
This leads to CI/CD:
Following these rules can empower you to Automate your Deployments…
It’s worth mentioning that if you start incorporating the rebases like this, especially in the previous paragraph, then you will eventually be able to do CI/CD (continuous integration / continuous deployment).
If you can do CI/CD, then you will rarely have a need where a human will even touch a production server!
You can automate the whole process reliably with build tools like Jenkins or Gitlab CI/CD. At One Blink Tech, we use Bitbucket Pipelines most frequently
A successful git branching model:
If you want to read a little more, Vincent Driessen wrote a great article that I first read probably back for the first time back in 2013, and has made a big difference.
It is similar to my simplified process that I shared a couple months ago.
My adapted git branching model for agencies:
In this git-branching model you can see that it is significantly less complicated than the one that Vincent explained in his article.
Simpler is not always better, but in this case, my experience has found that agencies constantly struggle to keep the complex one straight, and when they try to maintain it, things break down quickly and the project becomes confused and even delayed.
Often for agencies, there is little to no actual UAT in the process, that’s just done right on a staging server while devs are scrambling to get features written before the tight deadline arrives.
So here, we combine the release candidate
and develop
branches into one.
You still MUST continually rebase as you merge down. That’s nothing I haven’t said above.
Feature branches must always be rebased (on release) before merging into release branches.
Release branch must always be rebased on master before merging into master branch.
That’s all for today!