A product version, such as v1.0.0.100, represents not only a unique production release of software, but helps identify feature sets and hotfix stages for said product. Right now I see two ways to maintain the final package/build/binary version of a product:

  1. Version Control. A file somewhere stores the version number. Continuous Integration (CI) build server will have a script to build the software that uses this checked-in version number to apply it to all areas of the software needed (binaries, installer packages, help pages, documentation, etc).

  2. Environment and/or build parameters. These are maintained outside of version control (i.e. they are not tied to the snapshot/tag/branch). The build scripts distribute and use the number in the same way, however they just obtain the value differently (it is provided to the build script, instead of having the script know where to get it relative to the source tree).

The problem with the first approach is that it can complicate merges across mainline branches. If you still maintain 2 parallel releases of the same software, you will resolve conflicts when merging between the two mainlines if the version has changed on both since the last merge.

The problem with the second approach is reconciliation. When you go back to a release 1 year ago, you will rely solely on the tag information to identify its release number.

In both cases, there might be certain aspects of the version number that are not known prior to the CI build. For example, a CI build may programmatically put in a 4th component that is really the automated build number (e.g. 140th build on the branch). It might also be a revision number in VCS.

What is the best way to keep up with a software's version number? Should the "known" parts always be maintained in VCS? And if so, are the conflicts across mainline branches an issue?

Right now we maintain our version number via parameters specified and maintained in the CI build plan (Atlassian Bamboo). We have to be careful before merging to our master branch that the version numbers are properly setup in advance of the CI build kicking off. With regards to the Gitflow workflow, I feel that if the version number were tracked in source control, we could guarantee it is setup properly when we create our release branch in preparation of the release. QA would perform final integration/smoke/regression testing on this branch and upon signoff, a merge to master takes place which signals commitment to release.

  • 3
    Is a merge conflict between two versions of a file version.txt where one version contains the single line 1.0.7 and the other 1.2.0 really that hard to resolve? If this is the only conflict in merging two branches that went apart, I'd consider myself very lucky. How often does it occur? If it does occur, isn't it a good thing that you are forced to think about what version number the merged version should have? (Sorry for the ambiguous use of the word “version”.)
    – 5gon12eder
    Commented Aug 17, 2015 at 22:29
  • 1
    @5gon12eder The difficulties or feelings about the merge itself is irrelevant. It's just a negative aspect of the overall solution. Commented Aug 18, 2015 at 1:34

2 Answers 2


Personally, I choose option 3: keep versioning information in VCS metadata, specifically, tags.

Git makes it very easy to do so, because there is a command git describe, which can uniquely describe a commit based on a tag. Here's how it works:

  • If the current commit is tagged, output the name of the tag.
  • Otherwise, walk the history backwards until you find a tag and then output a description in the following format: <tag>-<number of commits since the tag>-g<abbreviated commit hash>.
  • If there are uncommitted changes in the workingtree, append -dirty.

So, if you are doing a release build, and have the commit tagged 1.2.3, it will output 1.2.3. If you are currently working on 1.2.4 and you made 4 commits since 1.2.3, and have uncommitted changes in the tree, it will output 1.2.3-4-gdeadbee-dirty.

This is guaranteed to be unique and monotonic, as well as human-readable, and thus can be used directly as a version string. The only thing you have to ensure is a proper naming convention for tags.

  • I really love this idea, but this seems difficult to manage with JIRA + Bamboo. Bamboo only functions on branches, not tags, so you'd have to make sure a tag is pushed before a build is generated. This is error prone. Commented Aug 18, 2015 at 1:43
  • 1
    git describe also works with branches: "--all – Instead of using only the annotated tags, use any ref found in refs/ namespace. This option enables matching any known branch, remote-tracking branch, or lightweight tag." Not sure how this works with Bamboo, though. (This will of course require careful naming of branches, just like the normal mode does with tags.) Commented Aug 18, 2015 at 1:58
  • 1
    I know some folks which do completely automatic releases from Git. The version string is built by git describe, the ChangeLog is generated from git shortlog (well, actually from a script which parses the output of git log --pretty=tformat:<some custom format string>), and the release notes are generated from the tag description and git notes attached to important milestone commits. Commented Aug 18, 2015 at 2:05
  • My point is, that the tag would need to be created in advance of the release so that commits after it are properly versioned with a base number. This goes against the principle of tagging during or at the time of release. Bamboo picks up builds automatically based on commits to master (from develop, remember I'm using gitflow). What if someone pushes a merge to master without a tag? It won't use the proper version (in fact it would use the version of the last release) Commented Aug 18, 2015 at 2:07
  • If you use a scheme like this, tagging is releasing. Ah, I see what you're getting at, I think. So, currently, your CI server is the release driver, and with this change, the SCM is the release driver, but you would like it to remain that way? Commented Aug 18, 2015 at 2:13

Yes. It is good practice to keep most of the version number in vcs. If we consider semantic versioning semver.org where we have major.minor.patch.build the first three must live in vcs. The last one can be a incrementing number from your build server used to backtrack the specific commit that a binary is made from.

To facilitate this in .NET we have made a small cmd line exe that is committed to git. With a pre-build event, it picks up the build number which teamcity tagged during build. This cmd line tool auto generates a class with one constant containing the build number. The rest of the version number: major.minor.patch is just a normal constant in another file. These two shared files are included in every assembly in a solution as a link(alt+shift-drag).

This approach is powerfull enough that we can have teamcity build and test our code. Push to azure and have kudu build it again but with the teamcity build number as the version of the dll's.

  • 1. Why is it good practice? You have described what you guiys do, but not why it's better to do things this way. 2. You seem to be contradicting yourself. If the version number contains a build index / build number indicator, then you're not keeping the unique version number in the VCS, only part of it.
    – einpoklum
    Commented Aug 7, 2022 at 13:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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