GIST: sorting semantic versioned Git tags

Jonas Brømsø - Sep 4 - - Dev Community

Today I was inspecting some Git version tags. The repositories in question are using semantic versioning and they did not come in the order I expected.

This is an example from an open source repository of mine:

╰─ git tag
0.1.0
0.2.0
0.3.0
0.3.1
0.4.0
0.5.0
0.6.0
0.6.1
v0.10.0
v0.11.0
v0.12.0
v0.13.0
v0.13.1
v0.7.0
v0.8.0
v0.9.0
Enter fullscreen mode Exit fullscreen mode

I attempted to pipe it to sort, expecting it to fail.

╰─ git tag | sort
0.1.0
0.2.0
0.3.0
0.3.1
0.4.0
0.5.0
0.6.0
0.6.1
v0.10.0
v0.11.0
v0.12.0
v0.13.0
v0.13.1
v0.7.0
v0.8.0
v0.9.0
Enter fullscreen mode Exit fullscreen mode

So I fired up my editor and wrote a small Perl script, since my experience with sorting in Perl is that it is pretty powerful.

#!perl

use warnings;
use strict;
use Data::Dumper;
use Getopt::Long;

my $reverse;

GetOptions ('reverse'  => \$reverse)   # flag
    or die("Error in command line arguments\n");

# read all from standard input
my @tags = <STDIN>;

# remove newlines
chomp @tags;

my @sorted_tags = sort {
    ($b =~ /v?(\d+)\.\d+\.\d+/)[0] <=> ($a =~ /v?(\d+)\.\d+\.\d+/)[0]
                        ||
    ($b =~ /v?\d+\.(\d+)\.\d+/)[0] <=> ($a =~ /v?\d+\.(\d+)\.\d+/)[0]
                        ||
    ($b =~ /v?\d+\.\d+\.(\d+)/)[0] <=> ($a =~ /v?\d+\.\d+\.(\d+)/)[0]
                        ||
                fc($a)  cmp  fc($b)
} @tags;

# print sorted tags to standard output on separate lines
if ($reverse) {
    print join("\n", reverse @sorted_tags), "\n";
} else {
    print join("\n", @sorted_tags), "\n";
}

exit 0;
Enter fullscreen mode Exit fullscreen mode

So now I can do:

╰─ git tag | sort_semantic_version_numbers.pl
v0.13.1
v0.13.0
v0.12.0
v0.11.0
v0.10.0
v0.9.0
v0.8.0
v0.7.0
0.6.1
0.6.0
0.5.0
0.4.0
0.3.1
0.3.0
0.2.0
0.1.0
Enter fullscreen mode Exit fullscreen mode

And I even through in a --reverse (-r is the shortform):

╰─ git tag | sort_semantic_version_numbers.pl --reverse
0.1.0
0.2.0
0.3.0
0.3.1
0.4.0
0.5.0
0.6.0
0.6.1
v0.7.0
v0.8.0
v0.9.0
v0.10.0
v0.11.0
v0.12.0
v0.13.0
v0.13.1
Enter fullscreen mode Exit fullscreen mode

There might a better approach, but this was done pretty fast. Yes the script possible lacks support for all sorts of tag formats, but it works for semantic version numbers and that works for my use-case.

Script available as a Gist.

Feedback, suggestions etc. most welcome.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .