Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

My organization is considering moving from SVN to Git. One argument against moving is as follows:

How do we do versioning?

We have an SDK distribution based on the NetBeans Platform. As the svn revisions are simple numbers we can use them to extend the version numbers of our plugins and SDK builds. How do we handle this when we move to Git?

Possible solutions:

  • Using the build number from hudson (Problem: you have to check hudson to correlate that to an actual git version)
  • Manually upping the version for nightly and stable (Problem: Learning curve, human error)

If someone else has encountered a similar problem and solved it, we'd love to hear how.

share|improve this question
Could you get your hudson (not jenkins?) server to automatically add a git tag after each successful build? This would have the added advantage that it makes it really clear which git commits have build issues or test failures, since they would remain un-tagged. – Mark Booth Mar 29 '12 at 18:39
see… – CharlesB Mar 30 '12 at 21:00
As a side note, you can add the build count to the tag by tracking the build times. – Shahbaz Jul 9 '14 at 9:07
Not sure if a viable solution, but how about exporting from git to a svn repo right before every build? Then just build from the svn repo - if centralized is what we want, just use that instead. – Jonny Dec 4 '15 at 20:54
up vote 88 down vote accepted

Use tags to mark commits with version numbers:

git tag -a v2.5 -m 'Version 2.5'

Push tags upstream—this is not done by default:

git push --tags

Then use the describe command:

git describe --tags --long

This gives you a string of the format:

^    ^ ^
|    | |
|    | SHA of HEAD
|    |
|    number of commits since last tag
last tag
share|improve this answer
Agree - it should be easy to automate nightly tag numbering if you need that, and promotion to stable is manual anyway. – Useless Mar 28 '12 at 19:00
Small improvement: git describe --long --tags --dirty --always. 'Dirty' will tell you if there were local changes when the 'describe' was done (meaning it can't fully describe the state of the repo). 'Always' means you won't get an error when there are no tags. It will fallback to just a commit hash. So you can get 76001f2-dirty as an example. Obviously, seeing 'dirty' means somebody messed up. – Mike Weller Mar 14 '13 at 13:00
How can this work when the tag is generated last. Normally you want builds going forward to have the next version of your product. But they will always be forced to use the last version in this case. Only the final, shipped build will have the proper number. – void.pointer Aug 20 '15 at 21:34
@void.pointer: Sure, this version number answers the question “which release was this commit based on?” not “which release will this commit be in?” However, you’re free to interpret tags differently. For example, if you tag HEAD as v2.5, you can just as well interpret that as the start of the 2.5 release cycle, then tag v2.5-release or whatever you like. – Jon Purdy Aug 20 '15 at 22:44
@JonPurdy Tags are not traditionally used to define a "starting" point, however. In any case, the start will always be the commit tagged as the last release. So I don't see a reason to tag a commit 2 times for this purpose. – void.pointer Aug 20 '15 at 23:23

This has come up on a few projects for me. The best solution I've had so far is to generate a version number like this:

x.y.<number of commits>.r<git-hash>

Typically, it's generated by our build system using a combination of some static file or tag to get the major revision numbers, "git rev-list HEAD | wc -l" (which was faster than using git log), and "git rev-parse HEAD". The reasoning was follows:

  1. We needed the ability to have high-level versioning happen explicitly (i.e. x.y)
  2. When parallel development was happening, we needed to NEVER generate the same version number.
  3. We wanted to easily track down where a version came from.
  4. When parallel lines were merged, we wanted the new version to resolve higher than either of the branches.

Number 2 is invisible to most people, but is really important, and really difficult with distributed source control. SVN helps with this by giving you a single revision number. It turns out that a commit count is as close as you can get, while magically solving #4 as well. In the presence of branches, this is still not unique, in which case we add the hash, which neatly solves #3 as well.

Most of this was to accommodate deploying via Python's pip. This guaranteed that "pip install" would maybe be a bit odd during parallel development (i.e. packages from people on different branches would intermingle, but in a deterministic fashion), but that after merges, everything sorted out. Barring the presence of an exposed rebase or amend, this worked quite nicely for the above requirements.

In case you're wondering, we chose to put the r in front of the hash due to some weirdness with how Python packaging handles letters in version numbers (i.e. a-e are less than 0, which would make "1.3.10.a1234" < "1.3.10" < "").

share|improve this answer
+1 excellent suggestion with great rationale for the spec - I think I will adapt this scheme – kfmfe04 Nov 16 '12 at 16:34
btw, how did you deal with the chicken-egg problem of determining the git-hash before you check it in? Did you use some form of .gitignore or some other trick? – kfmfe04 Nov 16 '12 at 23:58
I didn't. I don't use the hash until package build time, which is long after check-in. Different languages have different ways to inject this. For Python, I use './ egg_info -b ".${BUILD_VERSION}" sdist'. For C and C++, I define a macro at compile time with 'CFLAGS=-D "${BUILD_VERSION}"'. For Go, I define a symbol at link time with 'go install -ldflags appmodule.BuildVersion"-X .${BUILD_VERSION}"'. – Jayson Jun 12 '13 at 23:12

Versions are identified hashing the SHA1 hashes of all the files in the stored directory tree at the time of checkin. This hash is stored alongside the hashes of the parent checkin(s) so that the full history can be read.

Take a look at the process of using 'git-describe' by way of GIT-VERSION-GEN and how you can add this via your build process when you tag your release.

Here is a nice blog that gives an example of how to get what you want:

share|improve this answer

This might be a bit overkill, but I'll let you know how we do it.

We use a branching structure very similar to this.

Hudson builds off our "develop" branches and increments build numbers starting from 0. The build number is unique to each project and gets tagged in version control. The reason is so that you can tell exactly which develop branch build 42 came from, for example (each project can have several develop branches in parallel, because each project can have several teams working on different aspects of the project).

When we decide that a particular build is good enough to be released, the commit that triggered that build gets tagged with a release version number, which is decided by marketing. This means that the dev teams don't care about what the final version number is and marketing is free to shuffle around version numbers as it sees fit. The final version number and build number are both present in the released product.

Example: 2.1.0 build 1337

This means, for a specific product release, you can tell which was the last team to have worked on it and you can query git for all the commits leading up to release to diagnose a problem if you need to.

share|improve this answer

Pro Git in section 7.2 "Git Attributes" in "Keyword" Expansion part contains a nice example of using smudge&clean filters for generating RCS-style keywords. You can use the same technique for embedding some-version-string into code, formatted and calculated according to your rules. You still can use git describe as a staring point, but you have the possibility to transform to any more appropriate form and get from v2.5-14-feebdaed, for example, clean 2.5.14

share|improve this answer
-1 for ruining a good answer with completely uncalled for ad hominem attacks. – Jörg W Mittag Mar 29 '12 at 0:52
Who's to say it was git-boys voting you down. It could easily be people who prefer a little civility. – Mark Booth Mar 29 '12 at 18:19
FYI, I've just edited the answer. – Keith Thompson Jun 5 '12 at 2:38
git describe outputs the tag name unless --long is passed or there are commits since the last tag, so it's perfectly clean already. If you weren't changing the defaults, it would have given you exactly what you wanted. – strcat Jan 25 '15 at 4:36

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.