I’m going to be talking in this article about effective use of Git for team-based workflows. Before I really dive in, I want to point out that if you are using Git, and you haven’t read the Git book, you need to stop reading this post and go read that first. You don’t have to read all of it, but at the very least, sections 1, 2, 3 and 5 are absolute requirements. Once you’ve read and understood everything there, you’ll be ready to digest and understand the topic of this post.
Working on a software project with a team of other developers is standard practice for the industry. It’s the rare situation where just one developer works on a project at a time. Of course, when you’re working with a team, even if you’re using version control like Git, the possibility of conflicts in your code arises.
One of the biggest selling points of version-control software in general, and Git in particular, is that it provides a mechanism for dealing with conflicts when they occur, but also for avoiding conflicts in the first place. The difference is that while Git provides an “automated” solution for dealing with existing conflicts, it’s really up to the development team to create processes and workflows around Git that will help to avoid them, Git just provides us with the basic tools.
The Power Of Branches
You should already have a pretty good understanding of what branches are, and generally what they’re used for. From the perspective of the development team, branches are gold, but only if they’re used properly. Having a bunch of branches lying around on each developers local machine, and a correspondingly huge number of branches lying around on the shared remote, will lead to the same level of chaos and headache that would be present if you didn’t have Git’s branching capabilities in the first place. They key to success is having and maintain a consistent workflow.
Now, much of what constitutes a good workflow is a matter of opinion, and very much a function of the needs of your current project. However, having worked on a number of software development projects, each using Git in various, but similar ways, I think there is a good ‘middle-ground’ strategy that works as a starting point that can be built from. This strategy is based upon maintaining a few key branches, and a few rules around workflow, which I will describe here.
The Key Branches
A software project should have at it’s inception, 2 branches. The first is the default provided one, master. The second is called develop. Different strategies will call for the existence of one or more additional “core” branches, but these two are an absolute minimum requirement.
The Master Branch
The master branch represents the “production-ready” version of your code. The general rule of thumb is that you should be happy and comfortable deploying your master branch to a production server on demand, at any time. If there is code in your project that does not meet that simple criteria, it should not be in your master branch, period.
The Develop Branch
The develop branch is the stable “working branch” of your code. If you’re working on a team in a professional environment, you should be dividing your features and tasks up in to “releases” that go out on some kind of schedule. In essence, the point of the develop branch is to hold working, completed code until everything is in place for a release. The rule of thumb here is that you should be happy and comfortable having any feature or bug fix tested if it is on the develop branch. Incomplete features or unfinished fixes should not be merged in to develop.
Because there are fairly strict criteria for what kind of code is allowed within master and develop, it’s obviously necessary to have other branches that exist to facilitate version control for ongoing development effort. A general catch-all term for this type of branch is “topic branch”.
A topic branch should hold the working, in-progress code for a given discrete feature or fix. Topic branches that hold too many fixes or too many features tend to get out of hand quickly and become difficult to manage. As a rule, topic branches are named after the discrete feature being developed or the bug being fixed. In organizations where these things are tracked using issue tracking software, topic branches tend to be named after ticket numbers and descriptions within that system.
Working With Branches
Now that we have an understanding of the types of branches we’ll have in play, we can talk about how to actually use them in a logical workflow. The first thing to discuss is when and how to add code to any given branch:
Adding Code To Master
You never commit to master. _The only way to add code to the _master branch is to merge develop in to it. There is only one exception to this rule: the hotfix. A hotfix is a fix for a problem deemed too important to wait for the usual development cycle to complete. Hotfixes are risky and should be avoided at all costs, but when they do occur, you have no choice but to merge a topic branch in to master. Even in the case of a hotfix, you shouldn’t be adding individual commits directly to master.
Adding Code To Develop
You never commit to develop. There really isn’t any exception to this rule. The develop branch should only ever be updated via merges from topic branches. This helps to guarantee that no code goes in until it’s been completed and thoroughly tested in isolation.
Adding Code To Topic Branches
How often to commit code to a topic branch is a matter of personal and team preference. I’ve worked in organizations that didn’t like to see a bunch of small commits, so they asked developers to roll their commits up using interactive rebasing. You lose some history that way, but your commit log is cleaner. In other cases, organizations don’t care what the commit log looks like, so long as the work is solid. As a developer, whether you want to roll your commits up before you merge to develop or not, I highly recommend committing often. If you haven’t check in code in the last 5 minutes, you should strongly consider doing so. It’s far more common, I think, for coders to wait until they’ve reached some logical stopping point, and that’s okay, but my general advice is to try to divide your work in to small chunks so that your ‘stopping points’ occur frequently.
Okay, so we understand what the key branches are, we know when and how to commit code to them. Each of these general bits of advice can be boiled down in to a workflow that can be repeated, no matter what kind of code you’re working on, or for what reason. You can think of these as a set of ‘wetware algorithms’ for managing Git:
Developing a Feature/Fixing A Bug
The Git process for developing a feature or fixing a bug is essentially the same. This, of course, assumes that the bug is present on develop, or that it is present on master and develop, but doesn’t justify a hotfix.
- Check out the develop branch:
git checkout develop
- Pull the latest changes from the remote:
git pull origin develop
- Cut, (check out), a topic branch to hold your changes:
git checkout -b my_topic
- Checking in as often as necessary to your _topic branch, _fix the problem or build the feature.
- Push your topic branch up to the remote so that it can be code reviewed 1:
git push origin my_topic
- Once your topic_branch changes have been approved1, checkout develop and pull changes again:
- Merge your topic branch in to develop2:
git merge my_topic
- Push the newly merged develop branch to the remote so that others see the changes:
git push origin develop
- Delete your topic branch from the remote and from your local:
git branch -d my_topic && git push origin :my_topic
Developing a Feature With Multiple Topic Branches
A lot of documentation and discussion around Git workflows assumes an ideal world-state, wherein one person is able to tear off a distinct chunk of work from a feature, cut a branch from develop and go happily hacking away, while other members of the team are either working on something completely different, or otherwise are on a task that is related to, but not dependent upon, the work in another topic branch. In reality, I think it’s far more common to see Programmer A working on one part of a feature and Programmer B working on another part of a feature, each with dependent parts that aren’t ready for inclusion in develop.
In those situations, I’m an advocate of a shared topic branch. A shared topic branch works kind of like the develop branch would for a single isolated feature. The difference is that instead of each developer working on a common feature checking their topic branches out from develop, _they check out from the shared _topic branch instead.
Of course, this has an impact upon the usual workflow, such that it becomes something like this:
One thing to note about branch naming conventions when it comes to this kind of workflow. It’s not uncommon, (and is a generally good idea), to specify which branch you’ve cut your topic branch from in the name of your topic branch. Thus, if I’ve cut my topic branch from a branch called cool_feature_x, I’d name my branch something like: ‘cool_feature_x/my_specific_task’. Slashes are perfectly acceptable characters in branch names, and they can help keep things organized. This isn’t usually necessary when cutting from develop since the default assumption is that topic branches are cut from develop.
I think this just about covers most day-to-day work with Git. Git is a fantastic version control system, definitely my favorite, and it’s extremely powerful when it’s used the right way. Hopefully, this post has offered some guidance as to how and why you should develop a consistent workflow for your projects. Until next time, cheers!
1. Your organization might not actually participate in code reviews. I’m going to be highly opinionated and say that if they’re not, they should. Stay tuned for a future post on that topic.
2. I actually prefer to rebase the topic branch on develop so that my merges actually become fast-forwards, but that’s definitely a matter of preference and subject to hot debate, so I’m not making an official recommendation out of it.