util: add sort helper
authorMatt Mackall <mpm@selenic.com>
Fri, 27 Jun 2008 18:28:45 -0500
changeset 6762 f67d1468ac50
parent 6761 cb981fc955fb
child 6763 403682f1c678
child 6776 39319a457dda
util: add sort helper
hgext/bugzilla.py
hgext/churn.py
hgext/convert/cvs.py
hgext/convert/cvsps.py
hgext/convert/darcs.py
hgext/convert/gnuarch.py
hgext/convert/hg.py
hgext/convert/subversion.py
hgext/graphlog.py
hgext/inotify/server.py
hgext/keyword.py
hgext/mq.py
hgext/notify.py
hgext/purge.py
hgext/transplant.py
mercurial/changelog.py
mercurial/cmdutil.py
mercurial/commands.py
mercurial/context.py
mercurial/copies.py
mercurial/dirstate.py
mercurial/filemerge.py
mercurial/hbisect.py
mercurial/hgweb/hgwebdir_mod.py
mercurial/hgweb/webcommands.py
mercurial/hook.py
mercurial/localrepo.py
mercurial/manifest.py
mercurial/merge.py
mercurial/patch.py
mercurial/streamclone.py
mercurial/ui.py
mercurial/util.py
mercurial/verify.py
--- a/hgext/bugzilla.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/bugzilla.py	Fri Jun 27 18:28:45 2008 -0500
@@ -99,9 +99,7 @@
     def filter_real_bug_ids(self, ids):
         '''filter not-existing bug ids from list.'''
         self.run('select bug_id from bugs where bug_id in %s' % buglist(ids))
-        ids = [c[0] for c in self.cursor.fetchall()]
-        ids.sort()
-        return ids
+        return util.sort([c[0] for c in self.cursor.fetchall()])
 
     def filter_unknown_bug_ids(self, node, ids):
         '''filter bug ids from list that already refer to this changeset.'''
@@ -114,9 +112,7 @@
             self.ui.status(_('bug %d already knows about changeset %s\n') %
                            (id, short(node)))
             unknown.pop(id, None)
-        ids = unknown.keys()
-        ids.sort()
-        return ids
+        return util.sort(unknown.keys())
 
     def notify(self, ids):
         '''tell bugzilla to send mail.'''
--- a/hgext/churn.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/churn.py	Fri Jun 27 18:28:45 2008 -0500
@@ -92,14 +92,12 @@
             alias, actual = l.split()
             amap[alias] = actual
 
-    revs = [int(r) for r in cmdutil.revrange(repo, opts['rev'])]
-    revs.sort()
+    revs = util.sort([int(r) for r in cmdutil.revrange(repo, opts['rev'])])
     stats = countrevs(ui, repo, amap, revs, opts.get('progress'))
     if not stats:
         return
 
-    stats = [(-l, u, l) for u,l in stats.items()]
-    stats.sort()
+    stats = util.sort([(-l, u, l) for u,l in stats.items()])
     maxchurn = float(max(1, stats[0][2]))
     maxuser = max([len(u) for k, u, l in stats])
 
--- a/hgext/convert/cvs.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/convert/cvs.py	Fri Jun 27 18:28:45 2008 -0500
@@ -337,10 +337,7 @@
 
     def getchanges(self, rev):
         self.modecache = {}
-        files = self.files[rev]
-        cl = files.items()
-        cl.sort()
-        return (cl, {})
+        return util.sort(self.files[rev].items()), {}
 
     def getcommit(self, rev):
         return self.changeset[rev]
@@ -349,7 +346,4 @@
         return self.tags
 
     def getchangedfiles(self, rev, i):
-        files = self.files[rev].keys()
-        files.sort()
-        return files
-
+        return util.sort(self.files[rev].keys())
--- a/hgext/convert/cvsps.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/convert/cvsps.py	Fri Jun 27 18:28:45 2008 -0500
@@ -297,8 +297,7 @@
         if store:
             # clean up the results and save in the log.
             store = False
-            e.tags = [scache(x) for x in tags.get(e.revision, [])]
-            e.tags.sort()
+            e.tags = util.sort([scache(x) for x in tags.get(e.revision, [])])
             e.comment = scache('\n'.join(e.comment))
 
             revn = len(e.revision)
@@ -468,9 +467,7 @@
             for tag in e.tags:
                 tags[tag] = True
         # remember tags only if this is the latest changeset to have it
-        tagnames = [tag for tag in tags if globaltags[tag] is c]
-        tagnames.sort()
-        c.tags = tagnames
+        c.tags = util.sort([tag for tag in tags if globaltags[tag] is c])
 
     # Find parent changesets, handle {{mergetobranch BRANCHNAME}}
     # by inserting dummy changesets with two parents, and handle
--- a/hgext/convert/darcs.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/convert/darcs.py	Fri Jun 27 18:28:45 2008 -0500
@@ -110,9 +110,8 @@
                 copies[elt.get('from')] = elt.get('to')
             else:
                 changes.append((elt.text.strip(), rev))
-        changes.sort()
         self.lastrev = rev
-        return changes, copies
+        return util.sort(changes), copies
 
     def getfile(self, name, rev):
         if rev != self.lastrev:
--- a/hgext/convert/gnuarch.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/convert/gnuarch.py	Fri Jun 27 18:28:45 2008 -0500
@@ -130,10 +130,8 @@
             for c in cps:
                 copies[c] = cps[c]
 
-        changes.sort()
         self.lastrev = rev
-
-        return changes, copies
+        return util.sort(changes), copies
 
     def getcommit(self, rev):
         changes = self.changes[rev]
--- a/hgext/convert/hg.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/convert/hg.py	Fri Jun 27 18:28:45 2008 -0500
@@ -164,14 +164,11 @@
              tagparent = nullid
 
          try:
-             old = parentctx.filectx(".hgtags").data()
-             oldlines = old.splitlines(1)
-             oldlines.sort()
+             oldlines = util.sort(parentctx['.hgtags'].data().splitlines(1))
          except:
              oldlines = []
 
-         newlines = [("%s %s\n" % (tags[tag], tag)) for tag in tags.keys()]
-         newlines.sort()
+         newlines = util.sort([("%s %s\n" % (tags[tag], tag)) for tag in tags])
 
          if newlines == oldlines:
              return None
@@ -238,8 +235,7 @@
         else:
             m, a, r = self.repo.status(ctx.parents()[0].node(), ctx.node())[:3]
         changes = [(name, rev) for name in m + a + r]
-        changes.sort()
-        return (changes, self.getcopies(ctx, m + a))
+        return util.sort(changes), self.getcopies(ctx, m + a)
 
     def getcopies(self, ctx, files):
         copies = {}
--- a/hgext/convert/subversion.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/convert/subversion.py	Fri Jun 27 18:28:45 2008 -0500
@@ -658,8 +658,7 @@
                 # This will fail if a directory was copied
                 # from another branch and then some of its files
                 # were deleted in the same transaction.
-                children = self._find_children(path, revnum)
-                children.sort()
+                children = util.sort(self._find_children(path, revnum))
                 for child in children:
                     # Can we move a child directory and its
                     # parent in the same commit? (probably can). Could
@@ -732,8 +731,7 @@
             parents = []
             # check whether this revision is the start of a branch or part
             # of a branch renaming
-            orig_paths = orig_paths.items()
-            orig_paths.sort()
+            orig_paths = util.sort(orig_paths.items())
             root_paths = [(p,e) for p,e in orig_paths if self.module.startswith(p)]
             if root_paths:
                 path, ent = root_paths[-1]
@@ -1045,10 +1043,9 @@
         return dirs
 
     def add_dirs(self, files):
-        add_dirs = [d for d in self.dirs_of(files)
+        add_dirs = [d for d in util.sort(self.dirs_of(files))
                     if not os.path.exists(self.wjoin(d, '.svn', 'entries'))]
         if add_dirs:
-            add_dirs.sort()
             self.xargs(add_dirs, 'add', non_recursive=True, quiet=True)
         return add_dirs
 
@@ -1058,8 +1055,7 @@
         return files
 
     def tidy_dirs(self, names):
-        dirs = list(self.dirs_of(names))
-        dirs.sort()
+        dirs = util.sort(self.dirs_of(names))
         dirs.reverse()
         deleted = []
         for d in dirs:
--- a/hgext/graphlog.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/graphlog.py	Fri Jun 27 18:28:45 2008 -0500
@@ -13,6 +13,7 @@
 from mercurial.i18n import _
 from mercurial.node import nullrev
 from mercurial.util import Abort, canonpath
+from mercurial import util
 
 def revision_grapher(repo, start_rev, stop_rev):
     """incremental revision grapher
@@ -53,8 +54,7 @@
         for parent in parents:
             if parent not in next_revs:
                 parents_to_add.append(parent)
-        parents_to_add.sort()
-        next_revs[rev_index:rev_index + 1] = parents_to_add
+        next_revs[rev_index:rev_index + 1] = util.sort(parents_to_add)
 
         edges = []
         for parent in parents:
@@ -105,8 +105,7 @@
         for parent in parents:
             if parent not in next_revs:
                 parents_to_add.append(parent)
-        parents_to_add.sort()
-        next_revs[rev_index:rev_index + 1] = parents_to_add
+        next_revs[rev_index:rev_index + 1] = util.sort(parents_to_add)
 
         edges = []
         for parent in parents:
--- a/hgext/inotify/server.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/inotify/server.py	Fri Jun 27 18:28:45 2008 -0500
@@ -534,9 +534,7 @@
                 self.ui.note('%s processing %d deferred events as %d\n' %
                              (self.event_time(), self.deferred,
                               len(self.eventq)))
-            eventq = self.eventq.items()
-            eventq.sort()
-            for wpath, evts in eventq:
+            for wpath, evts in util.sort(self.eventq.items()):
                 for evt in evts:
                     self.deferred_event(wpath, evt)
             self.eventq.clear()
--- a/hgext/keyword.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/keyword.py	Fri Jun 27 18:28:45 2008 -0500
@@ -183,7 +183,6 @@
         candidates = [f for f in files if self.iskwfile(f, ctx.flags)]
         if candidates:
             self.restrict = True # do not expand when reading
-            candidates.sort()
             action = expand and 'expanding' or 'shrinking'
             for f in candidates:
                 fp = self.repo.file(f)
@@ -382,8 +381,7 @@
     kwt = kwtools['templater']
     status = _status(ui, repo, kwt, opts.get('untracked'), *pats, **opts)
     modified, added, removed, deleted, unknown, ignored, clean = status
-    files = modified + added + clean + unknown
-    files.sort()
+    files = util.sort(modified + added + clean + unknown)
     wctx = repo[None]
     kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
     cwd = pats and repo.getcwd() or ''
--- a/hgext/mq.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/mq.py	Fri Jun 27 18:28:45 2008 -0500
@@ -143,8 +143,7 @@
             bad = self.check_guard(guard)
             if bad:
                 raise util.Abort(bad)
-        guards = dict.fromkeys(guards).keys()
-        guards.sort()
+        guards = util.sort(util.unique(guards))
         self.ui.debug('active guards: %s\n' % ' '.join(guards))
         self.active_guards = guards
         self.guards_dirty = True
@@ -536,8 +535,7 @@
         return (err, n)
 
     def _clean_series(self, patches):
-        indices = [self.find_series(p) for p in patches]
-        indices.sort()
+        indices = util.sort([self.find_series(p) for p in patches])
         for i in indices[-1::-1]:
             del self.full_series[i]
         self.parse_series()
@@ -545,10 +543,10 @@
 
     def finish(self, repo, revs):
         revs.sort()
-        firstrev = repo.changelog.rev(revlog.bin(self.applied[0].rev))
+        firstrev = repo[self.applied[0].rev].rev()
         appliedbase = 0
         patches = []
-        for rev in revs:
+        for rev in util.sort(revs):
             if rev < firstrev:
                 raise util.Abort(_('revision %d is not managed') % rev)
             base = revlog.bin(self.applied[appliedbase].rev)
@@ -1261,8 +1259,7 @@
                                    self.guards_path)
                         and not fl.startswith('.')):
                         msng_list.append(fl)
-            msng_list.sort()
-            for x in msng_list:
+            for x in util.sort(msng_list):
                 pfx = self.ui.verbose and ('D ') or ''
                 self.ui.write("%s%s\n" % (pfx, displayname(x)))
 
--- a/hgext/notify.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/notify.py	Fri Jun 27 18:28:45 2008 -0500
@@ -156,9 +156,7 @@
             if fnmatch.fnmatch(self.repo.root, pat):
                 for user in users.split(','):
                     subs[self.fixmail(user)] = 1
-        subs = subs.keys()
-        subs.sort()
-        return subs
+        return util.sort(subs)
 
     def url(self, path=None):
         return self.ui.config('web', 'baseurl') + (path or self.root)
--- a/hgext/purge.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/purge.py	Fri Jun 27 18:28:45 2008 -0500
@@ -77,15 +77,12 @@
     match = cmdutil.match(repo, dirs, opts)
     match.dir = directories.append
     status = repo.status(match=match, ignored=opts['all'], unknown=True)
-    files = status[4] + status[5]
-    files.sort()
-    directories.sort()
 
-    for f in files:
+    for f in util.sort(status[4] + status[5]):
         ui.note(_('Removing file %s\n') % f)
         remove(os.remove, f)
 
-    for f in directories[::-1]:
+    for f in util.sort(directories)[::-1]:
         if match(f) and not os.listdir(repo.wjoin(f)):
             ui.note(_('Removing directory %s\n') % f)
             remove(os.rmdir, f)
--- a/hgext/transplant.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/hgext/transplant.py	Fri Jun 27 18:28:45 2008 -0500
@@ -88,9 +88,7 @@
 
     def apply(self, repo, source, revmap, merges, opts={}):
         '''apply the revisions in revmap one by one in revision order'''
-        revs = revmap.keys()
-        revs.sort()
-
+        revs = util.sort(revmap)
         p1, p2 = repo.dirstate.parents()
         pulls = []
         diffopts = patch.diffopts(self.ui, opts)
@@ -310,9 +308,7 @@
         if not os.path.isdir(self.path):
             os.mkdir(self.path)
         series = self.opener('series', 'w')
-        revs = revmap.keys()
-        revs.sort()
-        for rev in revs:
+        for rev in util.sort(revmap):
             series.write(revlog.hex(revmap[rev]) + '\n')
         if merges:
             series.write('# Merges\n')
@@ -572,10 +568,6 @@
         for r in merges:
             revmap[source.changelog.rev(r)] = r
 
-        revs = revmap.keys()
-        revs.sort()
-        pulls = []
-
         tp.apply(repo, source, revmap, merges, opts)
     finally:
         if bundle:
--- a/mercurial/changelog.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/changelog.py	Fri Jun 27 18:28:45 2008 -0500
@@ -130,9 +130,7 @@
 
     def encode_extra(self, d):
         # keys must be sorted to produce a deterministic changelog entry
-        keys = d.keys()
-        keys.sort()
-        items = [_string_escape('%s:%s' % (k, d[k])) for k in keys]
+        items = [_string_escape('%s:%s' % (k, d[k])) for k in util.sort(d)]
         return "\0".join(items)
 
     def read(self, node):
@@ -175,7 +173,7 @@
         files = l[3:]
         return (manifest, user, (time, timezone), files, desc, extra)
 
-    def add(self, manifest, list, desc, transaction, p1=None, p2=None,
+    def add(self, manifest, files, desc, transaction, p1=None, p2=None,
                   user=None, date=None, extra={}):
 
         user, desc = util.fromlocal(user), util.fromlocal(desc)
@@ -189,7 +187,6 @@
         if extra:
             extra = self.encode_extra(extra)
             parseddate = "%s %s" % (parseddate, extra)
-        list.sort()
-        l = [hex(manifest), user, parseddate] + list + ["", desc]
+        l = [hex(manifest), user, parseddate] + util.sort(files) + ["", desc]
         text = "\n".join(l)
         return self.addrevision(text, transaction, len(self), p1, p2)
--- a/mercurial/cmdutil.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/cmdutil.py	Fri Jun 27 18:28:45 2008 -0500
@@ -653,9 +653,7 @@
             self.ui.write(_("copies:      %s\n") % ' '.join(copies))
 
         if extra and self.ui.debugflag:
-            extraitems = extra.items()
-            extraitems.sort()
-            for key, value in extraitems:
+            for key, value in util.sort(extra.items()):
                 self.ui.write(_("extra:       %s=%s\n")
                               % (key, value.encode('string_escape')))
 
@@ -799,9 +797,7 @@
             return showlist('tag', self.repo.nodetags(changenode), **args)
 
         def showextras(**args):
-            extras = changes[5].items()
-            extras.sort()
-            for key, value in extras:
+            for key, value in util.sort(changes[5].items()):
                 args = args.copy()
                 args.update(dict(key=key, value=value))
                 yield self.t('extra', **args)
@@ -1129,9 +1125,7 @@
         for i, window in increasing_windows(0, len(revs)):
             yield 'window', revs[0] < revs[-1], revs[-1]
             nrevs = [rev for rev in revs[i:i+window] if want(rev)]
-            srevs = list(nrevs)
-            srevs.sort()
-            for rev in srevs:
+            for rev in util.sort(list(nrevs)):
                 fns = fncache.get(rev)
                 if not fns:
                     def fns_generator():
@@ -1159,7 +1153,7 @@
     m = match(repo, pats, opts)
     if pats:
         modified, added, removed = repo.status(match=m)[:3]
-        files = modified + added + removed
+        files = util.sort(modified + added + removed)
         slist = None
         for f in m.files():
             if f == '.':
@@ -1173,11 +1167,8 @@
                     raise util.Abort(_("file %s not found!") % rel)
                 if stat.S_ISDIR(mode):
                     name = f + '/'
-                    if slist is None:
-                        slist = list(files)
-                        slist.sort()
-                    i = bisect.bisect(slist, name)
-                    if i >= len(slist) or not slist[i].startswith(name):
+                    i = bisect.bisect(files, name)
+                    if i >= len(files) or not files[i].startswith(name):
                         raise util.Abort(_("no match under directory %s!")
                                          % rel)
                 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
--- a/mercurial/commands.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/commands.py	Fri Jun 27 18:28:45 2008 -0500
@@ -380,9 +380,8 @@
     hexfunc = ui.debugflag and hex or short
     activebranches = [util.tolocal(repo[n].branch())
                             for n in repo.heads()]
-    branches = [(tag in activebranches, repo.changelog.rev(node), tag)
-                            for tag, node in repo.branchtags().items()]
-    branches.sort()
+    branches = util.sort([(tag in activebranches, repo.changelog.rev(node), tag)
+                          for tag, node in repo.branchtags().items()])
     branches.reverse()
 
     for isactive, node, tag in branches:
@@ -635,9 +634,7 @@
         ui.write("%s\n" % "\n".join(options))
         return
 
-    clist = cmdutil.findpossible(ui, cmd, table).keys()
-    clist.sort()
-    ui.write("%s\n" % "\n".join(clist))
+    ui.write("%s\n" % "\n".join(util.sort(cmdutil.findpossible(ui, cmd, table))))
 
 def debugfsinfo(ui, path = "."):
     file('.debugfsinfo', 'w').write('')
@@ -727,11 +724,9 @@
 
 def debugstate(ui, repo, nodates=None):
     """show the contents of the current dirstate"""
-    k = repo.dirstate._map.items()
-    k.sort()
     timestr = ""
     showdate = not nodates
-    for file_, ent in k:
+    for file_, ent in util.sort(repo.dirstate._map.items()):
         if showdate:
             if ent[3] == -1:
                 # Pad or slice to locale representation
@@ -1142,9 +1137,7 @@
                 except revlog.LookupError:
                     pass
         elif st == 'iter':
-            states = matches[rev].items()
-            states.sort()
-            for fn, m in states:
+            for fn, m in util.sort(matches[rev].items()):
                 copy = copies.get(rev, {}).get(fn)
                 if fn in skip:
                     if copy:
@@ -1162,9 +1155,7 @@
                     fstate[copy] = m
                 prev[fn] = rev
 
-    fstate = fstate.items()
-    fstate.sort()
-    for fn, state in fstate:
+    for fn, state in util.sort(fstate.items()):
         if fn in skip:
             continue
         if fn not in copies.get(prev[fn], {}):
@@ -1304,8 +1295,7 @@
             return
 
         ui.status(header)
-        fns = h.keys()
-        fns.sort()
+        fns = util.sort(h)
         m = max(map(len, fns))
         for f in fns:
             if ui.verbose:
@@ -2215,9 +2205,7 @@
         warn(modified, _('is modified'))
         warn(added, _('has been marked for add'))
 
-    files = remove + forget
-    files.sort()
-    for f in files:
+    for f in util.sort(remove + forget):
         if ui.verbose or not m.exact(f):
             ui.status(_('removing %s\n') % m.rel(f))
 
@@ -2401,10 +2389,7 @@
             (deleted, revert, remove, False, False),
             )
 
-        entries = names.items()
-        entries.sort()
-
-        for abs, (rel, exact) in entries:
+        for abs, (rel, exact) in util.sort(names.items()):
             mfentry = mf.get(abs)
             target = repo.wjoin(abs)
             def handle(xlist, dobackup):
--- a/mercurial/context.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/context.py	Fri Jun 27 18:28:45 2008 -0500
@@ -71,9 +71,7 @@
         return self.filectx(key)
 
     def __iter__(self):
-        a = self._manifest.keys()
-        a.sort()
-        for f in a:
+        for f in util.sort(self._manifest):
             yield f
 
     def changeset(self): return self._changeset
@@ -134,10 +132,7 @@
     def filectxs(self):
         """generate a file context for each file in this changeset's
            manifest"""
-        mf = self.manifest()
-        m = mf.keys()
-        m.sort()
-        for f in m:
+        for f in util.sort(mf):
             yield self.filectx(f, fileid=mf[f])
 
     def ancestor(self, c2):
@@ -383,12 +378,11 @@
         # sort by revision (per file) which is a topological order
         visit = []
         for f in files:
-            fn = [(n.rev(), n) for n in needed.keys() if n._path == f]
+            fn = [(n.rev(), n) for n in needed if n._path == f]
             visit.extend(fn)
-        visit.sort()
+
         hist = {}
-
-        for r, f in visit:
+        for r, f in util.sort(visit):
             curr = decorate(f.data(), f)
             for p in parents(f):
                 if p != nullid:
@@ -530,9 +524,7 @@
     def date(self): return self._date
     def description(self): return self._text
     def files(self):
-        f = self.modified() + self.added() + self.removed()
-        f.sort()
-        return f
+        return util.sort(self._status[0] + self._status[1] + self._status[2])
 
     def modified(self): return self._status[0]
     def added(self): return self._status[1]
@@ -688,8 +680,7 @@
         parents = [(p or nullid) for p in parents]
         p1, p2 = parents
         self._parents = [changectx(self._repo, p) for p in (p1, p2)]
-        files = list(files)
-        files.sort()
+        files = util.sort(list(files))
         self._status = [files, [], [], [], []]
         self._filectxfn = filectxfn
 
--- a/mercurial/copies.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/copies.py	Fri Jun 27 18:28:45 2008 -0500
@@ -11,9 +11,7 @@
 
 def _nonoverlap(d1, d2, d3):
     "Return list of elements in d1 not in d2 or d3"
-    l = [d for d in d1 if d not in d3 and d not in d2]
-    l.sort()
-    return l
+    return util.sort([d for d in d1 if d not in d3 and d not in d2])
 
 def _dirname(f):
     s = f.rfind("/")
@@ -49,9 +47,7 @@
         visit += [(p, depth - 1) for p in fc.parents()]
 
     # return old names sorted by depth
-    old = old.values()
-    old.sort()
-    return [o[1] for o in old]
+    return [o[1] for o in util.sort(old.values())]
 
 def _findlimit(repo, a, b):
     "find the earliest revision that's an ancestor of a or b but not both"
--- a/mercurial/dirstate.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/dirstate.py	Fri Jun 27 18:28:45 2008 -0500
@@ -153,9 +153,7 @@
         return key in self._map
 
     def __iter__(self):
-        a = self._map.keys()
-        a.sort()
-        for x in a:
+        for x in util.sort(self._map):
             yield x
 
     def parents(self):
@@ -436,8 +434,7 @@
         if not unknown:
             return ret
 
-        b = self._map.keys()
-        b.sort()
+        b = util.sort(self._map)
         blen = len(b)
 
         for x in unknown:
@@ -578,12 +575,10 @@
                             add((nn, 'f', st))
                         elif np in dc:
                             add((nn, 'm', st))
-            found.sort()
-            return found
+            return util.sort(found)
 
         # step one, find all files that match our criteria
-        files.sort()
-        for ff in files:
+        for ff in util.sort(files):
             nf = normpath(ff)
             nn = self.normalize(nf)
             f = _join(ff)
@@ -617,9 +612,7 @@
 
         # step two run through anything left in the dc hash and yield
         # if we haven't already seen it
-        ks = dc.keys()
-        ks.sort()
-        for k in ks:
+        for k in util.sort(dc):
             if k in known:
                 continue
             known[k] = 1
--- a/mercurial/filemerge.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/filemerge.py	Fri Jun 27 18:28:45 2008 -0500
@@ -63,8 +63,7 @@
         if t not in tools:
             tools[t] = int(_toolstr(ui, t, "priority", "0"))
     names = tools.keys()
-    tools = [(-p,t) for t,p in tools.items()]
-    tools.sort()
+    tools = util.sort([(-p,t) for t,p in tools.items()])
     uimerge = ui.config("ui", "merge")
     if uimerge:
         if uimerge not in names:
--- a/mercurial/hbisect.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/hbisect.py	Fri Jun 27 18:28:45 2008 -0500
@@ -60,7 +60,6 @@
                         children[prev] = [rev]
                         visit.append(prev)
 
-    candidates.sort()
     # have we narrowed it down to one entry?
     tot = len(candidates)
     if tot == 1:
@@ -71,7 +70,7 @@
     best_rev = None
     best_len = -1
     poison = {}
-    for rev in candidates:
+    for rev in util.sort(candidates):
         if rev in poison:
             for c in children.get(rev, []):
                 poison[c] = True # poison children
--- a/mercurial/hgweb/hgwebdir_mod.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/hgweb/hgwebdir_mod.py	Fri Jun 27 18:28:45 2008 -0500
@@ -19,8 +19,8 @@
 class hgwebdir(object):
     def __init__(self, config, parentui=None):
         def cleannames(items):
-            return [(util.pconvert(name).strip('/'), path)
-                    for name, path in items]
+            return util.sort([(util.pconvert(name).strip('/'), path)
+                              for name, path in items])
 
         self.parentui = parentui or ui.ui(report_untrusted=False,
                                           interactive = False)
@@ -34,7 +34,6 @@
             self.repos_sorted = ('', False)
         elif isinstance(config, dict):
             self.repos = cleannames(config.items())
-            self.repos.sort()
         else:
             if isinstance(config, util.configparser):
                 cp = config
--- a/mercurial/hgweb/webcommands.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/hgweb/webcommands.py	Fri Jun 27 18:28:45 2008 -0500
@@ -13,7 +13,7 @@
 from mercurial.repo import RepoError
 from common import paritygen, staticfile, get_contact, ErrorResponse
 from common import HTTP_OK, HTTP_NOT_FOUND
-from mercurial import graphmod
+from mercurial import graphmod, util
 
 # __all__ is populated with the allowed commands. Be sure to add to it if
 # you're adding a new command, or the new command won't work.
@@ -288,9 +288,7 @@
         raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
 
     def filelist(**map):
-        fl = files.keys()
-        fl.sort()
-        for f in fl:
+        for f in util.sort(files):
             full, fnode = files[f]
             if not fnode:
                 continue
@@ -304,9 +302,7 @@
                    "permissions": mf.flags(full)}
 
     def dirlist(**map):
-        fl = files.keys()
-        fl.sort()
-        for f in fl:
+        for f in util.sort(files):
             full, fnode = files[f]
             if fnode:
                 continue
@@ -378,9 +374,7 @@
 
         b = web.repo.branchtags()
         l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.items()]
-        l.sort()
-
-        for r,n,t in l:
+        for r,n,t in util.sort(l):
             yield {'parity': parity.next(),
                    'branch': t,
                    'node': hex(n),
--- a/mercurial/hook.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/hook.py	Fri Jun 27 18:28:45 2008 -0500
@@ -96,10 +96,9 @@
         oldstdout = os.dup(sys.__stdout__.fileno())
         os.dup2(sys.__stderr__.fileno(), sys.__stdout__.fileno())
 
-    hooks = [(hname, cmd) for hname, cmd in ui.configitems("hooks")
-             if hname.split(".", 1)[0] == name and cmd]
-    hooks.sort()
-    for hname, cmd in hooks:
+    for hname, cmd in util.sort(ui.configitems('hooks')):
+        if hname.split('.')[0] != name or not cmd:
+            continue
         if callable(cmd):
             r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
         elif cmd.startswith('python:'):
--- a/mercurial/localrepo.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/localrepo.py	Fri Jun 27 18:28:45 2008 -0500
@@ -366,8 +366,7 @@
             except:
                 r = -2 # sort to the beginning of the list if unknown
             l.append((r, t, n))
-        l.sort()
-        return [(t, n) for r, t, n in l]
+        return [(t, n) for r, t, n in util.sort(l)]
 
     def nodetags(self, node):
         '''return the tags associated with a node'''
@@ -811,7 +810,7 @@
         tr = None
         valid = 0 # don't save the dirstate if this isn't set
         try:
-            commit = wctx.modified() + wctx.added()
+            commit = util.sort(wctx.modified() + wctx.added())
             remove = wctx.removed()
             extra = wctx.extra().copy()
             branchname = extra['branch']
@@ -844,7 +843,6 @@
             new = {}
             changed = []
             linkrev = len(self)
-            commit.sort()
             for f in commit:
                 self.ui.note(f + "\n")
                 try:
@@ -871,10 +869,9 @@
 
             # update manifest
             m1.update(new)
-            remove.sort()
             removed = []
 
-            for f in remove:
+            for f in util.sort(remove):
                 if f in m1:
                     del m1[f]
                     removed.append(f)
@@ -950,10 +947,7 @@
             # for dirstate.walk, files=['.'] means "walk the whole tree".
             # follow that here, too
             fdict.pop('.', None)
-            mdict = self.manifest.read(self.changelog.read(node)[0])
-            mfiles = mdict.keys()
-            mfiles.sort()
-            for fn in mfiles:
+            for fn in self[node]:
                 for ffn in fdict:
                     # match if the file is the exact name or a directory
                     if ffn == fn or fn.startswith("%s/" % ffn):
@@ -961,9 +955,7 @@
                         break
                 if match(fn):
                     yield fn
-            ffiles = fdict.keys()
-            ffiles.sort()
-            for fn in ffiles:
+            for fn in util.sort(fdict):
                 if match.bad(fn, 'No such file in rev ' + short(node)) \
                         and match(fn):
                     yield fn
@@ -1065,10 +1057,8 @@
 
             # make sure to sort the files so we talk to the disk in a
             # reasonable order
-            mf2keys = mf2.keys()
-            mf2keys.sort()
             getnode = lambda fn: mf1.get(fn, nullid)
-            for fn in mf2keys:
+            for fn in util.sort(mf2):
                 if fn in mf1:
                     if (mf1.flags(fn) != mf2.flags(fn) or
                         (mf1[fn] != mf2[fn] and
@@ -1190,8 +1180,7 @@
         heads = self.changelog.heads(start)
         # sort the output in rev descending order
         heads = [(-self.changelog.rev(h), h) for h in heads]
-        heads.sort()
-        return [n for (r, n) in heads]
+        return [n for (r, n) in util.sort(heads)]
 
     def branchheads(self, branch=None, start=None):
         if branch is None:
@@ -1843,10 +1832,8 @@
                     add_extra_nodes(fname,
                                     msng_filenode_set.setdefault(fname, {}))
                     changedfiles[fname] = 1
-            changedfiles = changedfiles.keys()
-            changedfiles.sort()
             # Go through all our files in order sorted by name.
-            for fname in changedfiles:
+            for fname in util.sort(changedfiles):
                 filerevlog = self.file(fname)
                 if not len(filerevlog):
                     raise util.Abort(_("empty or missing revlog for %s") % fname)
@@ -1924,15 +1911,13 @@
             for chnk in cl.group(nodes, identity,
                                  changed_file_collector(changedfiles)):
                 yield chnk
-            changedfiles = changedfiles.keys()
-            changedfiles.sort()
 
             mnfst = self.manifest
             nodeiter = gennodelst(mnfst)
             for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
                 yield chnk
 
-            for fname in changedfiles:
+            for fname in util.sort(changedfiles):
                 filerevlog = self.file(fname)
                 if not len(filerevlog):
                     raise util.Abort(_("empty or missing revlog for %s") % fname)
--- a/mercurial/manifest.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/manifest.py	Fri Jun 27 18:28:45 2008 -0500
@@ -8,7 +8,7 @@
 from node import bin, hex, nullid
 from revlog import revlog, RevlogError
 from i18n import _
-import array, struct, mdiff, parsers
+import array, struct, mdiff, parsers, util
 
 class manifestdict(dict):
     def __init__(self, mapping=None, flags=None):
@@ -126,9 +126,7 @@
         # if we're using the listcache, make sure it is valid and
         # parented by the same node we're diffing against
         if not (changed and self.listcache and p1 and self.mapcache[0] == p1):
-            files = map.keys()
-            files.sort()
-
+            files = util.sort(map)
             for f in files:
                 checkforbidden(f)
 
--- a/mercurial/merge.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/merge.py	Fri Jun 27 18:28:45 2008 -0500
@@ -262,11 +262,10 @@
     "apply the merge action list to the working directory"
 
     updated, merged, removed, unresolved = 0, 0, 0, 0
-    action.sort()
-
     ms = mergestate(repo)
     ms.reset(wctx.parents()[0].node())
     moves = []
+    action.sort()
 
     # prescan for merges
     for a in action:
--- a/mercurial/patch.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/patch.py	Fri Jun 27 18:28:45 2008 -0500
@@ -1094,8 +1094,7 @@
         repo.copy(src, dst)
     removes = removes.keys()
     if removes:
-        removes.sort()
-        repo.remove(removes, True)
+        repo.remove(util.sort(removes), True)
     for f in patches:
         ctype, gp = patches[f]
         if gp and gp.mode:
@@ -1113,9 +1112,7 @@
     cmdutil.addremove(repo, cfiles)
     files = patches.keys()
     files.extend([r for r in removes if r not in files])
-    files.sort()
-
-    return files
+    return util.sort(files)
 
 def b85diff(to, tn):
     '''print base85-encoded binary diff'''
@@ -1208,13 +1205,10 @@
         for k, v in copy.items():
             copy[v] = k
 
-    all = modified + added + removed
-    all.sort()
     gone = {}
-
     gitmode = {'l': '120000', 'x': '100755', '': '100644'}
 
-    for f in all:
+    for f in util.sort(modified + added + removed):
         to = None
         tn = None
         dodiff = True
--- a/mercurial/streamclone.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/streamclone.py	Fri Jun 27 18:28:45 2008 -0500
@@ -34,8 +34,7 @@
     for x in walk(os.path.join(root, 'data'), True):
         yield x
     # write manifest before changelog
-    meta = list(walk(root, False))
-    meta.sort()
+    meta = util.sort(walk(root, False))
     meta.reverse()
     for x in meta:
         yield x
--- a/mercurial/ui.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/ui.py	Fri Jun 27 18:28:45 2008 -0500
@@ -312,15 +312,11 @@
         items = self._configitems(section, untrusted=untrusted, abort=True)
         if self.debugflag and not untrusted and self.ucdata:
             uitems = self._configitems(section, untrusted=True, abort=False)
-            keys = uitems.keys()
-            keys.sort()
-            for k in keys:
+            for k in util.sort(uitems):
                 if uitems[k] != items.get(k):
                     self.warn(_("Ignoring untrusted configuration option "
                                 "%s.%s = %s\n") % (section, k, uitems[k]))
-        x = items.items()
-        x.sort()
-        return x
+        return util.sort(items.items())
 
     def walkconfig(self, untrusted=False):
         cdata = self._get_cdata(untrusted)
--- a/mercurial/util.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/util.py	Fri Jun 27 18:28:45 2008 -0500
@@ -252,6 +252,12 @@
     """return the uniq elements of iterable g"""
     return dict.fromkeys(g).keys()
 
+def sort(l):
+    if not isinstance(l, list):
+        l = list(l)
+    l.sort()
+    return l
+
 class Abort(Exception):
     """Raised if a command needs to print an error and exit."""
 
--- a/mercurial/verify.py	Fri Jun 27 14:53:30 2008 -0500
+++ b/mercurial/verify.py	Fri Jun 27 18:28:45 2008 -0500
@@ -7,7 +7,7 @@
 
 from node import nullid, short
 from i18n import _
-import revlog
+import revlog, util
 
 def verify(repo):
     lock = repo.lock()
@@ -139,38 +139,26 @@
     ui.status(_("crosschecking files in changesets and manifests\n"))
 
     if havemf:
-        nm = []
-        for m in mflinkrevs:
-            for c in mflinkrevs[m]:
-                nm.append((c, m))
-        nm.sort()
-        for c, m in nm:
+        for c, m in util.sort([(c, m) for m in mflinkrevs for c in mflinkrevs[m]]):
             err(c, _("changeset refers to unknown manifest %s") % short(m))
-        del mflinkrevs, nm
+        del mflinkrevs
 
-        fl = filelinkrevs.keys()
-        fl.sort()
-        for f in fl:
+        for f in util.sort(filelinkrevs):
             if f not in filenodes:
                 lr = filelinkrevs[f][0]
                 err(lr, _("in changeset but not in manifest"), f)
-        del fl
 
     if havecl:
-        fl = filenodes.keys()
-        fl.sort()
-        for f in fl:
+        for f in util.sort(filenodes):
             if f not in filelinkrevs:
                 try:
                     lr = min([repo.file(f).linkrev(n) for n in filenodes[f]])
                 except:
                     lr = None
                 err(lr, _("in manifest but not in changeset"), f)
-        del fl
 
     ui.status(_("checking files\n"))
-    files = dict.fromkeys(filenodes.keys() + filelinkrevs.keys()).keys()
-    files.sort()
+    files = util.sort(util.unique(filenodes.keys() + filelinkrevs.keys()))
     for f in files:
         fl = repo.file(f)
         checklog(fl, f)
@@ -214,8 +202,7 @@
         # cross-check
         if f in filenodes:
             fns = [(mf.linkrev(l), n) for n,l in filenodes[f].items()]
-            fns.sort()
-            for lr, node in fns:
+            for lr, node in util.sort(fns):
                 err(lr, _("%s in manifests not found") % short(node), f)
 
     ui.status(_("%d files, %d changesets, %d total revisions\n") %