100

Let's imagine the blerp command line tool maintained on . This tool has the (hidden) --version option which returns its version (let's say 0.1.2) and another --commit which returns the commit number from which it was built.

Both the version and the commit number are hard-coded on the code base.

Now I make a bugfix then commit and rebuild my program. I will still see 0.1.2 although this new version differs from the original 0.1.2. Only the commit will tell me that it is not the same 0.1.2. Is that bugfix worth a different version number?

One solution is that each time I make a commit, I increase the hard-coded version number (which implies to always modify a minimum of 2 files for each commit). This is a binding solution and it does not work when the developers are working on different active branches. If Bob works on feature foo from the version 0.1.2 and Alice works on feature bar from the same version, how do they increase their version number? Bob can use the odd and Alice the even. What if Eve works on a third feature?

Another solution can be to use Git tags to automatically generate the version number. A script can find the closest tag starting with v such as v0.1.2 and use the tag name as the version number plus the first n digits of the current commit (v0.1.2 (build 4acd21)). This works well if the working directory is clean. One can imagine to add a * before the build number to indicate the working directory is not clean. The main problem with this solution is if somebody exports the sources, it won't be able to build blerp.

What possible alternative can solve this issue?

Josh Correia
  • 3,807
  • 3
  • 33
  • 50
nowox
  • 25,978
  • 39
  • 143
  • 293
  • 8
    Usually, you should avoid putting a version into the source files. Ideally, you would have a build process that encodes the version into the build number. That way the version is idendependent of the source used to build it with. That process can then also encode the commit id somewhere, so you always know what source is was built from. And as for storing the version number, the common solution for that is using tags. This also gives you the benefit that you can easily browse by version in your repository by looking at the tags. – poke Jun 17 '16 at 06:35
  • @poke How do you get the version number in your product if you just have the sources out of the SCM. What would be the version of `blerp`? – nowox Jun 17 '16 at 17:00
  • 1
    Usually, the thing you publish is not in the exact same state as the one in version control. So you can apply the version in your build process as I described. – poke Jun 17 '16 at 17:12
  • 2
    I know this is an old question, but I made a script that does some version management + much more: https://github.com/jv-k/bump-version.sh – jv-k Jul 16 '21 at 13:41

5 Answers5

196

Alexey Kiselev and Dario already hinted towards the answer, but I will try to explain it in detail.

Versioning Schemes

There are two types of versioning schemes:

  1. Internal version number: This can be incremented many times in a day (e.g. revision control number)
  2. Released version: This changes less often (e.g. semantic versioning)

People use different schemes as per their need, but semantic versioning is fairly widely used and authored by Tom Preston-Werner, co-founder of GitHub.

Semantic Versioning

Semantic versioning follows the pattern of X.Y.Z

Or more readable would be [major].[minor].[patch]-[build/beta/rc]

E.g. 1.2.0-beta

major or X can be incremented if there are major changes in software, like backward-incompatible API release.

minor or Y is incremented if backward compatible APIs are introduced.

patch or Z is incremented after a bug fix.

How do we achieve this using Git?

By using tags:

Tags in Git can be used to add a version number.

git tag -a "v1.5.0-beta" -m "version v1.5.0-beta"

adds a version tag of v1.5.0-beta to your current Git repository. Every new commit after this will auto-increment tag by appending commit number and commit hash. This can be viewed using the git describe command.

v1.5.0-beta-1-g0c4f33f here -1- is the commit number and 0c4f33f the abbreviation of commit's hash. The g prefix stands for "git".

Complete details can be viewed using:

git show v1.5.0-beta

Josh Correia
  • 3,807
  • 3
  • 33
  • 50
aaryan
  • 2,229
  • 2
  • 10
  • 15
35

Please, have a look at git describe command. This command shows you the latest tag and an amount of commits made after the tag was set. It also possible to show the dirtiness of the repository.

As you mentioned this command wouldn't work without the git repository (.git folder) and git installed. But it's almost unimaginable developer today without git but with all other tools installed.

Alexey Kiselev
  • 916
  • 7
  • 8
7

Revision numbers should be maintained by you, not by git. As, opposed to SVN, you don't have a incremental revision number growing on each commit, there's no way out of the box to git to contextualize what your version is.

everton
  • 7,579
  • 2
  • 29
  • 42
  • 2
    Yes. The git name is a hash derived from changes, and does not speak of the coders *intention*. Is this a releasable thing, or just a stop on the way? Git can't know. – Hack Saw May 30 '17 at 06:25
  • 3
    @HackSaw (or others at this late date) the comparison was to SVN which also does not speak of the coder's _intention_; but SVN being centralized can use a monotonically increasing counter whereas Git has to fall back on hashing to generate a unique id. – SensorSmith Feb 28 '20 at 22:10
5
  1. Create file in project dir (or where you want) build_number and put the value 1 in this file

  2. Go to git dir and create file called "pre-commit" in .git/hooks/ (without .sample)

  3. Put this code there

#!/bin/sh

currentbuildnumber=`cat build_number`
let "currentbuildnumber++"
printf $currentbuildnumber > build_number
currentbranch=`git branch | tr -cd "[:alpha:]"`
git log $currentbranch --pretty=format:"%h - %an, %ar : %s, Build: $currentbuildnumber"

Working for me :) enjoy!

Bondolin
  • 2,793
  • 7
  • 34
  • 62
chainable
  • 99
  • 1
  • 3
  • 2
    This still has the same issues as described by the OP, if you have 3 people working on 3 different branches they may end up with the same build and / or semantic version. – Lukas Willin Oct 07 '22 at 14:07
  • Thank you for this answer. Note that the "pre-commit" must be executable. Otherwise, git prints: "hint: The '.git/hooks/pre-commit' hook was ignored because it's not set as executable." – Alexander Lubyagin Feb 21 '23 at 07:26
4

As you say, versioning issues are usually solved in git using branch and tags(like the semantic versioning pattern).

The better way is to use git to track only changes in the codebase, ignoring (using .gitignore file) builds files and maintaining a clean repository.

Builds results (pre/compiled files, executables files, distribution files, zips, exe...) could depend on environment variables (platform, system arch, etc) and should be keep separate in a registry.

If the codebase is very big and hard to maintain, maybe you should consider dividing it into smaller components (or git submodules) to avoid cross-dependencies at development time.

Josh Correia
  • 3,807
  • 3
  • 33
  • 50
Dario
  • 3,905
  • 2
  • 13
  • 27