Semantic versioning with Drupal and Git Flow

An organized git branching model is essential for managing complexity on long-running Drupal projects. Without it, chaos ensues.

In this post, I’ll outline an approach for how to use the popular git flow methodology along with semantic versioning to organize your Drupal project. While this post is targeted towards Drupal 7, the same technique can easily be used with Drupal 8 or 6.

Git workflow

On small / medium Drupal projects, I’ve found that a simple GitHub Flow suffices. What is GitHub Flow? In a nutshell: whatever is merged into master is deployable; features or bug fixes should branch off master, and when they are ready, merged into master and deployed. Simple. And as the author of GithHub Flow states:

Git itself is fairly complex to understand, making the workflow that you use with it more complex than necessary is simply adding more mental overhead to everybody’s day. I would always advocate using the simplest possible system that will work for your team and doing so until it doesn’t work anymore and then adding complexity only as absolutely needed.

If GitHub Flow sounds like it will suit your needs, then you don’t need to finish reading the rest of this. But for larger projects with multiple developers and a longer timeline for development, read on.

Git flow

A great overview of git-flow is outlined in this document.

The summary is:

  1. Branch off of develop for feature development. When the feature is ready for client review and contains non-breaking changes, merge into develop
  2. When it’s time to release code to production, branch off of develop and create a release/* branch. Increment versions.
  3. When the release is ready, merge into master and tag a release. Checkout the tagged release in production. Merge the release back into develop.
  4. If an urgent issue or bug needs to be addressed, a hotfix branch can be created off of master, but hotfix branches should not be used to push features more rapidly, only to deal with bugs or security issues.

Typically, we’d then have tagged releases from master deployed to production while develop, release/* or feature/* branches would be deployed to dev/staging environments for peer review.

You’ll note that in step #2 above, git flow requires us to increment versions for a new release.

Well, how should we decide what version numbers to use?

Semantic versioning

One of the nice improvements in Drupal 8 is the adoption of semantic versioning for Drupal core. Semantic versioning is the idea that you can understand the significance of a project update by looking at the version number. From

Given a version number MAJOR.MINOR.PATCH, increment the:

  • MAJOR version when you make incompatible API changes,
  • MINOR version when you add functionality in a backwards-compatible manner, and
  • PATCH version when you make backwards-compatible bug fixes.

How can we use this in our Drupal project? Using the following notation: 7.x-MAJOR.MINOR-rcPATCH. So, if you have a custom module like myproject_deploy, its version number might look like 7.x-1.0-rc0. Let’s add some bug fixes: 7.x-1.0-rc1. Ok, how about new functionality in a backwards-compatible manner? 7.x-1.1-rc0. And if we make incompatible API changes? 7.x-2.0-rc0. Simple enough.


When I’ve used this workflow, I use the policy of updating every custom module, theme and install profile’s version number when creating a new release. In addition to making it clear which version of a site’s code you are using, this also means we can standardize hook_update_N() implementations across all custom modules.

Now, we don’t want to increment every custom package version by hand, so let’s use a Drush script to automate the process. Now, whenever git-flow prompts you to bump versions, you can run drush @myproject.local php-script /path/to/bump-versions.php.

Questions or comments? Let me know on twitter.