Tag/tag.png

Candidate for Deletion
This article may not be appropriate for this wiki, and may be deleted. More info...

Tag/tag.png

Unsupported Version
This article applies to an unsupported version of Ubuntu. More info...

Downgrade

So, for one reason or another, you've upgraded Ubuntu to a new version. Now, you're not liking the new version (or it's a development version and too unstable for your taste). Downgrading is a little-covered topic, but let's change that!

DISCLAIMER

The first method below worked fine for me downgrading from hardy+backports+proposed back to hardy-backports-proposed. However, a previous wikizen has stated, with regards the second method:

DO NOT DO THIS. YOU CANNOT DOWNGRADE PYTHON ACROSS PACKAGES FROM 8.04 (HARDY) TO 7.10 (GUTSY). I tried this to downgrade from 8.10 (intrepid) to 8.04 (hardy) again. It worked almost fine, until the ati-drivers came into place! You must remove all Third-party-packages before and downgrade without any X loaded. Then it might work.

This document is still under development, and likely will be until some user-friendly option is added to apt-get. Please pursue at your own risk.

The first method

The problem with downgrading is that apt will not do it automatically. That's not a bug: apt is intelligent enough to assume that it doesn't know everything about your system; dpkg, by design, can install different packages and versions entirely separate from the apt fetching mechanism.

But between apt and dpkg, all of the required information for downgrading is present on your system, you just have to use it properly. What this method does is ensure no version higher than the canonical versions in /var/lib/apt/lists/*_Packages is installed.

Setting up sources

The first step is to get apt into proper shape. Set up your sources.list file with your favorite method, and run sudo apt-get update.

Collating apt sources with dpkg records

In theory, the next step would be to browse through the file /var/lib/dpkg/status, find all the version numbers, and then try and find the highest version in the various /var/lib/apt/lists/*_Packages files. If the version in status is not the highest version in *_Packages, install the version in *_Packages instead. Note that in the unusual case there was an upgrade in *_Packages, that would be performed as well as the downgrades.

But this is tedious, and we have computers to do this for you.

The script

I wrote a simple perl script that does the above. It is slightly buggy with the version numbers (NOTE: in case someone wants to help improve this script, a better way to deal with comparing version numbers might be to launch a command similar to 'dpkg --compare-versions 1 lt 2 && echo true') and hasn't been extensively tested, but it should get the job done.

use strict;

use Data::Dumper;

my $status = {};
my $available = {};

open(STATUS, "/var/lib/dpkg/status");

parse_dpkg(*STATUS{IO}, $status);
close(STATUS);

my @files = split(/\s+/, `ls /var/lib/apt/lists/*_Packages`);

my $file;
foreach $file (@files) {
    open(AVAILABLE, $file);
    parse_dpkg(*AVAILABLE{IO}, $available);
    close AVAILABLE;
}

my @args = ("aptitude", "install");
my $key;
foreach $key (sort(keys %$status)) {
    if($available->{$key}
       and $status->{$key}->{"Version"}
       and $status->{$key}->{"Version"} ne $available->{$key}->{"Version"}) {
        push(@args, "$key=" . $available->{$key}->{"Version"} );
    }
}

print Dumper(\@args);

# system(@args);

sub parse_dpkg {
    my $fh = shift;
    my $hash = shift;

    my $curhash = {};
    
    my $line = "";

    my $go = 1;

    while($go) {
        ($_ = <$fh>) or $go = 0;

        /^\s*$/ and do {
            $line =~ /([^:]*):\s*(.*?)\s*$/s;
            $curhash->{$1} = $2;
            $line = "";

            if(!defined($hash->{$curhash->{"Package"}})
               or compare_version($hash->{$curhash->{"Package"}}->{"Version"},
                                  $curhash->{"Version"}) < 0) {
                $hash->{$curhash->{"Package"}} = $curhash;
            }
            $curhash = {};
            next;
        };

        /^(\s+.*)$/ and do {
            $line .= $1;
            next;
        };

        if($line) {
            $line =~ /([^:]*):\s*(.*?)\s*$/s;
            $curhash->{$1} = $2;
            $line = "";
        }

        $line = $_;
    }
}

sub compare_version {
    my $ver1 = shift;
    my $ver2 = shift;
    my $dbg = shift;

    my $ver1_epoch = 0;
    my $ver2_epoch = 0;

    my $ver1_uvers = '';
    my $ver2_uvers = '';

    $ver1 =~ s/^([^:]*):// and $ver1_epoch = $1;
    $ver2 =~ s/^([^:]*):// and $ver2_epoch = $1;

    $ver1 =~ s/-([^-]*)$// and $ver1_uvers = $1;
    $ver2 =~ s/-([^-]*)$// and $ver2_uvers = $1;

    if($ver1_epoch != $ver2_epoch) {
        if($dbg) {
            print "Difference of epochs: " . $ver1_epoch . " vs. " . $ver2_epoch . "\n";
            print "returning " . ($ver1_epoch <=> $ver2_epoch);
        }
        return $ver1_epoch <=> $ver2_epoch;
    }

    if($ver1 eq $ver2) {
        if($dbg) {
            print "Identical versions: " . $ver1 . " vs. " . $ver2 . "\n";
        }
        $ver1 = $ver1_uvers;
        $ver2 = $ver2_uvers;
    }

    if($ver1 eq $ver2) {
        if($dbg) {
            print "Identical uversions: " . $ver1 . " vs. " . $ver2 . "\n";
            print "Returning 0";
        }
        return 0;
    }

    my $digit = 0;

    while($ver1 or $ver2) {
        if($digit) {
            my $n1 = 0;
            my $n2 = 0;
            
            $ver1 =~ s/^([0-9]+)// and $n1 = $1;
            $ver2 =~ s/^([0-9]+)// and $n2 = $1;

            if($dbg) {
                print "Comparing $n1 with $n2...\n";
            }

            if($n1 != $n2) {
                if($dbg) {
                    print "returning " . ($n1 <=> $n2) . "\n";
                }
                return $n1 <=> $n2;
            }
            $digit = 0;

            $ver1 =~ s/^\.(?=[0-9]+)// and $digit = 1;
            $ver2 =~ s/^\.(?=[0-9]+)// and $digit = 1;
        } else {
            my $s1 = "";
            my $s2 = "";

            $ver1 =~ s/^([^0-9]+)// and $s1 = $1;
            $ver2 =~ s/^([^0-9]+)// and $s2 = $1;

            if($dbg) {
                print "Comparing $s1 with $s2...\n";
            }

            for(my $i = 0; $i < length($s1) or $i < length($s2); $i++) {
                my $c1 = "";
                my $c2 = "";

                $i < length($s1) and $c1 = substr($s1, $i, 1);
                $i < length($s2) and $c2 = substr($s2, $i, 1);

                if($c1 eq $c2) {
                    next;
                }

                if($c1 eq "~") {
                    if($dbg) {
                        print "returning 1\n";
                    }
                    return 1;
                }

                if($c2 eq "~") {
                    if($dbg) {
                        print "returning -1\n";
                    }
                    return -1;
                }

                if($c1 eq "") {
                    if($dbg) {
                        print "returning -1\n";
                    }
                    return -1;
                }

                if($c2 eq "") {
                    if($dbg) {
                        print "returning 1\n";
                    }
                    return 1;
                }

                if($c1 =~ /^[a-zA-Z]$/) {
                    if($c2 !~ /^[a-zA-Z]$/) {
                        if($dbg) {
                            print "returning 1\n";
                        }
                        return 1;
                    }
                } elsif($c2 =~ /^[a-zA-Z]$/) {
                    return -1;
                    if($dbg) {
                        print "returning -1\n";
                    }
                }

                return $c1 cmp $c2;
            }
            $digit = 1;
        }
    }
}

Using the script

The above script will spit out a list of packages to downgrade, and the version numbers to downgrade to. If everything looks ok, hold your breath, do a little good luck dance and uncomment the relevant line. Run it again, and presto! your system is downgraded.

Cleaning up

Hopefully the above script doesn't downgrade anything accidentally, but if it does, the solution is simple: just run a package management tool and upgrade. It should upgrade whatever has been accidentally downgraded and leave the rest alone.

The second method

The second method uses an apt mechanism called "pinning". That might seem more native, but the mechanism wasn't really designed for downgrading. It can only be used to downgrade major versions, not for incremental downgrades like +proposed to -proposed. For this guide, we'll use the example of downgrading Ubuntu 5.10 (Breezy) to Ubuntu 5.04 (Hoary). This guide can be adapted to downgrade any combination of releases.

Setting up sources

You'll need sources.list entries for both releases for the procedure to work properly. It's important to remove all other lines for now! Example sources.list:

deb http://archive.ubuntu.com/ubuntu hoary main restricted universe multiverse

deb http://archive.ubuntu.com/ubuntu breezy main restricted universe multiverse

Setting up pinning

Open up /etc/apt/preferences with your favorite editor (such as gksudo gedit /etc/apt/preferences)

Add the following lines to the file (remove any existing entries):

        Package: *
        Pin: release a=hoary
        Pin-Priority: 1001

        Package: *
        Pin: release a=breezy
        Pin-Priority: 60

Doing the downgrade

If you're ready to continue, we're going to continue:

First, we need to update APT's information: sudo apt-get update

Next, we'll downgrade: sudo apt-get dist-upgrade

Cleaning up after the storm

The last step probably will end up a catastrophic mess of incompletely installed packages. We need to fix that now.

Sources

http://linuxmafia.com/faq/Debian/downgrade.html


DowngradeHowto (last edited 2014-01-04 12:06:10 by knome)