Hello Git user. In this blog post I will discuss a technique for a unique version calculation for every Git commit.

You may ask why  we need this, after all every commit in Git is identified by a unique sha1 hash. That’s right, let’s take 2 commits, 4bd92c9 and f5fc029, use their sha1 hash as a version and perform a simple A/B test. The test showed that 4bd92c9 is preferred to f5fc029.

If this is the case, how can we tell:

  • Which version is newer?
  • If 4bd92c9 is included in f5fc029, or vice versa?
  • What branch they were built from?

It seems we need an alternative.

The common standard for the versioning is a SemVer scheme.

We will use its parts as follows:

  • Major – manual increment
  • Minor – every released feature will increment the minor
  • Patch – will always be 0

 

Now let’s take a look at our Git graph:

 

 

We will give a version number for every mentioned commit:

  • M1 – 1.0.0: our init major version
  • M2 – 1.1.0: some new feature released
  • M3 – 1.2.0: feature B released
  • M4 – 1.3.0: feature A released

But what about A1,B1? We need to give them a version number as well in order to identify these builds for various purposes like automation test, deploying on GA environment, etc.  According to the semver rules we need to mark these versions as a next minor pre-release, so we get:

  • A1 – 1.1.0-A-5
  • B1 – 1.2.0-B-3

Let’s put the version numbers in the graph:

 

Now let’s automate it by using the following logic. If you are on the master branch calculate the version by:

latest=$(git tag  -l --merged master --sort='-*authordate' | head -n1)

latest=$(git tag  -l --merged master --sort='-*authordate' | head -n1)
semver_parts=(${latest//./ })
major=${semver_parts[0]}
minor=${semver_parts[1]}
patch=${semver_parts[2]}

version=${major}.$((minor+1)).${patch}

Put the tag on a HEAD

 

If you are on the feature branch just calculate the version (without putting a tag) by:

latest=$(git tag  -l --merged master --sort='-*authordate' | head -n1)

latest=$(git tag  -l --merged master --sort='-*authordate' | head -n1)
semver_parts=(${latest//./ })
major=${semver_parts[0]}
minor=${semver_parts[1]}
patch=${semver_parts[2]}

branch=$(git rev-parse --abbrev-ref HEAD)

count=$(git rev-list HEAD ^${latest} --ancestry-path ${latest} --count)

version=${major}.${minor}.${patch}-${branch}-${count}

 

Combining all these together we get the following script:

#!/bin/bash

branch=$(git rev-parse --abbrev-ref HEAD)
latest=$(git tag  -l --merged master --sort='-*authordate' | head -n1)

semver_parts=(${latest//./ })
major=${semver_parts[0]}
minor=${semver_parts[1]}
patch=${semver_parts[2]}

count=$(git rev-list HEAD ^${latest} --ancestry-path ${latest} --count)
version=""

case $branch in
   "master")
      version=${major}.$((minor+1)).0
      ;;
   "feature/*")
      version=${major}.${minor}.${patch}-${branch}-${count}
      ;;
   *)
      >&2 echo "unsupported branch type"
      exit 1
      ;;
esac

echo ${version}
exit 0

 

Originally Published:

Start Your Taboola Career Today!