## Why build numbers?
Recently one of our test engineers asked if it would be possible to embed an increasing build number on “About” page in our mobile app. The purpose of such build number is to have a single reference point when reporting failed test suites or new problems in issue management tool (eg. [Bugzilla](http://bugzilla.org)).
It is like a dogtag for software: useless for normal users and in daily use — but much needed in case of emergency. And crashing software is an emergency.
At office we are not using any kind of continuous integration server (like [Hudson CI](http://hudson-ci.org/)) or separate build scripts for small projects. Such servers/scripts are usually configured to create build numbers themselves. For our purpose builds are prepared either by developer or tester in development environment and shared.
## Build number requirements
Requirements for build numbers are simple:
1. *Build numbers must be unique.*
Using the same source code clone/checkout must give the same build number.
2. *Build numbers must reference a single point in project history.*
There can’t be two different checkouts giving the same build number.
3. *Build numbers must be increasing.*
Consecutive and iterative builds must show progress is build numbers. It would be best if they are strictly sequential (`N+1`) but that’s not a hard requirement.
Usual approach to build numbers includes embedding date/hour and maybe some sequentially generated number. However if you have several builds a day then date-based number is insufficient. It does not identify uniquely a code version that was used to create the target.
Thus you need something more specific.
## Solution for Git
If Subversion is your VCS of choice, you are on the green. Just bring [`svnversion`](http://svnbook.red-bean.com/en/1.1/re57.html) into play to get highest revision number from source files. You are done here.
Not so easy with Git. Git uses SHA1 cryptographic hash to “describe” each of working tree states committed into repository. Using commit-sha as a build number will fail our requirement 3 completely. Given two commit-shas it is not possible to tell which is older and which is newer. We need more.
Git scripts to the rescue. As a solution I have developed following `post-merge` script:
1 2 3 4 5 6 7 8 | #!/bin/sh DIR='./' count=`git rev-list HEAD | wc -l | sed -e 's/ *//g' | xargs -n1 printf %04d` commit=`git show --abbrev-commit HEAD | grep '^commit' | sed -e 's/commit //'` buildno=b"$count.$commit" echo $buildno > "${DIR}/subdir/version.txt" |
Then contents of `version.txt` are simply inserted into our source code.
### Detailed explanation
Lines 4 and 5 of the script are working horses; they generate parts of the build number.
Line 4 lists all commit-shas that build up revision specified by `HEAD` (see [rev-list](http://manpages.ubuntu.com/manpages/dapper/man1/git-rev-list.1.html)), counts them and print as a 4 digit number. This fulfills our requirement 3. Beware however that it is possible that two branches will have exactly the same number of commits even if they are totally different. Hence we need second part.
Line 5 extracts `HEAD` commit-sha in abbreviated form (see [show](http://manpages.ubuntu.com/manpages/oneiric/en/man1/git-show.1.html)); that is instead of using 40-digit commit-sha `show` command evaluates shortest abbreviation possible that is still unique. Since Git is versioning whole tree states (and not single files) this fulfills our requirements 1 and 2.
Combining both values (line 6) we have unique and increasing build numbers.
### Why a post-merge script?
Each developer/tester has to put this `post-merge` script into `.git/hooks` directory in his repository clone. I have tested different `post-` hooks and this one seemed best. It is executed when you `pull` from remote and source code has changed. In such case a merge must always occur: be it automatic merge, fast-forward or manual merge of conflicted code — the `version.txt` is always properly generated.
## Conclusion
This solution is not perfect. First, each developer has to manually copy `post-merge` script into git’s `hooks` directory. Second, when you merge your own branches it will be executed so you have to remember not to give private builds for testing. Third, a fully automated solution should possibly generate a build number on the repository server after commit. And last, you should probably use proper build system that will do it automatically.
However, it has proven to be sufficient for our needs. Let’s hear your opinions and feedback in comments!
2012-05-29 at 08:55
you can use ‘git log –pretty=format:%h -1′ to get last abbreviated commit hash.
2012-09-09 at 02:38
why not use : git describe –tags
2012-09-09 at 12:33
As for ‘describe –tags’ – you simply don’t tag every commit, right?
2013-01-23 at 21:04
Using ‘git describe’ will work even if every commit is not tagged (adding ‘–tags’ uses any tags instead of only annotated tags).
What ‘git describe’ gives is the last tag name and if beyond a tagged commit, also gives the number of commits since that tag followed by the abbreviated commit SHA1 id, as in “-#-g” where the ‘g’ stands for ‘git’.
If the tags are always increasing, then ‘git describe’ will give a unique and increasing version id (I use this on my project). If on two branches from the same tag, it could give the same number of commits, but the SHA1 id will still be unique.
2013-04-21 at 15:41
You could have two repos with the same SHA1 but different number of commits. All it takes is one repo to have ‘squashed’ commits from a third repo instead of merging them all as is.
The only reliable solution that I know to the versioning problem is to keep the version number in a file (under source control) and increment it before performing a commit.