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.

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 BoothMar 29 '12 at 18:39

Agree - it should be easy to automate nightly tag numbering if you need that, and promotion to stable is manual anyway.
–
UselessMar 28 '12 at 19:00

10

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 WellerMar 14 '13 at 13:00

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:

We needed the ability to have high-level versioning happen explicitly (i.e. x.y)

When parallel development was happening, we needed to NEVER generate the same version number.

We wanted to easily track down where a version came from.

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" < "1.3.10.1234").

+1 excellent suggestion with great rationale for the spec - I think I will adapt this scheme
–
kfmfe04Nov 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?
–
kfmfe04Nov 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 './setup.py 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}"'.
–
JaysonJun 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:

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.

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

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.
–
strcatJan 25 at 4:36