Debian version strings

Sun, 10 Feb 2013 15:07:26 +0100
babble

As I did not find a nice explanation of Debian version numbers to point people at, here some random collection of information about:

All our packages have a version. For the package managers to know which to replace with which, those versions needs an ordering. As version orderings are like opinions (everyone has one), none of them would match any single one chosen for our tools to implement. So maintainers of Debian packages sometimes have to translate those versions into something the Debian tools understand.

But first let's start with some basics:

A Debian version string is of the form: [Epoch:]Upstream-Version[-Debian-Revision]

To make this form unique, the Upstream-Version may not contain an colon if there is no epoch and not contain a minus, if there is no Debian-Revision. The Epoch must be an integer (so no colons allowed). And the Debian-Revision may not contain a minus sign (so the Debian-Revision is everything right of the right-most minus sign, or empty if there is no such sign).

Two versions are compared by comparing all three parts. If the epochs differ, the biggest epoch wins. With same epochs, the biggest upstream version wins. With same epochs and same upstream versions, the biggest revision wins.

Comparing first the upstream version and then the revision is the only sensible thing to do, but it can have counter-intuitive effects if you try to compare versions with minus signs as Debian versions:

$ dpkg --compare-versions '1-2' '<<' '1-1-1' && echo true || echo false
true
$ dpkg --compare-versions '1-2-1' '<<' '1-1-1-1' && echo true || echo false
false

To compare two version parts (Upstream-Version or Debian-Revision), the string is split into pairs of digits and non digits. Consecutive digits are treated as a number and compared numerrically. Non-digit parts are compared just like ASCII strings with the exception that letters are sorted before non-letters and the tilde is treated specially (see below).

So 3pl12 and 3pl3s are slit into (3, 'pl', 12, '') and (3, 'pl', 3, 's') and the first is the larger version.

Comparing digits as characters makes not sense at least at the beginning of the string (otherweise version 10.1 would be smaller than 9.3). For digits later in the string there are two different version schemes competing here: There is GNU style 0.9.0 followed by 0.10.0 and decimal fractions like 0.11 < 0.9. Here a version comparison algorithm has to choose one and the one chosen by dpkg is both the one supporting the GNU numbering and also the one easier supporting the other scheme:

Imagine one software going 0.8 0.9 0.10 and one going 1.1 1.15 1.2. With out versioning scheme the first just works, while the second has to be translated into 1.1 1.15 1.20 to still be monotonic. The other way around, we would have to translate the first form to 0.08 0.09 0.10, or better 0.008 0.009 0.010 as we do not know how big those numbers will be, i.e. one would have to know beforehand where the numbers will end up, while adding zeros as needed for our scheme can be done with only knowing the previous numbers.

Another decision to be taken is how to treat non-numbers. The way dpkg did this was assuming adding stuff to the end increases numbers. This has the advantage to not needing to special case dots, as say 0.9.6.9 will be bigger than 0.9.6 naturally. I think back then this decision was also easier as usually anything attached was making the version bigger and one often saw versions like 3.3.bl.3 to denote some patches done atop of 3.3 in the 3th revision.

But this scheme has the disadvantage that version schemes like 1.0rc1 1.0rc2 1.0 do not map naturally. The classic way to work arround this is to translate that into 1.0rc1 1.0rc2 1.0.0 which works because the dot is a non-letter (it also works with 1.0-rc1 and 1.0+rc1 as the dot has a bigger ASCII number than minus or plus).

The new way is the specially treated tilde character. This character was added some years ago to sort before anything else, including an empty string. This means that 1.0~rc1 is less than 1.0:

dpkg --compare-versions '1.0~rc1-1' '<<' '1.0-1' && echo true || echo false

This scheme is especially useful if you want to create a package sorting before a package already there, as you for example do want with backports (as a user having a backport installed upgrading to the next distribution should get the backport replaced with the actual package). That's why backport usually having versions like 1.0-2~bpo60+1. Here 1.0-2 is the version of the un-backported version; bpo60 is a note that this is backported to Debian 6 (AKA squeeze) and the +1 is the number of the backport in case there are multiple tries necessary. (Note the use of the plus sign as the minus sign is not allowed in revisions and would make the part before a part of the upstream version).

Now, when to use which technique?

Usually try to stick to the upstream version if it is sane. (With the exception of data based versions, as experience shows upstreams later change their mind, so a version of 20130210 is best translated to 0.20130210 in order to have a future 1.0 sort higher).

If there are some suffixes in the version that denote a "before", prefix them with a tilde.

Compare a version to your last version and put zeros or .0 at the end (or the ends of parts) where necessary to increase them.

Some common examples:

You want to package 2.0 but you need to repack the .orig.tar because it contains a file with some non-free license (for example no license at all). Then you can call it 2.0~dfsg-1. If the file is later relicensed, you can do a 2.0-2 with the original file which sorts higher.
You need to repack 2.0 but it was already in the archive. then you can do 2.0+dfsg-1 as this is higher. (using the plus has the advantage that you can later use the 2.0.0 trick if the file is relicensed, while the older 2.0.dfsg-1 made this impossible). (One can also use -dfsg-1 here, but + might be easier to parse for humans).
You package a git snaphost of a version 2.0 not yet released, then you can use 2.0~gitXYZ-1
You package a git snapshot of upstream doing some works on top of 1.9, then you can use 1.9+gitXYZ-1