Simple git workflow at vaamo
Originally published on the Codecraft at Vaamo Blog
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.
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
.
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
hotfix
branch 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?"