Continuous Deployment, Versioning and Git

So you have heard about continuous delivery and continuous deployment and you are eager to try, but your manager is afraid that you will lose traceability of the project. How can frequent updates of your binaries be tracked in a rapidly progressive environment, where every commit is a potential release?

Artifact Version vs. Commit

The traditional Java community way of version handling with a three digit number, potentially followed by a build number or a qualifier, e.g. 1.2.3.RELEASE, works well if you have long development cycles with the occasional bugfix release in between. The problem is that it does not scale when the frequency of releases increases by a magnitude or two. Imagine what your version control system would look like if every commit had its own release branch, or if you manually had to update your dependencies several times daily. Consequently, the notion of snapshot releases has been adopted to indicate that you are working with a moving target. The tradeoff is that the traceability is lost, because one snapshot version refers to many different versions of the software.

Different workarounds that allow developers to identify specific snapshots have been introduced to overcome this problem. For example, Maven adds a timestamp when the artifact is deployed to a repository and Jenkins can generate a fingerprint for a binary. Both these methods enable tracking of the artifact version which is good, but they do not tell the complete story. Once you have pinpointed the specific artifact, you need to consult the build log to find out the corresponding commit before you can checkout the code and start debugging.

Wouldn’t it be nice if you could get the correct source code from the version control system immediately, without the detour to the build log? With Git, a 40 digit SHA-1 hash sum is calculated for each commit, making it the perfect candidate to add traceability to your project.


It turns out that the buildnumber-maven-plugin is the tool for the job. Unfortunately, the documentation on its web page is quite scarce, but we can get some more information by executing the plugin itself:

$ mvn buildnumber:help
This mojo is designed to give you a build number. So when you might make 100 builds of version 1.0-SNAPSHOT,
you can differentiate between them all. The build number is based on the revision number retrieved from scm.
You can access the build number in your pom with ${buildNumber}.

Looks promising, we add the following lines to the <build><plugins> section in our pom file:


And then we attempt to validate the project:

$ mvn validate
[ERROR] Failed to execute goal org.codehaus.mojo:buildnumber-maven-plugin:1.1:create (default)
Execution default of goal org.codehaus.mojo:buildnumber-maven-plugin:1.1:create failed:
The scm url cannot be null.

Ok, the plugin seems to be looking for a <scm> configuration tag in the pom (to be more specific, it is actually looking for a <scm><connection> or a <scm><developerConnection> tag), let’s add one. In order to do that, you also need to add the Git URLs to your repository:

    <!-- Replace the connection below with your project connection -->

Revalidate the project:

$ mvn validate
[INFO] Storing buildNumber: 8ceb5be9376a6ebe34e90d78f57f524d123437a9 at timestamp: 1333643345784

Let’s compare with the current Git SHA-1:

$ git show -s
commit 8ceb5be9376a6ebe34e90d78f57f524d123437a9

Excellent, now we have a Maven ${buildNumber} variable with the current Git SHA-1 value that we can use in our project!

Now, the question arises how we can utilize the ${buildNumber} in the best way. The most important thing is to advertise it to your users, let them be a developer of a dependent module, a tester at the Q/A department, a remote client application, a random Internet user or even yourself. Depending on the project type, different suggestions come to mind. I have created a project on GitHub with working implementations of the examples below.

Manifest Entry

If your users have access to the binary artifact, it can make sense to add the build number as an entry in the project’s MANIFEST.MF file. For example, the maven-war-plugin can be configured to add a Git-SHA-1 entry for a .war project:


Similarly, you can configure the maven-jar-plugin to add manifest entry for a .jar project.

Note, if your artifact is signed, you should not use the key SHA1-Digest as the manifest entry, because it is used during signature validation.

Properties File

Alternatively, a simple file may suit your need:


The file must be filtered so that the value gets assigned:

        <!-- You may have a different path for your properties file -->

Static Method Call

If you implemented the property file solution above, you might as well create helper class that wraps the build number in a static method call:

public class PropertiesFileReader {

    private static final Properties properties;

    /** Use a static initializer to read from file. */
    static {
        InputStream inputStream = PropertieFileReader.class.getResourceAsStream("/");
        properties = new Properties();
        try {
        } catch (IOException e) {
            throw new RuntimeException("Failed to read properties file", e);
        } finally {
            if (inputStream != null) {
                try {
                } catch (IOException e) {
                    // Ignore

    /** Hide default constructor. */
    private PropertiesFileReader() {}

     * Gets the Git SHA-1.
     * @return A {@code String} with the Git SHA-1.
    public static String getGitSha1() {
        return properties.getProperty("git-sha-1");

Web Service Resource

Sometimes your users do not have direct access to your artifacts, but don’t let that stop you from sharing the build number. With a little [insert your favorite web framework here] magic, you can publish the build number as a web service resource. Below is an example of a Spring Controller:

public class BuildNumberController {

    @RequestMapping(value = "/git-sha-1", produces = MediaType.APPLICATION_JSON_VALUE)
    public Map<string, string=""> getGitSha1() {
        String gitSha1 = PropertiesFileReader.getGitSha1();
        return Collections.singletonMap("git-sha-1", gitSha1);

Put into practice:

$ curl localhost:8080/build-number/git-sha-1

Web Projects

If you are a front-end web developer, one option is to embed the build number in the webpage itself, such as in an ever popular about box. A more subtle way is to inline it as an html comment in the index.html file:

<-- Git SHA-1: ${buildNumber} -->

Since we are dealing with a web project, the filtering works differently:

        <span class="c"><!-- You may have to change the path below if your index.html is located elsewhere -->

Wrap Up

Given that you now have a tool for managing artifact versions, should you stop making formal releases of your software? Front end web development is one area where the traditional release management may be questioned. The build server can continuously deploy successful builds to production and yet it is easy to find out exactly which commit that is currently used. Framework development on the other hand is a completely different story, considering the nature of the Java ecosystem where all dependency management rely on stable releases. Additionally, if you need a build number in a human readable format, the Git SHA-1 with its 40-digit string is probably not what you are looking for. The good news is that one solution does not exclude the other. If you would like beta testers to verify critical bug fixes or developers to start using the latest bleeding edge features in a snapshot release, you can benefit from using both the traditional version numbering as well as a commit specific identifier.



Oct 15th, 2012: Updated the blog post to include the version 1.1 of the buildnumber-maven-plugin because the 1.0 version had a bug that caused it to fail if executed from another process, e.g. within an IDE. Additionally, the reference project was updated to include all the latest versions of the dependencies and plugins.

Mattias Severson

Mattias is a senior software engineer specialized in backend architecture and development with experience of cloud based applications and scalable solutions. He is a clean code proponent who appreciates Agile methodologies and pragmatic Test Driven Development. Mattias has experience from many different environments, including everything between big international projects that last for years and solo, single day jobs. He is open-minded and curious about new technologies. Mattias believes in continuous improvement on a personal level as well as in the projects that he is working on. Additionally, Mattias is a frequent speaker at user groups, companies and conferences.

This Post Has 20 Comments

  1. Mikael Karlsson

    Interesting stuff and nice examples!

  2. Sune Simonsen

    Thanks that was exactly what I was looking for – glad that the information came from Jayway :-)

  3. robbiepl

    use Manifest class…

    final InputStream manifestStream =
    Manifest manifest = new Manifest(manifestStream);
    System.printf(“%1$-20s | %2$s%n”,”git-sha-1″,manifest.getMainAttributes().getValue(“git-sha-1”));

    1. Mariusz Przydatek

      Slight modification to pull the manifest file based on the request’s (HttpServletRequest) context. This way you’ll make sure you’re reading always the correct manifest file.

      ServletContext context = request.getSession().getServletContext();
      InputStream manifestStream = context.getResourceAsStream(“/META-INF/MANIFEST.MF”);
      Manifest manifest = new Manifest(manifestStream);

      Thanks for pointing in the right direction.

    1. Mattias Severson

      @Aniruddh: Glad you liked it! Please note that I have just updated the blog to include the 1.1 version of the buildnumber-maven-plugin, because I encountered this bug in a project recently. Luckily, the problem could be solved easily by just bumping the version number.

  4. Mariusz Przydatek

    Fantastic post!!

    1. Mattias Severson

      @Nikolay: Thanks! Good to know that you found the information useful and that you could solve your problem.

  5. KK

    I am having issues where i am not getting the build number and timestamp. the fields are empty in the manifest file. i have updated version but still does not work

    1. Mattias Severson

      Did you try using the example project at GitHub? The manifest entries in the above example are generated by the maven-war-plugin. If your project is a jar, then you must configure the manifest of the maven-jar-plugin instead.

  6. LinkTree

    Great post!
    I know its a bit old but I was looking for this and it really solve my problem.

    I’m using the filter to add parameter to my js files (to make sure that in each build the browser gets them new) and I was wondering if there is an easy way to take only 5 or 6 chars from the hash.

    Do you know a way?

    Thanx again for the great post,

    1. Mattias Severson

      @LinkTree: Thanks!

      Since you are using JavaScript, one option would be to use substring, e.g.

      var shortBuildNumber = "${buildNumber}".substring(0, 5);

      Alternatively, you can configure the shortRevisionLength of the buildnumber-maven-plugin, see the documentation (scroll down), e.g.

  7. Chris

    nice post.
    In a CD environment, you would ideally also like to use the commit id in your artifact naming scheme but this is not possible with the build number plugin.
    I wonder how you solved that problem?

    1. Mattias Severson

      @Chris: The versions-maven-plugin (specifically the versions:set goal) allows you to specifically set the project <version>, and you can pass the ${buildNumber} as an argument. However, by doing so you will break the Maven version number format which is fine, as long as you are aware of the trade-offs:

      • It will not be possible to tell which release is the latest just by looking at the <version> tag, neither by humans, nor by tools like the Maven versions:display-dependency-updates goal.
      • If you are using an artifact repository such as Nexus or Sonar and you only have little disk space, you should consider to configure it to discard old artifacts. In case an old build is requested, you can always pull out the corresponding source code from the version control system and re-create your artifact.
  8. Beau Jackson

    Very nice example. Thank you!

  9. Eduardo Ribeiro da Silva

    Great article Severson, buildernumber-maven-plugin is really helpfull.
    But I don’t know how to resolve a specific situation:
    When I build my project with JENKINS, all works fine: The plugin upgrade correctly, but it does not commit+push in git repository, and buildernumber became outdated.

    Anybody has an idea to resolve this problem?

    1. Mattias Severson

      @Eduardo: Thank you.

      You cannot put the value of a sha in a file that is being versioned controlled, it is a chicken and egg problem that you cannot solve. Once you make a new commit, you will get a new sha. If you add that sha to a file that is under version control, it means that the file has been updated. If you commit that change you will get a new sha, which also needs to be commited, and so on…

      The trick is that there is no additional git commit / push required. As soon as you are happy with the feature you are developing you commit its changes. Jenkins will pick up the new commit, the buildnumber-maven-plugin will pick up the associated sha from Git and forward as a parameter in build time to the artifact that is being generated (with aid of the filtering described above). If you for some reason need to re-build your artifact later, all you have to is to check out the same version and re-build it. The two resulting binaries will have the same sha values since the builds where based on the same commit.

      Take a look at the file in the example project that I published on GitHub.

  10. Sandeep Pant

    Very Nice Example. Thanks a lot!!!

Leave a Reply