How do you achieve a numeric versioning scheme with Git?

  • 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.

    As a side note, you can add the build count to the tag by tracking the build times.

    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.

  • Jon Purdy

    Jon Purdy Correct answer

    9 years ago

    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:

    v2.5-0-gdeadbee
    ^    ^ ^^
    |    | ||
    |    | |'-- SHA of HEAD (first seven chars)
    |    | '-- "g" is for git
    |    '---- number of commits since last tag
    |
    '--------- last tag
    

    Agree - it should be easy to automate nightly tag numbering if you need that, and promotion to stable is manual anyway.

    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.

    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: 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.

    @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.

    Another small improvement. If you want to have other tags as well, but use a specifically patterned tag for revision generation, you may use the `--match` option like this: `git describe --long --tags --dirty --always --match 'v[0-9]\.[0-9]'`

    @void.pointer, this highly depends on how you tag. I understand your point and am familiar with practices where integration and test team tags the source code before shipping it to a customer. In my opinion that doesn't hinder use of this approach. You may still use 'vM.m' styled tags to mark the beginning of development of a new version, and the I&T team can still use something like "Release M.m" tags to tag the release they approved for shipping to a customer. See my previous comment on matching specific tags.

    This will not work if your build artifact, e.g., RPM package, requires the build number stamped inside. This is because you will need to tag first before you can build. Automated builds are always run on the latest commit (HEAD). You cannot possibly create a tag for every commit.

    @alvinabad Two questions: 1) Why can't you create a tag for every commit? 2) Why is `git describe` insufficient for providing a build number for an untagged commit?

    @8bittree, 1) you could create a tag for every commit. But why would you do that? A tag is supposed to provide meaningful identification of a commit in history. If you tag every commit then you'll have as many tags as commits. 2) The answer provided suggested to use git describe to retrieve the build number information once the tag has been created. You'll use this to retrive the previous build number to determine the next build number to use. But this won't work if you need to stamp the build# inside a package like RPM, because you'll need to create the tag first before you can run the build.

    @alvinabad 1) A commit hash uniquely identifies a commit, but carries no information itself. A tag can provide information at a glance. (Is `cafebeef` newer than `12345678`? I'd have to go digging through git to find out. But I can be pretty sure `v2.3.4` is newer than `v2.3.3`, no need to dive into the repo.) 2) Why can you not stamp the `git describe` output as the build# inside the RPM?

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM