egencache: Sort file names for ChangeLog
authorSebastian Luther <SebastianLuther@gmx.de>
Mon, 4 Oct 2010 18:41:28 +0000 (20:41 +0200)
committerZac Medico <zmedico@gentoo.org>
Wed, 6 Oct 2010 22:57:15 +0000 (15:57 -0700)
bin/egencache

index b2f208fcea35047c66ef2168c17321384d160571..bf4b6d567444be54a6698a171a76a4d87036de2c 100755 (executable)
@@ -38,10 +38,11 @@ except ImportError:
 from portage import os, _encodings, _unicode_encode, _unicode_decode
 from _emerge.MetadataRegen import MetadataRegen
 from portage.cache.cache_errors import CacheError, StatCollision
+from portage.manifest import guessManifestFileType
 from portage.util import cmp_sort_key, writemsg_level
 from portage import cpv_getkey
 from portage.dep import Atom, isjustname
-from portage.versions import pkgcmp, pkgsplit
+from portage.versions import pkgcmp, pkgsplit, vercmp
 
 try:
        import xml.etree.ElementTree
@@ -463,6 +464,71 @@ class GenUseLocalDesc(object):
 
                output.close()
 
+if sys.hexversion < 0x3000000:
+       _filename_base = unicode
+else:
+       _filename_base = str
+
+class _special_filename(_filename_base):
+       """
+       Helps to sort file names by file type and other criteria.
+       """
+       def __new__(cls, status_change, file_name):
+               return _filename_base.__new__(cls, status_change + file_name)
+
+       def __init__(self, status_change, file_name):
+               _filename_base.__init__(status_change + file_name)
+               self.status_change = status_change
+               self.file_name = file_name
+               self.file_type = guessManifestFileType(file_name)
+
+       def file_type_lt(self, a, b):
+               """
+               Defines an ordering between file types.
+               """
+               first = a.file_type
+               second = b.file_type
+               if first == second:
+                       return False
+
+               if first == "EBUILD":
+                       return True
+               elif first == "MISC":
+                       return second in ("EBUILD",)
+               elif first == "AUX":
+                       return second in ("EBUILD", "MISC")
+               elif first == "DIST":
+                       return second in ("EBUILD", "MISC", "AUX")
+               elif first is None:
+                       return False
+               else:
+                       raise ValueError("Unknown file type '%s'" % first)
+
+       def __lt__(self, other):
+               """
+               Compare different file names, first by file type and then
+               for ebuilds by version and lexicographically for others.
+               EBUILD < MISC < AUX < DIST < None
+               """
+               if self.__class__ != other.__class__:
+                       raise NotImplementedError
+
+               # Sort by file type as defined by file_type_lt().
+               if self.file_type_lt(self, other):
+                       return True
+               elif self.file_type_lt(other, self):
+                       return False
+
+               # Files have the same type.
+               if self.file_type == "EBUILD":
+                       # Sort by version. Lowest first.
+                       ver = "-".join(pkgsplit(self.file_name[:-7])[1:3])
+                       other_ver = "-".join(pkgsplit(other.file_name[:-7])[1:3])
+                       return vercmp(ver, other_ver) < 0
+               else:
+                       # Sort lexicographically.
+                       return self.file_name < other.file_name
+
 class GenChangeLogs(object):
        def __init__(self, portdb):
                self.returncode = os.EX_OK
@@ -537,11 +603,11 @@ class GenChangeLogs(object):
                                        elif f[1] == 'ChangeLog':
                                                pass
                                        elif f[0].startswith('A'):
-                                               changed.append('+%s' % f[1])
+                                               changed.append(_special_filename("+", f[1]))
                                        elif f[0].startswith('D'):
-                                               changed.append('-%s' % f[1])
+                                               changed.append(_special_filename("-", f[1]))
                                        elif f[0].startswith('M'):
-                                               changed.append(f[1])
+                                               changed.append(_special_filename("", f[1]))
                                        else:
                                                writemsg_level(
                                                        "ERROR: unexpected git file status for %s: %s\n" % (cp,f,),
@@ -554,7 +620,7 @@ class GenChangeLogs(object):
                        (ts, author) = cinfo[0].split(' ', 1)
                        date = time.strftime('%d %b %Y', time.gmtime(float(ts)))
 
-                       # XXX: sort changes (ebuilds should go by PV)
+                       changed = [str(x) for x in sorted(changed)]
 
                        wroteheader = False
                        for c in changed:
@@ -592,6 +658,7 @@ class GenChangeLogs(object):
        def run(self):
                repo_path = self._portdb.porttrees[0]
                os.chdir(repo_path)
+
                if 'git' not in FindVCS():
                        writemsg_level(
                                "ERROR: --update-changelogs supported only in git repos\n",
@@ -601,10 +668,13 @@ class GenChangeLogs(object):
 
                for cp in self._portdb.cp_all():
                        os.chdir(os.path.join(repo_path, cp))
-
                        # Determine whether ChangeLog is up-to-date by comparing
                        # the newest commit timestamp with the ChangeLog timestamp.
                        lmod = self.grab(['git', 'log', '--format=%ct', '-1', '.'])
+                       if not lmod:
+                               # This cp has not been added to the repo.
+                               continue
+
                        try:
                                cmod = os.stat('ChangeLog').st_mtime
                        except OSError: