Changeset based Build Numbering


Here at Teamprise we've recently changed our build numbering scheme.  We use the traditional Major.Minor.Revision.Build approach with Major.Minor.Revision being the only thing an end user should care about and Major.Minor the only thing we would every do any marketing for.  The build number used to be an integer that incremented everytime we did a build.  On day one it started at 1 (i.e. and when we got to the final 2.0 build it was 2058 (i.e. 

Internally, we have several different build types.  For example, we have a build for development which runs really fast but is missing steps like code obfuscation.  The "Release Build" is the build that compiles, generates documentation and obfuscates all the code just as we would distribute it to customers and would performs all of the automated tests etc.  Release Builds are the ones that our internal QA folks use to test against.  We used to label when we made a build so that we could tie that build to the exact versions of all the files included in it.

During a development cycle we typically update the versions we use to develop with fairly regularly (we use Teamprise to develop Teamprise, what is commonly referred to as dogfooding).  Every month or so we issue a preview releases to brave customers who have asked for a particular feature so that they can test them and let us know how we are getting along.  When the time comes to ship we flag a certain release build as our "release candidate" and then we start to hammer it as much as we can, trying to break it.  We also ship these release candidates to customers who want them.  We build up a list of bugs or features we would like to fix and then we triage them to decide which ones we will fix before we ship.  We then fix, re-build and test until we are happy with a release candidate and then we release it.  Typically we have 2 or 3 release candidates before we have a release we will sign off on.

Finally, when we decide which version is our RTM build (release-to-manufacture) we branch our mainline of code to create a branch for that release.  In Team Foundation Server you can create a branch from set of labeled files so this works well.  We can in effect branch after the fact, once we know what our shipping version was.  Development for the next version carries on in trunk.  If we find an issue or a bug that needs a fix applied to the previous release we can fix it in that branch and merge the changes into trunk.

Then we had a brain-wave.  I'm not sure exactly who's idea this was in the team, I think possibly several of us had it at the same time - and googling around this idea isn't particularly original.  Anyway, we now use the last changeset number for the $/Teamprise path in our Team Foundation Server code repository as the build number. We then append on to this a one letter code for the build type (for example, looking at the build server the latest release build is where R stands for Release). 

In Team Foundation Server, the changeset number is an incrementing number that gets assigned to every transaction of change to the version control system.  Each changeset number therefore represents a precise state of the files in version control on that server instance.

The advantage of this is that if I want to get the source code that exactly matches the state of play at the time of that build I can do a Get Specific... and ask for changeset 5113.  Additionally, I can lookup changeset 5133 in TFS and get the exact time and date that this change was made.  Also - if I want to compare the differences between builds this becomes trivial (again by knowing the changeset numbers).  It also means that we do not have to create labels for every build.  While labeling in TFS is fairly cheap, no point doing it if we don't have to.

Anyway.  I am so taken with the changeset build numbering approach that I thought I would mention it here.  While we happen to be building JAR files for our Java code, the same process can easily be applied to .NET assemblies etc.  I'm also going to be modifying the CruiseControl.NET integration to TFS to make versioning based on changeset numbers easier.  Stay tuned for details.


We do the incrementing value as well, in a header file that gets modified and checked in as the first part of the build step (we're a C++ shop). My question is how do you get the build number into the product?


In the java world it is really easy. We get the changeset data into a variable in Ant and then we use that variable to do a number of substitutions in .properties files but also to name the jar files. Eclipse uses a naming convention to indicate which is the latest version (which works very similar to the 0.0.0.qualifier system used by .NET Assemblies.

So, to answer your question when we do the check-out of the files we then do a string substitution inside those file replacing it with the build number that we have generated.

Not sure if that helps in your C++ world. I'm afriad I get scared now-a-days when I have to work in unmanaged code...

Okay, so you do change the source after it is retrieved from source control. That's the main think I was wondering about. I suppose we could also hook it up using an environment variable. We already use an environment variable to control whether or not the build is an "official" one.

I wonder if the Vault command line client would return this value for us. I don't see anything obvious in the help. I'll have to do some digging.

This would result in a huge inflation of our build numbers... we hit a transaction number of 100000 back in December (we have history in our tree for over 10 years, it was imported from VSS). I'm guessing that would really confuse some people. Thanks for the idea.

Oops, "the main thing", not "the main think".


I'd have to ask my colleagues over on the SourceGear side of the fence about the usage of the Vault command line as it is something I don't do every day.

I would think that possibly something like "vault HISTORY -rowlimit 1 pathToProject" might give you enough to be able to parse out the changeset - however I'd have to ask them.

Good luck,


I was using this same approach for my projects at work, until we ran into the UInt16 limit of .NET version numbers (which didn't take long at all, since the changeset is global to the entire server, and we have hundreds of project teams using the same server).
Now we have to do some ugly modulo math and split the changeset number between the Revision and Build.

Hi Martin,

You mentioned: "I'm also going to be modifying the CruiseControl.NET integration to TFS to make versioning based on changeset numbers easier." Do you still have plans to do so?

Thanks, Vladiv

Leave a comment


Creative Commons License
This blog is licensed under a Creative Commons License.