How does maven sort version numbers?

Solution 1:

Since version 3.0, Maven uses a consistent system to compare version numbers for both individual versions and version ranges. The system now makes a lot of sense, once you've understood a few gotchas.

All comparisons are now done by ComparableVersion, which says:

  • mixing of '-' (dash) and '.' (dot) separators,
  • transition between characters and digits also constitutes a separator: 1.0alpha1 => [1, 0, alpha, 1]
  • unlimited number of version components,
  • version components in the text can be digits or strings,
  • strings are checked for well-known qualifiers and the qualifier ordering is used for version ordering. Well-known qualifiers (case insensitive) are:
    • alpha or a
    • beta or b
    • milestone or m
    • rc or cr
    • snapshot
    • (the empty string) or ga or final
    • sp
  • Unknown qualifiers are considered after known qualifiers, with lexical order (always case insensitive),
  • a dash usually precedes a qualifier, and is always less important than something preceded with a dot.

This means that versions come out in the following order, which I think makes perfect sense, apart from 1.0-SNAPSHOT right in the middle:

  • 1.0-beta1-SNAPSHOT
  • 1.0-beta1
  • 1.0-beta2-SNAPSHOT
  • 1.0-rc1-SNAPSHOT
  • 1.0-rc1
  • 1.0-SNAPSHOT
  • 1.0
  • 1.0-sp
  • 1.0-whatever
  • 1.0.1

The main gotcha I found in all this is that snapshot comes after beta or rc, so you can't have a development version of 1.0-SNAPSHOT, then release 1.0-beta1 or 1.0-rc1 and have Maven understand that those are later.

Also note that 1.0-beta-1 is exactly the same as 1.0beta1, and 1.0 is exactly the same as 1 or 1.0.0.

Version ranges now work (nearly) the way you'd expect, too. For example, [1.0-alpha-SNAPSHOT,1.0] will find 1.0-beta1-SNAPSHOT, 1.0-beta1, 1.0-rc1-SNAPSHOT, 1.0-rc1, 1.0-SNAPSHOT or 1.0, preferring later items over earlier ones. This is fully supported by mvn versions:resolve, M2Eclipse and so on.

Solution 2:

This is a test that was written directly against the ComparableVersion class from Maven.

package org.codehaus.mojo.buildhelper.versioning;

import org.apache.maven.artifact.versioning.ComparableVersion;
import org.junit.Assert;
import org.junit.Test;

public class TempTest {
    @Test
    public void testVersions() {
        Assert.assertTrue(new ComparableVersion("1.0-beta1-SNAPSHOT").compareTo(
                new ComparableVersion("1.0-beta1")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0-beta1").compareTo(
                new ComparableVersion("1.0-beta2-SNAPSHOT")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0-beta2-SNAPSHOT").compareTo(
                new ComparableVersion("1.0-rc1-SNAPSHOT")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0-rc1-SNAPSHOT").compareTo(
                new ComparableVersion("1.0-rc1")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0-rc1").compareTo(
                new ComparableVersion("1.0-SNAPSHOT")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0-SNAPSHOT").compareTo(
                new ComparableVersion("1.0")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0").compareTo(
                new ComparableVersion("1")) == 0);
        Assert.assertTrue(new ComparableVersion("1.0").compareTo(
                new ComparableVersion("1.0-sp")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0-sp").compareTo(
                new ComparableVersion("1.0-whatever")) < 0);
        Assert.assertTrue(new ComparableVersion("1.0-whatever").compareTo(
                new ComparableVersion("1.0.1")) < 0);
    }
}

This test asserts that the following versions are considered to be from lowest to highest by Maven:

  • 1.0-beta1-SNAPSHOT
  • 1.0-beta1
  • 1.0-beta2-SNAPSHOT
  • 1.0-rc1-SNAPSHOT
  • 1.0-rc1
  • 1.0-SNAPSHOT
  • 1.0 and 1 (these are equal)
  • 1.0-sp
  • 1.0-whatever
  • 1.0.1

Solution 3:

Your assumption about using major/minor/incremtal/ etc. is simply wrong. The comparision is done in ComparableVersion which contains the implementation. The ctor will call parseVersion(...) which uses ComparableVersion which is stored as instance in DefaultArtifactVersion and it's used during the compareTo(..)

There are parts like getMajor.., etc. but those are not working correctly. This is the reason why will be marked deprecated.

The information by Stehpen Collony is true for Maven 2 but not for Maven 3 anymore.