Git branching and development cycle for a small team17 Nov 2017
It has been a while since I last wrote an article. I've been keeping myself busy with my work over at BookMyShow's international team to bring their android app to market. Majority of my time was spent on code refactoring which includes rewriting certain components, fixing already existing bugs and integrating new features. All this came with a tight deadline for a team of two.
We had a harrowing experience in the initial two months. We had no proper development strategy. We had a master branch where both of us pushed each and every change. There were two other branches mainitained by both of us. There was no release cycle and whenever someone requested for the latest version of the app we had to check if the master branch was working and whether all the features/bug fixes were merged.
This was no good. We were pushing and pulling from master at random times, and this has led to lots of 3-way merges and merge conflicts. We never had a stable version of the latest code at any time.
Luckily this was the time I started reading Ry's Git Tutorial. I learned a great bunch from this book. Soon I started looking into other articles which could add up to my git knowledge. I came across this article A successful Git branching model by Vincent Driessen. After getting inspired by his system, I wrote my own version for our team. I stripped some of the branching strategy since it seemed too tedious for a team of two.
We've two branches with infinite timeline, meaning they reside in the development cycle forever. They are never deleted.
Master always contain the latest deployable code. It is where the source code always reflect a production ready state. Develop contains the features which are required for the next release. When someone works on a new feature, a new topic branch is created from develop branch. When the feature is completed it's merged back to develop and the topic branch is deleted. When the develop branch is production ready, it's merged back to master.
Topic branches are typically lightweight branches that you create locally and that have a name that is meaningful for you. They are where you might do work for a bug fix or feature (they're also called feature branches) that is expected to take some time to complete.
In the master branch, the version is bumped, and a new commit is created to record the version change. Here release branch is skipped since we don't find any real value in it. I personally feel like release branch would make more sense when continuous integration is used. Now the master branch is in production ready state and a new build is made. Master is merged back to develop. Team members can now pull to keep their local branches to-date with the remote branches.
# Master branch git merge develop # Bump verson git commit -m "Update to v5.3.6" # Make a new build git checkout develop git merge master
Using git tag at this stage will be a great way to keep track of particular points in history, which in this case is various release versions. The tag will point to the commit "Update to v5.3.6" in our case.
A tag, on the other hand, is created to point to a specific commit and thereafter does not change, even if the branch moves on. In other words, tags are immutable references.
These branches have limited lifetime and are deleted once their purpose is served.
Feature branches are always branched off from develop and once the feature is complete, it's merged back to develop or are ignored if the changes are no longer required. The branch is then deleted. Feature branches should be named by the name of the feature, eg. push-notifications
# Develop branch git checkout -b push-notifications # Implement the feature and commit the changes git checkout develop git merge push-notifications git branch -d push-notifications
In certain situations we might encounter 3-way merges. When faced with so, we always abort the merge and rebase our feature branch with develop and then merge again. Having a linear git history will help with better readability and helps in modifying history at a later time. Eg. Reverting or cherry-picking a feature.
A feature branch is used not only for features, but also bug fixes which needs to be addressed within the next release. Of course there are situations where we need to make quick fix to production. In those situations we use a hotfix branch.
Hotfix branches are used for making quick fixes to production release. So they are branched off from master and are merged back to master. they should be named by the following convention, hotfix-*. Eg. hotfix-edit-profile.
# Master branch git checkout -b hotfix-edit-profile # Fix and commit the changes git checkout master git merge hotfix-edit-profile git branch -d hotfix-edit-profile
Once hotfix is merged back to master, a new commit is made which contains the version bump. After that we merge the master back to develop, and now team members can pull develop to keep their locals updated.
Current team workflow
Our team uses bitbucket for storing code. It's configured in such a way that you can't rewrite git history. This prevents force push to remote. Whatever that's pushed to remote will stay in git forever. Because of this we take great care in pushing commits. Every commit contains only changes it's supposed to contain. The makes it easier to revert changes that are not required anymore, or are accidentally merged.
Repo's root contains README file which contains the development cycle I wrote for the team. Bitbucket does a good job at displaying it in the browser. This helps anyone who joins the team to have a grasp of how our team works. We're also planning to add unit tests as soon as we're done with the app release. Having a proper structure will encourage new employees to have a positive feeling about the team and have a better time writing code. This will also provide as a base for our team to introduce better workflows in the future.
People find it easier to join an ongoing success. Show them a glimpse of the future and you'll get them to rally around.
The Pragmatic Programmer - Stone soup and boiled frogs
We use git rebase religiously and other git commands to keep a linear development history. All our git commits have proper messages and description, thanks to How to Write a Git Commit Message. We never have 3-way merges anymore and merge conflicts are non-existent. Part of this is because we merge more often and never touch each others code at the same time. Each and every commit reflects the feature or fix that we worked on. Because of this, resetting or reverting a feature won't accidentally remove any other feature.
There are no silver bullet. All this will become effective only when people follow it. This development cycle may or may not work for you. It helped my team tremendously, and we're keeping this strategy for a long time. As the team grows changes might be made. I can't tell you if my system will work for your team. But what I can tell is to have a good working knowledge of Git. Soon enough you'll be able to understand what works and what doesn't. Go ahead, and write a development cycle for your team, save it in the project root as a README and follow it like your gospel. Improve your development cycle from time to time. If you have a big team and big project, it would be great to look into continuous integration and integrate it to your workflow.