At vaamo we use git for version control of our main application and our code is hosted in a repository on GitHub. While we discussed our git workflow heavily in the beginning, we’ve settled for a very simple workflow for quite some time now.

Our workflow consists of the following rules:

  • Everyone in the team is working on the master-branch
  • We strive for a linear commit history on master which corresponds to the Centralized Workflow in this nice comparison of git workflows.
  • Every push to master gets built and tested by Travis
  • It’s everyone’s responsibility to recover from a failed build as fast as possible.
  • Our master is always in a state that is ready for deployment.

That’s it really.

In the following I’ll explain how we manage to keep a linear commit history and discuss why we opted for this simple git workflow in comparison to others out there.

How do we keep a linear commit history

To keep the commit history linear every developer works on the local master branch and uses rebasing to integrate upstream changes from origin/master. While we could use the handy git pull --rebase command to do just that, we typically use the smart-pull command from git-smart. In addition to rebasing, smart-pull also takes care of stashing local changes in the working directory and performs the rebase with the -p flag which tries to recreate merges if there are any in the rebase.

While most work is typically done directly on top of the local master, every developer at vaamo can create as many local branches as needed. As long as the code is rebased onto master before pushing to the central repository.

And this is basically all there is to it. Once code is pushed to origin/master it is ready to be reviewed by others on the team.

Why not use [insert-your-favorite-workflow™ here]?

The centralized workflow is very simple and hasn’t caused us any major problems during the last year. It may seem like we are not making use of the full potential of distributed version control, but git is still much better suited for a centralized workflow than e.g. Subversion would be.

Git --distributed-even-if-your-workflow-isn't

Just a couple of examples: Since everybody has a full copy of the repository you can work on-the-go, amend commits as long as they are not pushed yet and do all kinds of operations very fast locally, like figuring out who changed what and why in a file.

Feature Branches

One of the downsides of the centralized approach is that commits for a specific feature are not grouped together like they would be if we would use one branch per feature (see Feature Branch Workflow). This makes working together on a feature and discussing the relevant commits in isolation much easier (e.g. using a pull request).

Long running feature branches often require that the master branch is merged into the feature branch a couple of times until the feature is finished. If done frequently - as you should - this can result in the dreaded merge bubble, and you will have a hard time sorting out when and with what possible merge conflict resolution a given commit was merged into master.

Merge bubble as a bunch of lines in GitK Gif of guy trying to read the lines

It turns out that we don’t need that complexity yet. Our features are usually divided into managable chunks and are normally only worked on by one developer. Commits are pushed to master frequently before the task is finished and once it’s done the developer collects the relevant commits (typically five commits or less) on the relevant Trello card for review.

The first couple of commits for a feature contain code that is not yet on any active execution path. Only the very last commits contains the code that activates the feature (e.g. the user facing change in the UI or the updated dependency injection configuration). In addition if we really have a feature that should not yet be accessible in production for all users we still integrate it into master but put it behind a Feature Toggles

Gitflow

The last workflow that I want to quickly touch on is called Gitflow. It combines the idea of feature branches with release management. The master is what is currently in production while all ongoing development is integrated on a second dev branch. A release consists of merging the dev branch into master. Hotfixes are possible by branching off of master creating a hotfixbranch and then merging the hotfix branch both into master and dev.

While this workflow is very useful when the code on master is automatically deployed into production, we could achieve basically the same by using tags on the master branch as triggers for a deployment.

Exceptions to the rule: When do we branch?

All that said, we are not religious about it. We still push branches from time to time when we prototype new designs, feel like something is not quite ready to be deployed or we wrote a piece of code that we really want to get reviewed before merging into master. But these are few and far between and we try to integrate them as soon as possible

Famous last words

Commiting directly on the master branch has worked quite nicely at vaamo for the last year with a team of around five developers. It certainly requires a high amount of trust in every developer and in the tests we are writing. It is probably not the right workflow for open-source projects and as we grow we might ourselves realize that it doesn’t scale for us. What we will do at that point, we don’t know yet, but we’ll figure it out once we’re there.

As for the famous last words: “I’ll just do git push --force origin master, ok?”