changeset 6776:39319a457dda

merge with mpm
author Dirkjan Ochtman <dirkjan@ochtman.nl>
date Sat, 28 Jun 2008 09:28:01 +0200
parents 54ccf41761c9 (current diff) f67d1468ac50 (diff)
children 44c5157474e7
files mercurial/hgweb/hgweb_mod.py
diffstat 67 files changed, 714 insertions(+), 1060 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/dumprevlog	Thu Jun 26 13:37:47 2008 -0700
+++ b/contrib/dumprevlog	Sat Jun 28 09:28:01 2008 +0200
@@ -12,7 +12,7 @@
     binopen = lambda fn: open(fn, 'rb')
     r = revlog.revlog(binopen, f)
     print "file:", f
-    for i in xrange(r.count()):
+    for i in r:
         n = r.node(i)
         p = r.parents(n)
         d = r.revision(n)
--- a/hgext/acl.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/acl.py	Sat Jun 28 09:28:01 2008 +0200
@@ -91,7 +91,7 @@
 
     def check(self, node):
         '''return if access allowed, raise exception if not.'''
-        files = self.repo.changectx(node).files()
+        files = self.repo[node].files()
         if self.deniable:
             for f in files:
                 if self.deny(f):
@@ -118,7 +118,5 @@
         ui.debug(_('acl: changes have source "%s" - skipping\n') % source)
         return
 
-    start = repo.changelog.rev(bin(node))
-    end = repo.changelog.count()
-    for rev in xrange(start, end):
+    for rev in xrange(repo[node].rev(), len(repo)):
         c.check(repo.changelog.node(rev))
--- a/hgext/bugzilla.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/bugzilla.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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.'''
@@ -300,7 +296,7 @@
                          hooktype)
     try:
         bz = bugzilla(ui, repo)
-        ctx = repo.changectx(node)
+        ctx = repo[node]
         ids = bz.find_bug_ids(ctx)
         if ids:
             for id in ids:
--- a/hgext/children.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/children.py	Sat Jun 28 09:28:01 2008 +0200
@@ -25,7 +25,7 @@
     if file_:
         ctx = repo.filectx(file_, changeid=rev)
     else:
-        ctx = repo.changectx(rev)
+        ctx = repo[rev]
 
     displayer = cmdutil.show_changeset(ui, repo, opts)
     for node in [cp.node() for cp in ctx.children()]:
--- a/hgext/churn.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/churn.py	Sat Jun 28 09:28:01 2008 +0200
@@ -7,7 +7,7 @@
 '''allow graphing the number of lines changed per contributor'''
 
 from mercurial.i18n import gettext as _
-from mercurial import mdiff, cmdutil, util, node
+from mercurial import patch, cmdutil, util, node
 import os, sys
 
 def get_tty_width():
@@ -31,98 +31,41 @@
         pass
     return 80
 
-def __gather(ui, repo, node1, node2):
-    def dirtywork(f, mmap1, mmap2):
-        lines = 0
-
-        to = mmap1 and repo.file(f).read(mmap1[f]) or None
-        tn = mmap2 and repo.file(f).read(mmap2[f]) or None
-
-        diff = mdiff.unidiff(to, "", tn, "", f, f).split("\n")
-
-        for line in diff:
-            if not line:
-                continue # skip EOF
-            if line.startswith(" "):
-                continue # context line
-            if line.startswith("--- ") or line.startswith("+++ "):
-                continue # begining of diff
-            if line.startswith("@@ "):
-                continue # info line
-
-            # changed lines
-            lines += 1
-
-        return lines
-
-    ##
-
-    lines = 0
-
-    changes = repo.status(node1, node2)[:5]
-
-    modified, added, removed, deleted, unknown = changes
-
-    who = repo.changelog.read(node2)[1]
-    who = util.email(who) # get the email of the person
-
-    mmap1 = repo.manifest.read(repo.changelog.read(node1)[0])
-    mmap2 = repo.manifest.read(repo.changelog.read(node2)[0])
-    for f in modified:
-        lines += dirtywork(f, mmap1, mmap2)
-
-    for f in added:
-        lines += dirtywork(f, None, mmap2)
-
-    for f in removed:
-        lines += dirtywork(f, mmap1, None)
-
-    for f in deleted:
-        lines += dirtywork(f, mmap1, mmap2)
-
-    for f in unknown:
-        lines += dirtywork(f, mmap1, mmap2)
-
-    return (who, lines)
-
-def gather_stats(ui, repo, amap, revs=None, progress=False):
+def countrevs(ui, repo, amap, revs, progress=False):
     stats = {}
-
-    cl    = repo.changelog
-
+    count = pct = 0
     if not revs:
-        revs = range(0, cl.count())
-
-    nr_revs = len(revs)
-    cur_rev = 0
+        revs = range(len(repo))
 
     for rev in revs:
-        cur_rev += 1 # next revision
-
-        node2    = cl.node(rev)
-        node1    = cl.parents(node2)[0]
-
-        if cl.parents(node2)[1] != node.nullid:
+        ctx2 = repo[rev]
+        parents = ctx2.parents()
+        if len(parents) > 1:
             ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
             continue
 
-        who, lines = __gather(ui, repo, node1, node2)
+        ctx1 = parents[0]
+        lines = 0
+        ui.pushbuffer()
+        patch.diff(repo, ctx1.node(), ctx2.node())
+        diff = ui.popbuffer()
 
-        # remap the owner if possible
-        if who in amap:
-            ui.note("using '%s' alias for '%s'\n" % (amap[who], who))
-            who = amap[who]
+        for l in diff.split('\n'):
+            if (l.startswith("+") and not l.startswith("+++ ") or
+                l.startswith("-") and not l.startswith("--- ")):
+                lines += 1
 
-        if not who in stats:
-            stats[who] = 0
-        stats[who] += lines
-
-        ui.note("rev %d: %d lines by %s\n" % (rev, lines, who))
+        user = util.email(ctx2.user())
+        user = amap.get(user, user) # remap
+        stats[user] = stats.get(user, 0) + lines
+        ui.debug("rev %d: %d lines by %s\n" % (rev, lines, user))
 
         if progress:
-            nr_revs = max(nr_revs, 1)
-            if int(100.0*(cur_rev - 1)/nr_revs) < int(100.0*cur_rev/nr_revs):
-                ui.write("\rGenerating stats: %d%%" % (int(100.0*cur_rev/nr_revs),))
+            count += 1
+            newpct = int(100.0 * count / max(len(revs), 1))
+            if pct < newpct:
+                pct = newpct
+                ui.write("\rGenerating stats: %d%%" % pct)
                 sys.stdout.flush()
 
     if progress:
@@ -139,61 +82,32 @@
     <alias email> <actual email>'''
 
     def pad(s, l):
-        if len(s) < l:
-            return s + " " * (l-len(s))
-        return s[0:l]
-
-    def graph(n, maximum, width, char):
-        maximum = max(1, maximum)
-        n = int(n * width / float(maximum))
-
-        return char * (n)
-
-    def get_aliases(f):
-        aliases = {}
-
-        for l in f.readlines():
-            l = l.strip()
-            alias, actual = l.split()
-            aliases[alias] = actual
-
-        return aliases
+        return (s + " " * l)[:l]
 
     amap = {}
     aliases = opts.get('aliases')
     if aliases:
-        try:
-            f = open(aliases,"r")
-        except OSError, e:
-            print "Error: " + e
-            return
+        for l in open(aliases, "r"):
+            l = l.strip()
+            alias, actual = l.split()
+            amap[alias] = actual
 
-        amap = get_aliases(f)
-        f.close()
-
-    revs = [int(r) for r in cmdutil.revrange(repo, opts['rev'])]
-    revs.sort()
-    stats = gather_stats(ui, repo, amap, revs, opts.get('progress'))
+    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
 
-    # make a list of tuples (name, lines) and sort it in descending order
-    ordered = stats.items()
-    if not ordered:
-        return
-    ordered.sort(lambda x, y: cmp(y[1], x[1]))
-    max_churn = ordered[0][1]
+    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])
 
-    tty_width = get_tty_width()
-    ui.note(_("assuming %i character terminal\n") % tty_width)
-    tty_width -= 1
-
-    max_user_width = max([len(user) for user, churn in ordered])
+    ttywidth = get_tty_width()
+    ui.debug(_("assuming %i character terminal\n") % ttywidth)
+    width = ttywidth - maxuser - 2 - 6 - 2 - 2
 
-    graph_width = tty_width - max_user_width - 1 - 6 - 2 - 2
-
-    for user, churn in ordered:
-        print "%s %6d %s" % (pad(user, max_user_width),
-                             churn,
-                             graph(churn, max_churn, graph_width, '*'))
+    for k, user, churn in stats:
+        print "%s %6d %s" % (pad(user, maxuser), churn,
+                             "*" * int(churn * width / maxchurn))
 
 cmdtable = {
     "churn":
--- a/hgext/convert/cvs.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/convert/cvs.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/convert/cvsps.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/convert/darcs.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/convert/gnuarch.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/convert/hg.py	Sat Jun 28 09:28:01 2008 +0200
@@ -157,21 +157,18 @@
 
     def puttags(self, tags):
          try:
-             parentctx = self.repo.changectx(self.tagsbranch)
+             parentctx = self.repo[self.tagsbranch]
              tagparent = parentctx.node()
          except RepoError, inst:
              parentctx = None
              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
@@ -212,25 +209,24 @@
 
     def changectx(self, rev):
         if self.lastrev != rev:
-            self.lastctx = self.repo.changectx(rev)
+            self.lastctx = self.repo[rev]
             self.lastrev = rev
         return self.lastctx
 
     def getheads(self):
         if self.rev:
-            return [hex(self.repo.changectx(self.rev).node())]
+            return [hex(self.repo[self.rev].node())]
         else:
             return [hex(node) for node in self.repo.heads()]
 
     def getfile(self, name, rev):
         try:
-            return self.changectx(rev).filectx(name).data()
+            return self.changectx(rev)[name].data()
         except revlog.LookupError, err:
             raise IOError(err)
 
     def getmode(self, name, rev):
-        m = self.changectx(rev).manifest()
-        return (m.execf(name) and 'x' or '') + (m.linkf(name) and 'l' or '')
+        return self.changectx(rev).manifest().flags(name)
 
     def getchanges(self, rev):
         ctx = self.changectx(rev)
@@ -239,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	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/convert/subversion.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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/extdiff.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/extdiff.py	Sat Jun 28 09:28:01 2008 +0200
@@ -52,7 +52,6 @@
 
 def snapshot_node(ui, repo, files, node, tmproot):
     '''snapshot files as of some revision'''
-    mf = repo.changectx(node).manifest()
     dirname = os.path.basename(repo.root)
     if dirname == "":
         dirname = "root"
@@ -61,17 +60,18 @@
     os.mkdir(base)
     ui.note(_('making snapshot of %d files from rev %s\n') %
             (len(files), short(node)))
+    ctx = repo[node]
     for fn in files:
-        if not fn in mf:
+        wfn = util.pconvert(fn)
+        if not wfn in ctx:
             # skipping new file after a merge ?
             continue
-        wfn = util.pconvert(fn)
         ui.note('  %s\n' % wfn)
         dest = os.path.join(base, wfn)
         destdir = os.path.dirname(dest)
         if not os.path.isdir(destdir):
             os.makedirs(destdir)
-        data = repo.wwritedata(wfn, repo.file(wfn).read(mf[wfn]))
+        data = repo.wwritedata(wfn, ctx[wfn].data())
         open(dest, 'wb').write(data)
     return dirname
 
@@ -122,8 +122,7 @@
     '''
     node1, node2 = cmdutil.revpair(repo, opts['rev'])
     matcher = cmdutil.match(repo, pats, opts)
-    modified, added, removed, deleted, unknown = repo.status(
-        node1, node2, matcher)[:5]
+    modified, added, removed = repo.status(node1, node2, matcher)[:3]
     if not (modified or added or removed):
         return 0
 
--- a/hgext/gpg.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/gpg.py	Sat Jun 28 09:28:01 2008 +0200
@@ -239,7 +239,7 @@
         repo.opener("localsigs", "ab").write(sigmessage)
         return
 
-    for x in repo.status()[:5]:
+    for x in repo.status(unknown=True)[:5]:
         if ".hgsigs" in x and not opts["force"]:
             raise util.Abort(_("working copy of .hgsigs is changed "
                                "(please commit .hgsigs manually "
--- a/hgext/graphlog.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/graphlog.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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:
@@ -89,7 +89,7 @@
     assert start_rev >= stop_rev
     curr_rev = start_rev
     revs = []
-    filerev = repo.file(path).count() - 1
+    filerev = len(repo.file(path)) - 1
     while filerev >= 0:
         fctx = repo.filectx(path, fileid=filerev)
 
@@ -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:
@@ -198,7 +197,7 @@
         revs = revrange(repo, rev_opt)
         return (max(revs), min(revs))
     else:
-        return (repo.changelog.count() - 1, 0)
+        return (len(repo) - 1, 0)
 
 def graphlog(ui, repo, path=None, **opts):
     """show revision history alongside an ASCII revision graph
--- a/hgext/hgk.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/hgk.py	Sat Jun 28 09:28:01 2008 +0200
@@ -52,12 +52,10 @@
     """diff trees from two commits"""
     def __difftree(repo, node1, node2, files=[]):
         assert node2 is not None
-        mmap = repo.changectx(node1).manifest()
-        mmap2 = repo.changectx(node2).manifest()
+        mmap = repo[node1].manifest()
+        mmap2 = repo[node2].manifest()
         m = cmdutil.match(repo, files)
-        status = repo.status(node1, node2, match=m)[:5]
-        modified, added, removed, deleted, unknown = status
-
+        modified, added, removed  = repo.status(node1, node2, m)[:3]
         empty = short(nullid)
 
         for f in modified:
@@ -103,7 +101,7 @@
 def catcommit(ui, repo, n, prefix, ctx=None):
     nlprefix = '\n' + prefix;
     if ctx is None:
-        ctx = repo.changectx(n)
+        ctx = repo[n]
     (p1, p2) = ctx.parents()
     ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ??
     if p1: ui.write("parent %s\n" % short(p1.node()))
@@ -175,7 +173,7 @@
 # you can specify a commit to stop at by starting the sha1 with ^
 def revtree(ui, args, repo, full="tree", maxnr=0, parents=False):
     def chlogwalk():
-        count = repo.changelog.count()
+        count = len(repo)
         i = count
         l = [0] * 100
         chunk = 100
@@ -191,7 +189,7 @@
                     l[chunk - x:] = [0] * (chunk - x)
                     break
                 if full != None:
-                    l[x] = repo.changectx(i + x)
+                    l[x] = repo[i + x]
                     l[x].changeset() # force reading
                 else:
                     l[x] = 1
--- a/hgext/inotify/__init__.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/inotify/__init__.py	Sat Jun 28 09:28:01 2008 +0200
@@ -47,13 +47,12 @@
         # to recurse.
         inotifyserver = False
 
-        def status(self, match, list_ignored, list_clean,
-                   list_unknown=True):
+        def status(self, match, ignored, clean, unknown=True):
             files = match.files()
             try:
-                if not list_ignored and not self.inotifyserver:
+                if not ignored and not self.inotifyserver:
                     result = client.query(ui, repo, files, match, False,
-                                          list_clean, list_unknown)
+                                          clean, unknown)
                     if result is not None:
                         return result
             except socket.error, err:
@@ -82,15 +81,14 @@
                     if query:
                         try:
                             return query(ui, repo, files or [], match,
-                                         list_ignored, list_clean, list_unknown)
+                                         ignored, clean, unknown)
                         except socket.error, err:
                             ui.warn(_('could not talk to new inotify '
                                            'server: %s\n') % err[1])
                             ui.print_exc()
 
             return super(inotifydirstate, self).status(
-                match, list_ignored, list_clean,
-                list_unknown)
+                match, ignored, clean, unknown)
 
     repo.dirstate.__class__ = inotifydirstate
 
--- a/hgext/inotify/client.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/inotify/client.py	Sat Jun 28 09:28:01 2008 +0200
@@ -11,7 +11,7 @@
 import common
 import os, select, socket, stat, struct, sys
 
-def query(ui, repo, names, match, list_ignored, list_clean, list_unknown=True):
+def query(ui, repo, names, match, ignored, clean, unknown=True):
     sock = socket.socket(socket.AF_UNIX)
     sockpath = repo.join('inotify.sock')
     sock.connect(sockpath)
@@ -20,10 +20,10 @@
         for n in names or []:
             yield n
         states = 'almrx!'
-        if list_ignored:
+        if ignored:
             raise ValueError('this is insanity')
-        if list_clean: states += 'n'
-        if list_unknown: states += '?'
+        if clean: states += 'n'
+        if unknown: states += '?'
         yield states
 
     req = '\0'.join(genquery())
--- a/hgext/inotify/server.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/inotify/server.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/keyword.py	Sat Jun 28 09:28:01 2008 +0200
@@ -78,7 +78,7 @@
 "Log = {desc}" expands to the first line of the changeset description.
 '''
 
-from mercurial import commands, cmdutil, context, dispatch, filelog, revlog
+from mercurial import commands, cmdutil, dispatch, filelog, revlog
 from mercurial import patch, localrepo, templater, templatefilters, util
 from mercurial.hgweb import webcommands
 from mercurial.node import nullid, hex
@@ -142,7 +142,7 @@
     def getnode(self, path, fnode):
         '''Derives changenode from file path and filenode.'''
         # used by kwfilelog.read and kwexpand
-        c = context.filectx(self.repo, path, fileid=fnode)
+        c = self.repo.filectx(path, fileid=fnode)
         return c.node()
 
     def substitute(self, data, path, node, subfunc):
@@ -163,25 +163,26 @@
             return self.substitute(data, path, changenode, self.re_kw.sub)
         return data
 
-    def iskwfile(self, path, islink):
+    def iskwfile(self, path, flagfunc):
         '''Returns true if path matches [keyword] pattern
         and is not a symbolic link.
         Caveat: localrepository._link fails on Windows.'''
-        return self.matcher(path) and not islink(path)
+        return self.matcher(path) and not 'l' in flagfunc(path)
 
     def overwrite(self, node, expand, files):
         '''Overwrites selected files expanding/shrinking keywords.'''
-        ctx = self.repo.changectx(node)
-        mf = ctx.manifest()
         if node is not None:     # commit
+            ctx = self.repo[node]
+            mf = ctx.manifest()
             files = [f for f in ctx.files() if f in mf]
             notify = self.ui.debug
         else:                    # kwexpand/kwshrink
+            ctx = self.repo['.']
+            mf = ctx.manifest()
             notify = self.ui.note
-        candidates = [f for f in files if self.iskwfile(f, mf.linkf)]
+        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)
@@ -251,12 +252,12 @@
             return t2 != text
         return revlog.revlog.cmp(self, node, text)
 
-def _status(ui, repo, kwt, *pats, **opts):
+def _status(ui, repo, kwt, unknown, *pats, **opts):
     '''Bails out if [keyword] configuration is not active.
     Returns status of working directory.'''
     if kwt:
         matcher = cmdutil.match(repo, pats, opts)
-        return repo.status(match=matcher, list_clean=True)
+        return repo.status(match=matcher, unknown=unknown, clean=True)
     if ui.configitems('keyword'):
         raise util.Abort(_('[keyword] patterns cannot match'))
     raise util.Abort(_('no [keyword] patterns configured'))
@@ -266,15 +267,15 @@
     if repo.dirstate.parents()[1] != nullid:
         raise util.Abort(_('outstanding uncommitted merge'))
     kwt = kwtools['templater']
-    status = _status(ui, repo, kwt, *pats, **opts)
-    modified, added, removed, deleted, unknown, ignored, clean = status
+    status = _status(ui, repo, kwt, False, *pats, **opts)
+    modified, added, removed, deleted = status[:4]
     if modified or added or removed or deleted:
         raise util.Abort(_('outstanding uncommitted changes'))
     wlock = lock = None
     try:
         wlock = repo.wlock()
         lock = repo.lock()
-        kwt.overwrite(None, expand, clean)
+        kwt.overwrite(None, expand, status[6])
     finally:
         del wlock, lock
 
@@ -378,15 +379,11 @@
     That is, files matched by [keyword] config patterns but not symlinks.
     '''
     kwt = kwtools['templater']
-    status = _status(ui, repo, kwt, *pats, **opts)
+    status = _status(ui, repo, kwt, opts.get('untracked'), *pats, **opts)
     modified, added, removed, deleted, unknown, ignored, clean = status
-    files = modified + added + clean
-    if opts.get('untracked'):
-        files += unknown
-    files.sort()
-    wctx = repo.workingctx()
-    islink = lambda p: 'l' in wctx.fileflags(p)
-    kwfiles = [f for f in files if kwt.iskwfile(f, islink)]
+    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 ''
     kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
     if opts.get('all') or opts.get('ignore'):
@@ -511,7 +508,7 @@
         comparing against working dir.'''
         if node2 is not None:
             kwt.matcher = util.never
-        elif node1 is not None and node1 != repo.changectx().node():
+        elif node1 is not None and node1 != repo['.'].node():
             kwt.restrict = True
         patch_diff(repo, node1, node2, match, fp, changes, opts)
 
--- a/hgext/mq.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/mq.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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
@@ -342,7 +341,7 @@
         hg.clean(repo, head)
         self.strip(repo, n, update=False, backup='strip')
 
-        ctx = repo.changectx(rev)
+        ctx = repo[rev]
         ret = hg.merge(repo, rev)
         if ret:
             raise util.Abort(_("update returned %d") % ret)
@@ -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)
@@ -852,7 +850,7 @@
                 self.ui.warn(_('cleaning up working directory...'))
                 node = repo.dirstate.parents()[0]
                 hg.revert(repo, node, None)
-                unknown = repo.status()[4]
+                unknown = repo.status(unknown=True)[4]
                 # only remove unknown files that we know we touched or
                 # created while patching
                 for f in unknown:
@@ -933,7 +931,7 @@
                 qp = self.qparents(repo, rev)
                 changes = repo.changelog.read(qp)
                 mmap = repo.manifest.read(changes[0])
-                m, a, r, d, u = repo.status(qp, top)[:5]
+                m, a, r, d = repo.status(qp, top)[:4]
                 if d:
                     raise util.Abort("deletions found between repo revs")
                 for f in m:
@@ -1066,11 +1064,11 @@
                 # patch already
                 #
                 # this should really read:
-                #   mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
+                #   mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
                 # but we do it backwards to take advantage of manifest/chlog
                 # caching against the next repo.status call
                 #
-                mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
+                mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
                 changes = repo.changelog.read(tip)
                 man = repo.manifest.read(changes[0])
                 aaa = aa[:]
@@ -1078,7 +1076,7 @@
                     match = cmdutil.matchfiles(repo, mm + aa + dd)
                 else:
                     match = cmdutil.matchall(repo)
-                m, a, r, d, u = repo.status(match=match)[:5]
+                m, a, r, d = repo.status(match=match)[:4]
 
                 # we might end up with files that were added between
                 # tip and the dirstate parent, but then changed in the
@@ -1111,7 +1109,7 @@
                 m = util.unique(mm)
                 r = util.unique(dd)
                 a = util.unique(aa)
-                c = [filter(matchfn, l) for l in (m, a, r, [], u)]
+                c = [filter(matchfn, l) for l in (m, a, r)]
                 match = cmdutil.matchfiles(repo, util.unique(c[0] + c[1] + c[2]))
                 patch.diff(repo, patchparent, match=match,
                            fp=patchf, changes=c, opts=self.diffopts())
@@ -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)))
 
@@ -2319,7 +2316,7 @@
             # we might as well use it, but we won't save it.
 
             # update the cache up to the tip
-            self._updatebranchcache(partial, start, cl.count())
+            self._updatebranchcache(partial, start, len(cl))
 
             return partial
 
--- a/hgext/notify.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/notify.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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)
@@ -269,11 +267,11 @@
     node = bin(node)
     ui.pushbuffer()
     if hooktype == 'changegroup':
-        start = repo.changelog.rev(node)
-        end = repo.changelog.count()
+        start = repo[node].rev()
+        end = len(repo)
         count = end - start
         for rev in xrange(start, end):
-            n.node(repo.changelog.node(rev))
+            n.node(repo[node].rev())
         n.diff(node, repo.changelog.tip())
     else:
         count = 1
--- a/hgext/purge.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/purge.py	Sat Jun 28 09:28:01 2008 +0200
@@ -56,79 +56,42 @@
     files that this program would delete use the --print option.
     '''
     act = not opts['print']
-    ignored = bool(opts['all'])
-    abort_on_err = bool(opts['abort_on_err'])
-    eol = opts['print0'] and '\0' or '\n'
-    if eol == '\0':
-        # --print0 implies --print
-        act = False
-    force = bool(opts['force'])
-
-    def error(msg):
-        if abort_on_err:
-            raise util.Abort(msg)
-        else:
-            ui.warn(_('warning: %s\n') % msg)
+    eol = '\n'
+    if opts['print0']:
+        eol = '\0'
+        act = False # --print0 implies --print
 
     def remove(remove_func, name):
         if act:
             try:
                 remove_func(os.path.join(repo.root, name))
             except OSError, e:
-                error(_('%s cannot be removed') % name)
+                m = _('%s cannot be removed') % name
+                if opts['abort_on_err']:
+                    raise util.Abort(m)
+                ui.warn(_('warning: %s\n') % m)
         else:
             ui.write('%s%s' % (name, eol))
 
-    if not force:
-        _check_fs(ui, repo)
-
     directories = []
-    files = []
     match = cmdutil.match(repo, dirs, opts)
     match.dir = directories.append
-    for src, f, st in repo.dirstate.statwalk(match, ignored=ignored):
-        if src == 'f' and f not in repo.dirstate:
-            files.append(f)
-
-    directories.sort()
+    status = repo.status(match=match, ignored=opts['all'], unknown=True)
 
-    for f in files:
-        if f not in repo.dirstate:
-            ui.note(_('Removing file %s\n') % f)
-            remove(os.remove, f)
+    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)
 
-def _check_fs(ui, repo):
-    """Abort if there is the chance of having problems with name-mangling fs
-
-    In a name mangling filesystem (e.g. a case insensitive one)
-    dirstate.walk() can yield filenames different from the ones
-    stored in the dirstate. This already confuses the status and
-    add commands, but with purge this may cause data loss.
-
-    To prevent this, this function will abort if there are uncommitted
-    changes.
-    """
-
-    # We can't use (files, match) to do a partial walk here - we wouldn't
-    # notice a modified README file if the user ran "hg purge readme"
-    modified, added, removed, deleted = repo.status()[:4]
-    if modified or added or removed or deleted:
-        if not util.checkfolding(repo.path) and not ui.quiet:
-            ui.warn(_("Purging on name mangling filesystems is not "
-                      "fully supported.\n"))
-        raise util.Abort(_("outstanding uncommitted changes"))
-
 cmdtable = {
     'purge|clean':
         (purge,
          [('a', 'abort-on-err', None, _('abort if an error occurs')),
           ('',  'all', None, _('purge ignored files too')),
-          ('f', 'force', None, _('purge even when there are uncommitted changes')),
           ('p', 'print', None, _('print the file names instead of deleting them')),
           ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
                                   ' (implies -p)')),
--- a/hgext/record.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/record.py	Sat Jun 28 09:28:01 2008 +0200
@@ -405,8 +405,8 @@
         if match.files():
             changes = None
         else:
-            changes = repo.status(match=match)[:5]
-            modified, added, removed = changes[:3]
+            changes = repo.status(match=match)[:3]
+            modified, added, removed = changes
             match = cmdutil.matchfiles(repo, modified + added + removed)
         diffopts = mdiff.diffopts(git=True, nodates=True)
         fp = cStringIO.StringIO()
@@ -431,7 +431,7 @@
 
         if changes is None:
             match = cmdutil.matchfiles(repo, newfiles)
-            changes = repo.status(match=match)[:5]
+            changes = repo.status(match=match)
         modified = dict.fromkeys(changes[0])
 
         # 2. backup changed files, so we can restore them in the end
--- a/hgext/transplant.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/transplant.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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/hgext/win32text.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/hgext/win32text.py	Sat Jun 28 09:28:01 2008 +0200
@@ -98,8 +98,8 @@
 
 def forbidnewline(ui, repo, hooktype, node, newline, **kwargs):
     halt = False
-    for rev in xrange(repo.changelog.rev(bin(node)), repo.changelog.count()):
-        c = repo.changectx(rev)
+    for rev in xrange(repo[node].rev(), len(repo)):
+        c = repo[rev]
         for f in c.files():
             if f not in c:
                 continue
--- a/mercurial/archival.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/archival.py	Sat Jun 28 09:28:01 2008 +0200
@@ -208,18 +208,17 @@
             data = repo.wwritedata(name, data)
         archiver.addfile(name, mode, islink, data)
 
-    ctx = repo.changectx(node)
     if kind not in archivers:
         raise util.Abort(_("unknown archive type '%s'" % kind))
+
+    ctx = repo[node]
     archiver = archivers[kind](dest, prefix, mtime or ctx.date()[0])
-    m = ctx.manifest()
-    items = m.items()
-    items.sort()
+
     if repo.ui.configbool("ui", "archivemeta", True):
         write('.hg_archival.txt', 0644, False,
               lambda: 'repo: %s\nnode: %s\n' % (
                   hex(repo.changelog.node(0)), hex(node)))
-    for filename, filenode in items:
-        write(filename, m.execf(filename) and 0755 or 0644, m.linkf(filename),
-              lambda: repo.file(filename).read(filenode))
+    for f in ctx:
+        ff = ctx.flags(f)
+        write(f, 'x' in ff and 0755 or 0644, 'l' in ff, ctx[f].data)
     archiver.done()
--- a/mercurial/bundlerepo.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/bundlerepo.py	Sat Jun 28 09:28:01 2008 +0200
@@ -34,7 +34,7 @@
             for chunk in changegroup.chunkiter(bundlefile):
                 pos = bundlefile.tell()
                 yield chunk, pos - len(chunk)
-        n = self.count()
+        n = len(self)
         prev = None
         for chunk, start in chunkpositer():
             size = len(chunk)
--- a/mercurial/changelog.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/changelog.py	Sat Jun 28 09:28:01 2008 +0200
@@ -82,7 +82,7 @@
         "delay visibility of index updates to other readers"
         self._realopener = self.opener
         self.opener = self._delayopener
-        self._delaycount = self.count()
+        self._delaycount = len(self)
         self._delaybuf = []
         self._delayname = None
 
@@ -108,7 +108,7 @@
         # if we're doing an initial clone, divert to another file
         if self._delaycount == 0:
             self._delayname = fp.name
-            if not self.count():
+            if not len(self):
                 # make sure to truncate the file
                 mode = mode.replace('a', 'w')
             return self._realopener(name + ".a", mode)
@@ -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, self.count(), p1, p2)
+        return self.addrevision(text, transaction, len(self), p1, p2)
--- a/mercurial/cmdutil.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/cmdutil.py	Sat Jun 28 09:28:01 2008 +0200
@@ -126,7 +126,7 @@
         if revrangesep in revs[0]:
             start, end = revs[0].split(revrangesep, 1)
             start = revfix(repo, start, 0)
-            end = revfix(repo, end, repo.changelog.count() - 1)
+            end = revfix(repo, end, len(repo) - 1)
         else:
             start = revfix(repo, revs[0], None)
     elif len(revs) == 2:
@@ -151,7 +151,7 @@
         if revrangesep in spec:
             start, end = spec.split(revrangesep, 1)
             start = revfix(repo, start, 0)
-            end = revfix(repo, end, repo.changelog.count() - 1)
+            end = revfix(repo, end, len(repo) - 1)
             step = start > end and -1 or 1
             for rev in xrange(start, end+step, step):
                 if rev in seen:
@@ -245,7 +245,7 @@
     '''find renamed files -- yields (before, after, score) tuples'''
     if added is None or removed is None:
         added, removed = repo.status()[1:3]
-    ctx = repo.changectx()
+    ctx = repo['.']
     for a in added:
         aa = repo.wread(a)
         bestname, bestscore = None, threshold
@@ -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)
@@ -930,7 +926,7 @@
 def finddate(ui, repo, date):
     """Find the tipmost changeset that matches the given date spec"""
     df = util.matchdate(date)
-    get = util.cachefunc(lambda r: repo.changectx(r).changeset())
+    get = util.cachefunc(lambda r: repo[r].changeset())
     changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
     results = {}
     for st, rev, fns in changeiter:
@@ -988,11 +984,11 @@
     m = match(repo, pats, opts)
     follow = opts.get('follow') or opts.get('follow_first')
 
-    if repo.changelog.count() == 0:
+    if not len(repo):
         return [], m
 
     if follow:
-        defrange = '%s:0' % repo.changectx().rev()
+        defrange = '%s:0' % repo['.'].rev()
     else:
         defrange = '-1:0'
     revs = revrange(repo, opts['rev'] or [defrange])
@@ -1007,9 +1003,9 @@
     if not slowpath:
         # Only files, no patterns.  Check the history of each file.
         def filerevgen(filelog, node):
-            cl_count = repo.changelog.count()
+            cl_count = len(repo)
             if node is None:
-                last = filelog.count() - 1
+                last = len(filelog) - 1
             else:
                 last = filelog.rev(node)
             for i, window in increasing_windows(last, nullrev):
@@ -1032,7 +1028,7 @@
         minrev, maxrev = min(revs), max(revs)
         for file_, node in iterfiles():
             filelog = repo.file(file_)
-            if filelog.count() == 0:
+            if not len(filelog):
                 if node is None:
                     # A zero count may be a directory or deleted file, so
                     # try to find matching entries on the slow path.
@@ -1058,8 +1054,7 @@
 
         # The slow path checks files modified in every changeset.
         def changerevgen():
-            for i, window in increasing_windows(repo.changelog.count()-1,
-                                                nullrev):
+            for i, window in increasing_windows(len(repo) - 1, nullrev):
                 for j in xrange(i - window, i + 1):
                     yield j, change(j)[3]
 
@@ -1130,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,9 +1152,8 @@
 
     m = match(repo, pats, opts)
     if pats:
-        status = repo.status(match=m)
-        modified, added, removed, deleted, unknown = status[:5]
-        files = modified + added + removed
+        modified, added, removed = repo.status(match=m)[:3]
+        files = util.sort(modified + added + removed)
         slist = None
         for f in m.files():
             if f == '.':
@@ -1175,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	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/commands.py	Sat Jun 28 09:28:01 2008 +0200
@@ -107,7 +107,7 @@
         lastfunc = funcmap[-1]
         funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
 
-    ctx = repo.changectx(opts['rev'])
+    ctx = repo[opts['rev']]
 
     m = cmdutil.match(repo, pats, opts)
     for abs in repo.walk(m, ctx.node()):
@@ -154,7 +154,7 @@
     The default is the basename of the archive, with suffixes removed.
     '''
 
-    ctx = repo.changectx(opts['rev'])
+    ctx = repo[opts['rev']]
     if not ctx:
         raise util.Abort(_('repository has no revisions'))
     node = ctx.node()
@@ -189,7 +189,7 @@
     hand. The result of this merge is not committed, as for a normal
     merge.
 
-    See 'hg help dates' for a list of formats valid for -d/--date.
+    See \'hg help dates\' for a list of formats valid for -d/--date.
     '''
     if rev and node:
         raise util.Abort(_("please specify just one revision"))
@@ -359,7 +359,7 @@
 
     if label:
         if not opts.get('force') and label in repo.branchtags():
-            if label not in [p.branch() for p in repo.workingctx().parents()]:
+            if label not in [p.branch() for p in repo.parents()]:
                 raise util.Abort(_('a branch of the same name already exists'
                                    ' (use --force to override)'))
         repo.dirstate.setbranch(util.fromlocal(label))
@@ -378,11 +378,10 @@
     Use the command 'hg update' to switch to an existing branch.
     """
     hexfunc = ui.debugflag and hex or short
-    activebranches = [util.tolocal(repo.changectx(n).branch())
+    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:
@@ -483,7 +482,7 @@
     %d   dirname of file being printed, or '.' if in repo root
     %p   root-relative path name of file being printed
     """
-    ctx = repo.changectx(opts['rev'])
+    ctx = repo[opts['rev']]
     err = 1
     m = cmdutil.match(repo, (file1,) + pats, opts)
     for abs in repo.walk(m, ctx.node()):
@@ -635,35 +634,30 @@
         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('')
     ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
     ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
-    ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
+    ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
                                 and 'yes' or 'no'))
     os.unlink('.debugfsinfo')
 
-def debugrebuildstate(ui, repo, rev=""):
+def debugrebuildstate(ui, repo, rev="tip"):
     """rebuild the dirstate as it would look like for the given revision"""
-    if rev == "":
-        rev = repo.changelog.tip()
-    ctx = repo.changectx(rev)
-    files = ctx.manifest()
+    ctx = repo[rev]
     wlock = repo.wlock()
     try:
-        repo.dirstate.rebuild(rev, files)
+        repo.dirstate.rebuild(ctx.node(), ctx.manifest())
     finally:
         del wlock
 
 def debugcheckstate(ui, repo):
     """validate the correctness of the current dirstate"""
     parent1, parent2 = repo.dirstate.parents()
-    m1 = repo.changectx(parent1).manifest()
-    m2 = repo.changectx(parent2).manifest()
+    m1 = repo[parent1].manifest()
+    m2 = repo[parent2].manifest()
     errors = 0
     for f in repo.dirstate:
         state = repo.dirstate[f]
@@ -730,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
@@ -776,7 +768,7 @@
     r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
     ui.write("   rev    offset  length   base linkrev" +
              " nodeid       p1           p2\n")
-    for i in xrange(r.count()):
+    for i in r:
         node = r.node(i)
         try:
             pp = r.parents(node)
@@ -790,7 +782,7 @@
     """dump an index DAG as a .dot file"""
     r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
     ui.write("digraph G {\n")
-    for i in xrange(r.count()):
+    for i in r:
         node = r.node(i)
         pp = r.parents(node)
         ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
@@ -913,7 +905,7 @@
 def debugrename(ui, repo, file1, *pats, **opts):
     """dump rename information"""
 
-    ctx = repo.changectx(opts.get('rev', 'tip'))
+    ctx = repo[opts.get('rev')]
     m = cmdutil.match(repo, (file1,) + pats, opts)
     for abs in repo.walk(m, ctx.node()):
         fctx = ctx.filectx(abs)
@@ -1122,7 +1114,7 @@
 
     fstate = {}
     skip = {}
-    get = util.cachefunc(lambda r: repo.changectx(r).changeset())
+    get = util.cachefunc(lambda r: repo[r].changeset())
     changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
     found = False
     follow = opts.get('follow')
@@ -1130,7 +1122,7 @@
         if st == 'window':
             matches.clear()
         elif st == 'add':
-            ctx = repo.changectx(rev)
+            ctx = repo[rev]
             matches[rev] = {}
             for fn in fns:
                 if fn in skip:
@@ -1145,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:
@@ -1165,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], {}):
@@ -1202,7 +1190,7 @@
         heads = []
         visitedset = util.set()
         for branchrev in branchrevs:
-            branch = repo.changectx(branchrev).branch()
+            branch = repo[branchrev].branch()
             if branch in visitedset:
                 continue
             visitedset.add(branch)
@@ -1307,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:
@@ -1454,7 +1441,7 @@
                 "can't query remote revision number, branch, or tags")
         output = [hexfunc(srepo.lookup(rev))]
     elif not rev:
-        ctx = repo.workingctx()
+        ctx = repo[None]
         parents = ctx.parents()
         changed = False
         if default or id or num:
@@ -1466,7 +1453,7 @@
             output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
                                     (changed) and "+" or ""))
     else:
-        ctx = repo.changectx(rev)
+        ctx = repo[rev]
         if default or id:
             output = [hexfunc(ctx.node())]
         if num:
@@ -1563,7 +1550,7 @@
                     message = None
                 ui.debug(_('message:\n%s\n') % message)
 
-                wp = repo.workingctx().parents()
+                wp = repo.parents()
                 if opts.get('exact'):
                     if not nodeid or not p1:
                         raise util.Abort(_('not a mercurial patch'))
@@ -1756,7 +1743,7 @@
 
     """
 
-    get = util.cachefunc(lambda r: repo.changectx(r).changeset())
+    get = util.cachefunc(lambda r: repo[r].changeset())
     changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
 
     limit = cmdutil.loglimit(opts)
@@ -1765,7 +1752,7 @@
     if opts['copies'] and opts['rev']:
         endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
     else:
-        endrev = repo.changelog.count()
+        endrev = len(repo)
     rcache = {}
     ncache = {}
     def getrenamed(fn, rev):
@@ -1777,7 +1764,7 @@
             rcache[fn] = {}
             ncache[fn] = {}
             fl = repo.file(fn)
-            for i in xrange(fl.count()):
+            for i in fl:
                 node = fl.node(i)
                 lr = fl.linkrev(node)
                 renamed = fl.renamed(node)
@@ -1793,7 +1780,7 @@
         # filectx logic.
 
         try:
-            return repo.changectx(rev).filectx(fn).renamed()
+            return repo[rev][fn].renamed()
         except revlog.LookupError:
             pass
         return None
@@ -1869,17 +1856,13 @@
     if not node:
         node = rev
 
-    m = repo.changectx(node).manifest()
-    files = m.keys()
-    files.sort()
-
-    for f in files:
+    decor = {'l':'644 @ ', 'x':'755 * ', '':'644   '}
+    ctx = repo[node]
+    for f in ctx:
         if ui.debugflag:
-            ui.write("%40s " % hex(m[f]))
+            ui.write("%40s " % hex(ctx.manifest()[f]))
         if ui.verbose:
-            type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
-            perm = m.execf(f) and "755" or "644"
-            ui.write("%3s %1s " % (perm, type))
+            ui.write(decor[ctx.flags(f)])
         ui.write("%s\n" % f)
 
 def merge(ui, repo, node=None, force=None, rev=None):
@@ -1902,7 +1885,7 @@
         node = rev
 
     if not node:
-        branch = repo.workingctx().branch()
+        branch = repo.changectx(None).branch()
         bheads = repo.branchheads()
         if len(bheads) > 2:
             raise util.Abort(_("branch '%s' has %d heads - "
@@ -1916,7 +1899,7 @@
                                    "please merge with an explicit rev") %
                                  branch)
             msg = _('there is nothing to merge')
-            if parent != repo.lookup(repo.workingctx().branch()):
+            if parent != repo.lookup(repo[None].branch()):
                 msg = _('%s - use "hg update" instead') % msg
             raise util.Abort(msg)
 
@@ -1973,9 +1956,9 @@
     """
     rev = opts.get('rev')
     if rev:
-        ctx = repo.changectx(rev)
+        ctx = repo[rev]
     else:
-        ctx = repo.workingctx()
+        ctx = repo[None]
 
     if file_:
         m = cmdutil.match(repo, (file_,), opts)
@@ -2204,46 +2187,27 @@
         raise util.Abort(_('no files specified'))
 
     m = cmdutil.match(repo, pats, opts)
-    mardu = map(dict.fromkeys, repo.status(match=m))[:5]
-    modified, added, removed, deleted, unknown = mardu
-
-    remove, forget = [], []
-    for abs in repo.walk(m):
-
-        reason = None
-        if abs in removed or abs in unknown:
-            continue
-
-        # last column
-        elif abs in deleted:
-            remove.append(abs)
-
-        # rest of the third row
-        elif after and not force:
-            reason = _('still exists (use -f to force removal)')
-
-        # rest of the first column
-        elif abs in added:
-            if not force:
-                reason = _('has been marked for add (use -f to force removal)')
-            else:
-                forget.append(abs)
-
-        # rest of the third column
-        elif abs in modified:
-            if not force:
-                reason = _('is modified (use -f to force removal)')
-            else:
-                remove.append(abs)
-
-        # rest of the second column
-        elif not reason:
-            remove.append(abs)
-
-        if reason:
-            ui.warn(_('not removing %s: file %s\n') % (m.rel(abs), reason))
-        elif ui.verbose or not m.exact(abs):
-            ui.status(_('removing %s\n') % m.rel(abs))
+    s = repo.status(match=m, clean=True)
+    modified, added, deleted, clean = s[0], s[1], s[3], s[6]
+
+    def warn(files, reason):
+        for f in files:
+            ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
+                    % (m.rel(f), reason))
+
+    if force:
+        remove, forget = modified + deleted + clean, added
+    elif after:
+        remove, forget = deleted, []
+        warn(modified + added + clean, _('still exists'))
+    else:
+        remove, forget = deleted + clean, []
+        warn(modified, _('is modified'))
+        warn(added, _('has been marked for add'))
+
+    for f in util.sort(remove + forget):
+        if ui.verbose or not m.exact(f):
+            ui.status(_('removing %s\n') % m.rel(f))
 
     repo.forget(forget)
     repo.remove(remove, unlink=not after)
@@ -2297,7 +2261,7 @@
             elif opts.get("unmark"):
                 ms.mark(f, "u")
             else:
-                wctx = repo.workingctx()
+                wctx = repo[None]
                 mctx = wctx.parents()[-1]
                 ms.resolve(f, wctx, mctx)
 
@@ -2348,7 +2312,7 @@
     if not opts['rev'] and p2 != nullid:
         raise util.Abort(_('uncommitted merge - please provide a '
                            'specific revision'))
-    ctx = repo.changectx(opts['rev'])
+    ctx = repo[opts['rev']]
     node = ctx.node()
     mf = ctx.manifest()
     if node == parent:
@@ -2425,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):
@@ -2466,7 +2427,7 @@
                 if pmf is None:
                     # only need parent manifest in this unlikely case,
                     # so do not read by default
-                    pmf = repo.changectx(parent).manifest()
+                    pmf = repo[parent].manifest()
                 if abs in pmf:
                     if mfentry:
                         # if version of file is same in parent and target
@@ -2480,7 +2441,7 @@
         if not opts.get('dry_run'):
             def checkout(f):
                 fc = ctx[f]
-                repo.wwrite(f, fc.data(), fc.fileflags())
+                repo.wwrite(f, fc.data(), fc.flags())
 
             audit_path = util.path_auditor(repo.root)
             for f in remove[0]:
@@ -2668,13 +2629,13 @@
     changestates = zip(states, 'MAR!?IC', stat)
 
     if (opts['all'] or opts['copies']) and not opts['no_status']:
-        ctxn = repo.changectx(nullid)
-        ctx1 = repo.changectx(node1)
-        ctx2 = repo.changectx(node2)
+        ctxn = repo[nullid]
+        ctx1 = repo[node1]
+        ctx2 = repo[node2]
         added = stat[1]
         if node2 is None:
             added = stat[0] + stat[1] # merged?
-            ctx2 = repo.workingctx()
+
         for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].items():
             if k in added:
                 copy[k] = v
@@ -2713,7 +2674,7 @@
     See 'hg help dates' for a list of formats valid for -d/--date.
     """
 
-    rev_ = None
+    rev_ = "."
     names = (name1,) + names
     if len(names) != len(dict.fromkeys(names)):
         raise util.Abort(_('tag names must be unique'))
@@ -2744,7 +2705,7 @@
     if not rev_ and repo.dirstate.parents()[1] != nullid:
         raise util.Abort(_('uncommitted merge - please provide a '
                            'specific revision'))
-    r = repo.changectx(rev_).node()
+    r = repo[rev_].node()
 
     if not message:
         message = (_('Added tag %s for changeset %s') %
@@ -2801,7 +2762,7 @@
     that repository becomes the current tip. The "tip" tag is special
     and cannot be renamed or assigned to a different changeset.
     """
-    cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
+    cmdutil.show_changeset(ui, repo, opts).show(len(repo) - 1)
 
 def unbundle(ui, repo, fname1, *fnames, **opts):
     """apply one or more changegroup files
--- a/mercurial/context.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/context.py	Sat Jun 28 09:28:01 2008 +0200
@@ -12,19 +12,11 @@
 class changectx(object):
     """A changecontext object makes access to data related to a particular
     changeset convenient."""
-    def __init__(self, repo, changeid=None):
+    def __init__(self, repo, changeid=''):
         """changeid is a revision number, node, or tag"""
+        if changeid == '':
+            changeid = '.'
         self._repo = repo
-
-        if not changeid and changeid != 0:
-            p1, p2 = self._repo.dirstate.parents()
-            self._rev = self._repo.changelog.rev(p1)
-            if self._rev == -1:
-                changeid = 'tip'
-            else:
-                self._node = p1
-                return
-
         self._node = self._repo.lookup(changeid)
         self._rev = self._repo.changelog.rev(self._node)
 
@@ -63,6 +55,12 @@
             md = self._repo.manifest.readdelta(self._changeset[0])
             self._manifestdelta = md
             return self._manifestdelta
+        elif name == '_parents':
+            p = self._repo.changelog.parents(self._node)
+            if p[1] == nullid:
+                p = p[:-1]
+            self._parents = [changectx(self._repo, x) for x in p]
+            return self._parents
         else:
             raise AttributeError, name
 
@@ -73,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
@@ -93,8 +89,7 @@
 
     def parents(self):
         """return contexts for each parent changeset"""
-        p = self._repo.changelog.parents(self._node)
-        return [changectx(self._repo, x) for x in p]
+        return self._parents
 
     def children(self):
         """return contexts for each child changeset"""
@@ -121,7 +116,7 @@
     def filenode(self, path):
         return self._fileinfo(path)[0]
 
-    def fileflags(self, path):
+    def flags(self, path):
         try:
             return self._fileinfo(path)[1]
         except revlog.LookupError:
@@ -137,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):
@@ -240,9 +232,7 @@
 
     def filerev(self): return self._filerev
     def filenode(self): return self._filenode
-    def fileflags(self): return self._changectx.fileflags(self._path)
-    def isexec(self): return 'x' in self.fileflags()
-    def islink(self): return 'l' in self.fileflags()
+    def flags(self): return self._changectx.flags(self._path)
     def filelog(self): return self._filelog
 
     def rev(self):
@@ -388,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:
@@ -468,7 +457,7 @@
             self._user = self._repo.ui.username()
         if parents:
             p1, p2 = parents
-            self._parents = [self._repo.changectx(p) for p in (p1, p2)]
+            self._parents = [changectx(self._repo, p) for p in (p1, p2)]
         if changes:
             self._status = list(changes)
 
@@ -492,15 +481,18 @@
         return True
 
     def __getattr__(self, name):
-        if name == '_parents':
-            self._parents = self._repo.parents()
-            return self._parents
         if name == '_status':
-            self._status = self._repo.status()
+            self._status = self._repo.status(unknown=True)
             return self._status
         if name == '_manifest':
             self._buildmanifest()
             return self._manifest
+        elif name == '_parents':
+            p = self._repo.dirstate.parents()
+            if p[1] == nullid:
+                p = p[:-1]
+            self._parents = [changectx(self._repo, x) for x in p]
+            return self._parents
         else:
             raise AttributeError, name
 
@@ -509,16 +501,14 @@
 
         man = self._parents[0].manifest().copy()
         copied = self._repo.dirstate.copies()
-        is_exec = util.execfunc(self._repo.root,
-                                lambda p: man.execf(copied.get(p,p)))
-        is_link = util.linkfunc(self._repo.root,
-                                lambda p: man.linkf(copied.get(p,p)))
+        cf = lambda x: man.flags(copied.get(x, x))
+        ff = self._repo.dirstate.flagfunc(cf)
         modified, added, removed, deleted, unknown = self._status[:5]
         for i, l in (("a", added), ("m", modified), ("u", unknown)):
             for f in l:
                 man[f] = man.get(copied.get(f, f), nullid) + i
                 try:
-                    man.set(f, is_exec(f), is_link(f))
+                    man.set(f, ff(f))
                 except OSError:
                     pass
 
@@ -534,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]
@@ -552,14 +540,10 @@
         [t.extend(p.tags()) for p in self.parents()]
         return t
 
-    def parents(self):
-        """return contexts for each parent changeset"""
-        return self._parents
-
     def children(self):
         return []
 
-    def fileflags(self, path):
+    def flags(self, path):
         if '_manifest' in self.__dict__:
             try:
                 return self._manifest.flags(path)
@@ -569,12 +553,9 @@
         pnode = self._parents[0].changeset()[0]
         orig = self._repo.dirstate.copies().get(path, path)
         node, flag = self._repo.manifest.find(pnode, orig)
-        is_link = util.linkfunc(self._repo.root,
-                                lambda p: flag and 'l' in flag)
-        is_exec = util.execfunc(self._repo.root,
-                                lambda p: flag and 'x' in flag)
         try:
-            return (is_link(path) and 'l' or '') + (is_exec(path) and 'x' or '')
+            ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
+            return ff(path)
         except OSError:
             pass
 
@@ -698,9 +679,8 @@
         self._user = user or self._repo.ui.username()
         parents = [(p or nullid) for p in parents]
         p1, p2 = parents
-        self._parents = [self._repo.changectx(p) for p in (p1, p2)]
-        files = list(files)
-        files.sort()
+        self._parents = [changectx(self._repo, p) for p in (p1, p2)]
+        files = util.sort(list(files))
         self._status = [files, [], [], [], []]
         self._filectxfn = filectxfn
 
@@ -728,6 +708,7 @@
     def clean(self): return self._status[5]
     def branch(self): return self._extra['branch']
     def extra(self): return self._extra
+    def flags(self, f): return self[f].flags()
 
     def parents(self):
         """return contexts for each parent changeset"""
@@ -754,7 +735,7 @@
     def __str__(self): return "%s@%s" % (self.path(), self._changectx)
     def path(self): return self._path
     def data(self): return self._data
-    def fileflags(self): return self._flags
+    def flags(self): return self._flags
     def isexec(self): return 'x' in self._flags
     def islink(self): return 'l' in self._flags
     def renamed(self): return self._copied
--- a/mercurial/copies.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/copies.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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"
@@ -67,7 +63,7 @@
     #   - quit when interesting revs is zero
 
     cl = repo.changelog
-    working = cl.count() # pseudo rev for the working directory
+    working = len(cl) # pseudo rev for the working directory
     if a is None:
         a = working
     if b is None:
--- a/mercurial/dirstate.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/dirstate.py	Sat Jun 28 09:28:01 2008 +0200
@@ -70,14 +70,17 @@
         elif name == '_slash':
             self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
             return self._slash
+        elif name == '_checklink':
+            self._checklink = util.checklink(self._root)
+            return self._checklink
         elif name == '_checkexec':
             self._checkexec = util.checkexec(self._root)
             return self._checkexec
-        elif name == '_folding':
-            self._folding = not util.checkfolding(self._join('.hg'))
-            return self._folding
+        elif name == '_checkcase':
+            self._checkcase = not util.checkcase(self._join('.hg'))
+            return self._checkcase
         elif name == 'normalize':
-            if self._folding:
+            if self._checkcase:
                 self.normalize = self._normalize
             else:
                 self.normalize = lambda x: x
@@ -88,8 +91,33 @@
     def _join(self, f):
         return os.path.join(self._root, f)
 
-    def folding(self):
-        return self._folding
+    def flagfunc(self, fallback):
+        if self._checklink:
+            if self._checkexec:
+                def f(x):
+                    p = os.path.join(self._root, x)
+                    if os.path.islink(p):
+                        return 'l'
+                    if util.is_exec(p):
+                        return 'x'
+                    return ''
+                return f
+            def f(x):
+                if os.path.islink(os.path.join(self._root, x)):
+                    return 'l'
+                if 'x' in fallback(x):
+                    return 'x'
+                return ''
+            return f
+        if self._checkexec:
+            def f(x):
+                if 'l' in fallback(x):
+                    return 'l'
+                if util.is_exec(os.path.join(self._root, x)):
+                    return 'x'
+                return ''
+            return f
+        return fallback
 
     def getcwd(self):
         cwd = os.getcwd()
@@ -125,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):
@@ -356,7 +382,7 @@
     def rebuild(self, parent, files):
         self.clear()
         for f in files:
-            if files.execf(f):
+            if 'x' in files.flags(f):
                 self._map[f] = ('n', 0777, -1, 0, 0)
             else:
                 self._map[f] = ('n', 0666, -1, 0, 0)
@@ -408,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:
@@ -447,12 +472,7 @@
                 return True
         return False
 
-    def walk(self, match):
-        # filter out the src and stat
-        for src, f, st in self.statwalk(match):
-            yield f
-
-    def statwalk(self, match, unknown=True, ignored=False):
+    def walk(self, match, unknown, ignored):
         '''
         walk recursively through the directory tree, finding all files
         matched by the match function
@@ -537,9 +557,10 @@
                             continue
                 for f, kind, st in entries:
                     np = pconvert(join(nd, f))
+                    nn = self.normalize(np)
                     if np in known:
                         continue
-                    known[np] = 1
+                    known[nn] = 1
                     p = join(top, f)
                     # don't trip over symlinks
                     if kind == stat.S_IFDIR:
@@ -548,19 +569,18 @@
                             if hasattr(match, 'dir'):
                                 match.dir(np)
                         if np in dc and match(np):
-                            add((np, 'm', st))
+                            add((nn, 'm', st))
                     elif imatch(np):
                         if supported(np, st.st_mode):
-                            add((np, 'f', st))
+                            add((nn, 'f', st))
                         elif np in dc:
-                            add((np, 'm', st))
-            found.sort()
-            return found
+                            add((nn, 'm', st))
+            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)
             try:
                 st = lstat(f)
@@ -581,9 +601,9 @@
                     for f, src, st in findfiles(f):
                         yield src, f, st
             else:
-                if nf in known:
+                if nn in known:
                     continue
-                known[nf] = 1
+                known[nn] = 1
                 if match(nf):
                     if supported(ff, st.st_mode, verbose=True):
                         yield 'f', self.normalize(nf), st
@@ -592,16 +612,15 @@
 
         # 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
             if imatch(k):
                 yield 'm', k, None
 
-    def status(self, match, list_ignored, list_clean, list_unknown):
+    def status(self, match, ignored, clean, unknown):
+        listignored, listclean, listunknown = ignored, clean, unknown
         lookup, modified, added, unknown, ignored = [], [], [], [], []
         removed, deleted, clean = [], [], []
 
@@ -618,13 +637,12 @@
         dadd = deleted.append
         cadd = clean.append
 
-        for src, fn, st in self.statwalk(match, unknown=list_unknown,
-                                         ignored=list_ignored):
+        for src, fn, st in self.walk(match, listunknown, listignored):
             if fn not in dmap:
-                if (list_ignored or match.exact(fn)) and self._dirignore(fn):
-                    if list_ignored:
+                if (listignored or match.exact(fn)) and self._dirignore(fn):
+                    if listignored:
                         iadd(fn)
-                elif list_unknown:
+                elif listunknown:
                     uadd(fn)
                 continue
 
@@ -657,7 +675,7 @@
                     madd(fn)
                 elif time != int(st.st_mtime):
                     ladd(fn)
-                elif list_clean:
+                elif listclean:
                     cadd(fn)
             elif state == 'm':
                 madd(fn)
--- a/mercurial/filemerge.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/filemerge.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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:
@@ -132,7 +131,7 @@
     ui = repo.ui
     fd = fcd.path()
     binary = isbin(fcd) or isbin(fco) or isbin(fca)
-    symlink = fcd.islink() or fco.islink()
+    symlink = 'l' in fcd.flags() + fco.flags()
     tool, toolpath = _picktool(repo, ui, fd, binary, symlink)
     ui.debug(_("picked tool '%s' for %s (binary %s symlink %s)\n") %
                (tool, fd, binary, symlink))
@@ -146,7 +145,7 @@
     if tool == "internal:local":
         return 0
     if tool == "internal:other":
-        repo.wwrite(fd, fco.data(), fco.fileflags())
+        repo.wwrite(fd, fco.data(), fco.flags())
         return 0
     if tool == "internal:fail":
         return 1
@@ -180,9 +179,9 @@
     env = dict(HG_FILE=fd,
                HG_MY_NODE=short(mynode),
                HG_OTHER_NODE=str(fco.changectx()),
-               HG_MY_ISLINK=fcd.islink(),
-               HG_OTHER_ISLINK=fco.islink(),
-               HG_BASE_ISLINK=fca.islink())
+               HG_MY_ISLINK='l' in fcd.flags(),
+               HG_OTHER_ISLINK='l' in fco.flags(),
+               HG_BASE_ISLINK='l' in fca.flags())
 
     if tool == "internal:merge":
         r = simplemerge.simplemerge(a, b, c, label=['local', 'other'])
--- a/mercurial/graphmod.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/graphmod.py	Sat Jun 28 09:28:01 2008 +0200
@@ -69,6 +69,6 @@
                     edges.append((col, next.index(p), colors[p]))
 
         # Yield and move on
-        yield (repo.changectx(curr_rev), (idx, color), edges)
+        yield (repo[curr_rev], (idx, color), edges)
         revs = next
         curr_rev -= 1
--- a/mercurial/hbisect.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/hbisect.py	Sat Jun 28 09:28:01 2008 +0200
@@ -20,12 +20,12 @@
         badrev = min([changelog.rev(n) for n in bad])
         goodrevs = [changelog.rev(n) for n in good]
         # build ancestors array
-        ancestors = [[]] * (changelog.count() + 1) # an extra for [-1]
+        ancestors = [[]] * (len(changelog) + 1) # an extra for [-1]
 
         # clear good revs from array
         for node in goodrevs:
             ancestors[node] = None
-        for rev in xrange(changelog.count(), -1, -1):
+        for rev in xrange(len(changelog), -1, -1):
             if ancestors[rev] is None:
                 for prev in clparents(rev):
                     ancestors[prev] = None
@@ -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/hgweb_mod.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/hgweb/hgweb_mod.py	Sat Jun 28 09:28:01 2008 +0200
@@ -306,8 +306,8 @@
                            linenumber="% 8s" % lineno)
 
         r = self.repo
-        c1 = r.changectx(node1)
-        c2 = r.changectx(node2)
+        c1 = r[node1]
+        c2 = r[node2]
         date1 = util.datestr(c1.date())
         date2 = util.datestr(c2.date())
 
--- a/mercurial/hgweb/hgwebdir_mod.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/hgweb/hgwebdir_mod.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/hgweb/webcommands.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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.
@@ -110,10 +110,10 @@
         qw = query.lower().split()
 
         def revgen():
-            for i in xrange(cl.count() - 1, 0, -100):
+            for i in xrange(len(cl) - 1, 0, -100):
                 l = []
                 for j in xrange(max(0, i - 100), i + 1):
-                    ctx = web.repo.changectx(j)
+                    ctx = web.repo[j]
                     l.append(ctx)
                 l.reverse()
                 for e in l:
@@ -168,9 +168,9 @@
         if 'rev' in req.form:
             hi = req.form['rev'][0]
         else:
-            hi = web.repo.changelog.count() - 1
+            hi = len(web.repo) - 1
         try:
-            ctx = web.repo.changectx(hi)
+            ctx = web.repo[hi]
         except RepoError:
             return _search(web, tmpl, hi) # XXX redirect to 404 page?
 
@@ -178,7 +178,7 @@
         cl = web.repo.changelog
         l = [] # build a list in forward order for efficiency
         for i in xrange(start, end):
-            ctx = web.repo.changectx(i)
+            ctx = web.repo[i]
             n = ctx.node()
             showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
 
@@ -205,7 +205,7 @@
 
     maxchanges = shortlog and web.maxshortchanges or web.maxchanges
     cl = web.repo.changelog
-    count = cl.count()
+    count = len(cl)
     pos = ctx.rev()
     start = max(0, pos - maxchanges + 1)
     end = min(count, start + maxchanges)
@@ -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
@@ -299,14 +297,12 @@
             yield {"file": full,
                    "parity": parity.next(),
                    "basename": f,
-                   "date": fctx.changectx().date(),
+                   "date": fctx.date(),
                    "size": fctx.size(),
                    "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
@@ -343,7 +339,7 @@
             count = count + 1
             yield {"parity": parity.next(),
                    "tag": k,
-                   "date": web.repo.changectx(n).date(),
+                   "date": web.repo[n].date(),
                    "node": hex(n)}
 
     return tmpl("tags",
@@ -371,27 +367,24 @@
                        parity=parity.next(),
                        tag=k,
                        node=hex(n),
-                       date=web.repo.changectx(n).date())
+                       date=web.repo[n].date())
 
     def branches(**map):
         parity = paritygen(web.stripecount)
 
         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:
-            ctx = web.repo.changectx(n)
+        for r,n,t in util.sort(l):
             yield {'parity': parity.next(),
                    'branch': t,
                    'node': hex(n),
-                   'date': ctx.date()}
+                   'date': web.repo[n].date()}
 
     def changelist(**map):
         parity = paritygen(web.stripecount, offset=start-end)
         l = [] # build a list in forward order for efficiency
         for i in xrange(start, end):
-            ctx = web.repo.changectx(i)
+            ctx = web.repo[i]
             n = ctx.node()
             hn = hex(n)
 
@@ -410,7 +403,7 @@
         yield l
 
     cl = web.repo.changelog
-    count = cl.count()
+    count = len(cl)
     start = max(0, count - web.maxchanges)
     end = min(count, start + web.maxchanges)
 
@@ -499,7 +492,7 @@
     fctx = webutil.filectx(web.repo, req)
     f = fctx.path()
     fl = fctx.filelog()
-    count = fl.count()
+    count = len(fl)
     pagelen = web.maxshortchanges
     pos = fctx.filerev()
     start = max(0, pos - pagelen + 1)
@@ -580,7 +573,7 @@
     rev = webutil.changectx(web.repo, req).rev()
     bg_height = 39
 
-    max_rev = web.repo.changelog.count() - 1
+    max_rev = len(web.repo) - 1
     revcount = min(max_rev, int(req.form.get('revcount', [25])[0]))
     revnode = web.repo.changelog.node(rev)
     revnode_hex = hex(revnode)
@@ -589,7 +582,7 @@
     lessrev = max(0, rev - revcount / 2)
 
     maxchanges = web.maxshortchanges or web.maxchanges
-    count = web.repo.changelog.count()
+    count = len(web.repo)
     changenav = webutil.revnavgen(rev, maxchanges, count, web.repo.changectx)
 
     tree = list(graphmod.graph(web.repo, rev, rev - revcount))
--- a/mercurial/hgweb/webutil.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/hgweb/webutil.py	Sat Jun 28 09:28:01 2008 +0200
@@ -112,19 +112,17 @@
     return util.canonpath(repo.root, '', path)
 
 def changectx(repo, req):
+    changeid = "tip"
     if 'node' in req.form:
         changeid = req.form['node'][0]
     elif 'manifest' in req.form:
         changeid = req.form['manifest'][0]
-    else:
-        changeid = repo.changelog.count() - 1
 
     try:
-        ctx = repo.changectx(changeid)
+        ctx = repo[changeid]
     except RepoError:
         man = repo.manifest
-        mn = man.lookup(changeid)
-        ctx = repo.changectx(man.linkrev(mn))
+        ctx = repo[man.linkrev(man.lookup(changeid))]
 
     return ctx
 
@@ -135,8 +133,7 @@
     else:
         changeid = req.form['filenode'][0]
     try:
-        ctx = repo.changectx(changeid)
-        fctx = ctx.filectx(path)
+        fctx = repo[changeid][path]
     except RepoError:
         fctx = repo.filectx(path, fileid=changeid)
 
--- a/mercurial/hook.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/hook.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/localrepo.py	Sat Jun 28 09:28:01 2008 +0200
@@ -117,6 +117,21 @@
         else:
             raise AttributeError, name
 
+    def __getitem__(self, changeid):
+        if changeid == None:
+            return context.workingctx(self)
+        return context.changectx(self, changeid)
+
+    def __nonzero__(self):
+        return True
+
+    def __len__(self):
+        return len(self.changelog)
+
+    def __iter__(self):
+        for i in xrange(len(self)):
+            yield i
+
     def url(self):
         return 'file:' + self.root
 
@@ -330,7 +345,7 @@
         last = {}
         ret = []
         for node in heads:
-            c = self.changectx(node)
+            c = self[node]
             rev = c.rev()
             try:
                 fnode = c.filenode('.hgtags')
@@ -351,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'''
@@ -363,7 +377,7 @@
         return self.nodetagscache.get(node, [])
 
     def _branchtags(self, partial, lrev):
-        tiprev = self.changelog.count() - 1
+        tiprev = len(self) - 1
         if lrev != tiprev:
             self._updatebranchcache(partial, lrev+1, tiprev+1)
             self._writebranchcache(partial, self.changelog.tip(), tiprev)
@@ -408,8 +422,7 @@
         try:
             last, lrev = lines.pop(0).split(" ", 1)
             last, lrev = bin(last), int(lrev)
-            if not (lrev < self.changelog.count() and
-                    self.changelog.node(lrev) == last): # sanity check
+            if lrev >= len(self) or self[lrev].node() != last:
                 # invalidate the cache
                 raise ValueError('invalidating branch cache (tip differs)')
             for l in lines:
@@ -436,18 +449,13 @@
 
     def _updatebranchcache(self, partial, start, end):
         for r in xrange(start, end):
-            c = self.changectx(r)
+            c = self[r]
             b = c.branch()
             partial[b] = c.node()
 
     def lookup(self, key):
         if key == '.':
-            key, second = self.dirstate.parents()
-            if key == nullid:
-                raise repo.RepoError(_("no revision checked out"))
-            if second != nullid:
-                self.ui.warn(_("warning: working directory has two parents, "
-                               "tag '.' uses the first\n"))
+            return self.dirstate.parents()[0]
         elif key == 'null':
             return nullid
         n = self.changelog._match(key)
@@ -488,24 +496,12 @@
             f = f[1:]
         return filelog.filelog(self.sopener, f)
 
-    def changectx(self, changeid=None):
-        return context.changectx(self, changeid)
-
-    def workingctx(self):
-        return context.workingctx(self)
+    def changectx(self, changeid):
+        return self[changeid]
 
     def parents(self, changeid=None):
-        '''
-        get list of changectxs for parents of changeid or working directory
-        '''
-        if changeid is None:
-            pl = self.dirstate.parents()
-        else:
-            n = self.changelog.lookup(changeid)
-            pl = self.changelog.parents(n)
-        if pl[1] == nullid:
-            return [self.changectx(pl[0])]
-        return [self.changectx(pl[0]), self.changectx(pl[1])]
+        '''get list of changectxs for parents of changeid'''
+        return self[changeid].parents()
 
     def filectx(self, path, changeid=None, fileid=None):
         """changeid can be a changeset revision, node, or tag.
@@ -814,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']
@@ -846,25 +842,21 @@
             # check in files
             new = {}
             changed = []
-            linkrev = self.changelog.count()
-            commit.sort()
+            linkrev = len(self)
             for f in commit:
                 self.ui.note(f + "\n")
                 try:
                     fctx = wctx.filectx(f)
+                    newflags = fctx.flags()
                     new[f] = self.filecommit(fctx, m1, m2, linkrev, trp, changed)
-                    new_exec = fctx.isexec()
-                    new_link = fctx.islink()
                     if ((not changed or changed[-1] != f) and
                         m2.get(f) != new[f]):
                         # mention the file in the changelog if some
                         # flag changed, even if there was no content
                         # change.
-                        old_exec = m1.execf(f)
-                        old_link = m1.linkf(f)
-                        if old_exec != new_exec or old_link != new_link:
+                        if m1.flags(f) != newflags:
                             changed.append(f)
-                    m1.set(f, new_exec, new_link)
+                    m1.set(f, newflags)
                     if use_dirstate:
                         self.dirstate.normal(f)
 
@@ -877,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)
@@ -956,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):
@@ -967,18 +955,16 @@
                         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
         else:
-            for fn in self.dirstate.walk(match):
+            for src, fn, st in self.dirstate.walk(match, True, False):
                 yield fn
 
     def status(self, node1=None, node2=None, match=None,
-               list_ignored=False, list_clean=False, list_unknown=True):
+               ignored=False, clean=False, unknown=False):
         """return status of files between two nodes or node and working directory
 
         If node1 is None, use the first dirstate parent instead.
@@ -1000,6 +986,7 @@
         if not match:
             match = match_.always(self.root, self.getcwd())
 
+        listignored, listclean, listunknown = ignored, clean, unknown
         modified, added, removed, deleted, unknown = [], [], [], [], []
         ignored, clean = [], []
 
@@ -1016,27 +1003,22 @@
         # are we comparing the working directory?
         if not node2:
             (lookup, modified, added, removed, deleted, unknown,
-             ignored, clean) = self.dirstate.status(match, list_ignored,
-                                                    list_clean, list_unknown)
+             ignored, clean) = self.dirstate.status(match, listignored,
+                                                    listclean, listunknown)
             # are we comparing working dir against its parent?
             if compareworking:
                 if lookup:
                     fixup = []
                     # do a full compare of any files that might have changed
-                    ctx = self.changectx()
-                    mexec = lambda f: 'x' in ctx.fileflags(f)
-                    mlink = lambda f: 'l' in ctx.fileflags(f)
-                    is_exec = util.execfunc(self.root, mexec)
-                    is_link = util.linkfunc(self.root, mlink)
-                    def flags(f):
-                        return is_link(f) and 'l' or is_exec(f) and 'x' or ''
+                    ctx = self['.']
+                    ff = self.dirstate.flagfunc(ctx.flags)
                     for f in lookup:
-                        if (f not in ctx or flags(f) != ctx.fileflags(f)
+                        if (f not in ctx or ff(f) != ctx.flags(f)
                             or ctx[f].cmp(self.wread(f))):
                             modified.append(f)
                         else:
                             fixup.append(f)
-                            if list_clean:
+                            if listclean:
                                 clean.append(f)
 
                     # update dirstate for files that are actually clean
@@ -1057,11 +1039,10 @@
                 # generate a pseudo-manifest for the working dir
                 # XXX: create it in dirstate.py ?
                 mf2 = mfmatches(self.dirstate.parents()[0])
-                is_exec = util.execfunc(self.root, mf2.execf)
-                is_link = util.linkfunc(self.root, mf2.linkf)
+                ff = self.dirstate.flagfunc(mf2.flags)
                 for f in lookup + modified + added:
                     mf2[f] = ""
-                    mf2.set(f, is_exec(f), is_link(f))
+                    mf2.set(f, ff(f))
                 for f in removed:
                     if f in mf2:
                         del mf2[f]
@@ -1076,16 +1057,14 @@
 
             # 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
                          (mf2[fn] != "" or fcmp(fn, getnode)))):
                         modified.append(fn)
-                    elif list_clean:
+                    elif listclean:
                         clean.append(fn)
                     del mf1[fn]
                 else:
@@ -1201,11 +1180,11 @@
         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):
-        branch = branch is None and self.workingctx().branch() or branch
+        if branch is None:
+            branch = self[None].branch()
         branches = self.branchtags()
         if branch not in branches:
             return []
@@ -1243,7 +1222,7 @@
             if rev in ancestors:
                 ancestors.update(self.changelog.parentrevs(rev))
                 ancestors.remove(rev)
-            elif self.changectx(rev).branch() == branch:
+            elif self[rev].branch() == branch:
                 heads.append(rev)
                 ancestors.update(self.changelog.parentrevs(rev))
         heads = [self.changelog.node(rev) for rev in heads]
@@ -1658,7 +1637,7 @@
         # Nor do we know which filenodes are missing.
         msng_filenode_set = {}
 
-        junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
+        junk = mnfst.index[len(mnfst) - 1] # Get around a bug in lazyindex
         junk = None
 
         # A changeset always belongs to itself, so the changenode lookup
@@ -1853,12 +1832,10 @@
                     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 filerevlog.count() == 0:
+                if not len(filerevlog):
                     raise util.Abort(_("empty or missing revlog for %s") % fname)
                 # Toss out the filenodes that the recipient isn't really
                 # missing.
@@ -1909,10 +1886,10 @@
         def identity(x):
             return x
 
-        def gennodelst(revlog):
-            for r in xrange(0, revlog.count()):
-                n = revlog.node(r)
-                if revlog.linkrev(n) in revset:
+        def gennodelst(log):
+            for r in log:
+                n = log.node(r)
+                if log.linkrev(n) in revset:
                     yield n
 
         def changed_file_collector(changedfileset):
@@ -1934,17 +1911,15 @@
             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 filerevlog.count() == 0:
+                if not len(filerevlog):
                     raise util.Abort(_("empty or missing revlog for %s") % fname)
                 nodeiter = gennodelst(filerevlog)
                 nodeiter = list(nodeiter)
@@ -1973,7 +1948,7 @@
         """
         def csmap(x):
             self.ui.debug(_("add changeset %s\n") % short(x))
-            return cl.count()
+            return len(cl)
 
         def revmap(x):
             return cl.rev(x)
@@ -1996,11 +1971,11 @@
             trp = weakref.proxy(tr)
             # pull off the changeset group
             self.ui.status(_("adding changesets\n"))
-            cor = cl.count() - 1
+            cor = len(cl) - 1
             chunkiter = changegroup.chunkiter(source)
             if cl.addgroup(chunkiter, csmap, trp) is None and not emptyok:
                 raise util.Abort(_("received changelog group is empty"))
-            cnr = cl.count() - 1
+            cnr = len(cl) - 1
             changesets = cnr - cor
 
             # pull off the manifest group
@@ -2020,11 +1995,11 @@
                     break
                 self.ui.debug(_("adding %s revisions\n") % f)
                 fl = self.file(f)
-                o = fl.count()
+                o = len(fl)
                 chunkiter = changegroup.chunkiter(source)
                 if fl.addgroup(chunkiter, revmap, trp) is None:
                     raise util.Abort(_("received file revlog group is empty"))
-                revisions += fl.count() - o
+                revisions += len(fl) - o
                 files += 1
 
             # make changelog see real files again
--- a/mercurial/manifest.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/manifest.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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):
@@ -18,16 +18,8 @@
         self._flags = flags
     def flags(self, f):
         return self._flags.get(f, "")
-    def execf(self, f):
-        "test for executable in manifest flags"
-        return "x" in self.flags(f)
-    def linkf(self, f):
-        "test for symlink in manifest flags"
-        return "l" in self.flags(f)
-    def set(self, f, execf=False, linkf=False):
-        if linkf: self._flags[f] = "l"
-        elif execf: self._flags[f] = "x"
-        else: self._flags[f] = ""
+    def set(self, f, flags):
+        self._flags[f] = flags
     def copy(self):
         return manifestdict(dict.copy(self), dict.copy(self._flags))
 
@@ -134,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	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/merge.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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:
@@ -409,7 +408,7 @@
 
     wlock = repo.wlock()
     try:
-        wc = repo.workingctx()
+        wc = repo[None]
         if node is None:
             # tip of current branch
             try:
@@ -421,7 +420,7 @@
                     raise util.Abort(_("branch %s not found") % wc.branch())
         overwrite = force and not branchmerge
         pl = wc.parents()
-        p1, p2 = pl[0], repo.changectx(node)
+        p1, p2 = pl[0], repo[node]
         pa = p1.ancestor(p2)
         fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
         fastforward = False
@@ -460,7 +459,7 @@
         action = []
         if not force:
             _checkunknown(wc, p2)
-        if not util.checkfolding(repo.path):
+        if not util.checkcase(repo.path):
             _checkcollision(p2)
         action += _forgetremoved(wc, p2, branchmerge)
         action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
--- a/mercurial/patch.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/patch.py	Sat Jun 28 09:28:01 2008 +0200
@@ -8,7 +8,7 @@
 
 from i18n import _
 from node import hex, nullid, short
-import base85, cmdutil, mdiff, util, context, revlog, diffhelpers, copies
+import base85, cmdutil, mdiff, util, revlog, diffhelpers, copies
 import cStringIO, email.Parser, os, popen2, re, errno
 import sys, tempfile, zlib
 
@@ -1039,9 +1039,12 @@
                 continue
         elif state == 'git':
             gitpatches = values
+            cwd = os.getcwd()
             for gp in gitpatches:
                 if gp.op in ('COPY', 'RENAME'):
-                    copyfile(gp.oldpath, gp.path)
+                    src, dst = [util.canonpath(cwd, cwd, x)
+                                for x in [gp.oldpath, gp.path]]
+                    copyfile(src, dst)
                 changed[gp.path] = (gp.op, gp)
         else:
             raise util.Abort(_('unsupported parser state: %s') % state)
@@ -1091,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:
@@ -1110,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'''
@@ -1180,30 +1180,19 @@
 
     # reading the data for node1 early allows it to play nicely
     # with repo.status and the revlog cache.
-    ctx1 = context.changectx(repo, node1)
+    ctx1 = repo[node1]
     # force manifest reading
     man1 = ctx1.manifest()
     date1 = util.datestr(ctx1.date())
 
     if not changes:
-        changes = repo.status(node1, node2, match=match)[:5]
-    modified, added, removed, deleted, unknown = changes
+        changes = repo.status(node1, node2, match=match)
+    modified, added, removed = changes[:3]
 
     if not modified and not added and not removed:
         return
 
-    if node2:
-        ctx2 = context.changectx(repo, node2)
-        execf2 = ctx2.manifest().execf
-        linkf2 = ctx2.manifest().linkf
-    else:
-        ctx2 = context.workingctx(repo)
-        execf2 = util.execfunc(repo.root, None)
-        linkf2 = util.linkfunc(repo.root, None)
-        if execf2 is None:
-            mc = ctx2.parents()[0].manifest().copy()
-            execf2 = mc.execf
-            linkf2 = mc.linkf
+    ctx2 = repo[node2]
 
     if repo.ui.quiet:
         r = None
@@ -1212,15 +1201,14 @@
         r = [hexfunc(node) for node in [node1, node2] if node]
 
     if opts.git:
-        copy, diverge = copies.copies(repo, ctx1, ctx2, repo.changectx(nullid))
+        copy, diverge = copies.copies(repo, ctx1, ctx2, repo[nullid])
         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
@@ -1231,18 +1219,16 @@
             tn = getfilectx(f, ctx2).data()
         a, b = f, f
         if opts.git:
-            def gitmode(x, l):
-                return l and '120000' or (x and '100755' or '100644')
             def addmodehdr(header, omode, nmode):
                 if omode != nmode:
                     header.append('old mode %s\n' % omode)
                     header.append('new mode %s\n' % nmode)
 
             if f in added:
-                mode = gitmode(execf2(f), linkf2(f))
+                mode = gitmode[ctx2.flags(f)]
                 if f in copy:
                     a = copy[f]
-                    omode = gitmode(man1.execf(a), man1.linkf(a))
+                    omode = gitmode[man1.flags(a)]
                     addmodehdr(header, omode, mode)
                     if a in removed and a not in gone:
                         op = 'rename'
@@ -1261,11 +1247,11 @@
                 if f in copy and copy[f] in added and copy[copy[f]] == f:
                     dodiff = False
                 else:
-                    mode = gitmode(man1.execf(f), man1.linkf(f))
-                    header.append('deleted file mode %s\n' % mode)
+                    header.append('deleted file mode %s\n' %
+                                  gitmode[man1.flags(f)])
             else:
-                omode = gitmode(man1.execf(f), man1.linkf(f))
-                nmode = gitmode(execf2(f), linkf2(f))
+                omode = gitmode[man1.flags(f)]
+                nmode = gitmode[ctx2.flags(f)]
                 addmodehdr(header, omode, nmode)
                 if util.binary(to) or util.binary(tn):
                     dodiff = 'binary'
@@ -1291,7 +1277,7 @@
     revwidth = max([len(str(rev)) for rev in revs])
 
     def single(rev, seqno, fp):
-        ctx = repo.changectx(rev)
+        ctx = repo[rev]
         node = ctx.node()
         parents = [p.node() for p in ctx.parents() if p]
         branch = ctx.branch()
--- a/mercurial/repair.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/repair.py	Sat Jun 28 09:28:01 2008 +0200
@@ -23,8 +23,8 @@
     """find out the filelogs affected by the strip"""
     files = {}
 
-    for x in xrange(striprev, repo.changelog.count()):
-        for name in repo.changectx(x).files():
+    for x in xrange(striprev, len(repo)):
+        for name in repo[x].files():
             if name in files:
                 continue
             files[name] = 1
@@ -37,7 +37,7 @@
     """return the nodes that have to be saved before the strip"""
     def collectone(revlog):
         extra = []
-        startrev = count = revlog.count()
+        startrev = count = len(revlog)
         # find the truncation point of the revlog
         for i in xrange(0, count):
             node = revlog.node(i)
@@ -84,7 +84,7 @@
     tostrip = {striprev: 1}
     saveheads = {}
     savebases = []
-    for r in xrange(striprev + 1, cl.count()):
+    for r in xrange(striprev + 1, len(cl)):
         parents = cl.parentrevs(r)
         if parents[0] in tostrip or parents[1] in tostrip:
             # r is a descendant of striprev
--- a/mercurial/revlog.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/revlog.py	Sat Jun 28 09:28:01 2008 +0200
@@ -515,9 +515,11 @@
 
     def tip(self):
         return self.node(len(self.index) - 2)
-    def count(self):
+    def __len__(self):
         return len(self.index) - 1
-
+    def __iter__(self):
+        for i in xrange(len(self)):
+            yield i
     def rev(self, node):
         try:
             return self.nodemap[node]
@@ -620,12 +622,11 @@
             lowestrev = nullrev
         if (lowestrev == nullrev) and (heads is None):
             # We want _all_ the nodes!
-            return ([self.node(r) for r in xrange(0, self.count())],
-                    [nullid], list(self.heads()))
+            return ([self.node(r) for r in self], [nullid], list(self.heads()))
         if heads is None:
             # All nodes are ancestors, so the latest ancestor is the last
             # node.
-            highestrev = self.count() - 1
+            highestrev = len(self) - 1
             # Set ancestors to None to signal that every node is an ancestor.
             ancestors = None
             # Set heads to an empty dictionary for later discovery of heads
@@ -754,15 +755,15 @@
         as if they had no children
         """
         if start is None and stop is None:
-            count = self.count()
+            count = len(self)
             if not count:
                 return [nullid]
             ishead = [1] * (count + 1)
             index = self.index
-            for r in xrange(count):
+            for r in self:
                 e = index[r]
                 ishead[e[5]] = ishead[e[6]] = 0
-            return [self.node(r) for r in xrange(count) if ishead[r]]
+            return [self.node(r) for r in self if ishead[r]]
 
         if start is None:
             start = nullid
@@ -774,7 +775,7 @@
         heads = {startrev: 1}
 
         parentrevs = self.parentrevs
-        for r in xrange(startrev + 1, self.count()):
+        for r in xrange(startrev + 1, len(self)):
             for p in parentrevs(r):
                 if p in reachable:
                     if r not in stoprevs:
@@ -789,7 +790,7 @@
         """find the children of a given node"""
         c = []
         p = self.rev(node)
-        for r in range(p + 1, self.count()):
+        for r in range(p + 1, len(self)):
             prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
             if prevs:
                 for pr in prevs:
@@ -818,8 +819,8 @@
             if str(rev) != id:
                 raise ValueError
             if rev < 0:
-                rev = self.count() + rev
-            if rev < 0 or rev >= self.count():
+                rev = len(self) + rev
+            if rev < 0 or rev >= len(self):
                 raise ValueError
             return self.node(rev)
         except (ValueError, OverflowError):
@@ -982,7 +983,7 @@
         df = self.opener(self.datafile, 'w')
         try:
             calc = self._io.size
-            for r in xrange(self.count()):
+            for r in self:
                 start = self.start(r) + (r + 1) * calc
                 length = self.length(r)
                 fp.seek(start)
@@ -995,7 +996,7 @@
         fp = self.opener(self.indexfile, 'w', atomictemp=True)
         self.version &= ~(REVLOGNGINLINEDATA)
         self._inline = False
-        for i in xrange(self.count()):
+        for i in self:
             e = self._io.packentry(self.index[i], self.node, self.version, i)
             fp.write(e)
 
@@ -1031,7 +1032,7 @@
         if node in self.nodemap:
             return node
 
-        curr = self.count()
+        curr = len(self)
         prev = curr - 1
         base = self.base(prev)
         offset = self.end(prev)
@@ -1146,7 +1147,7 @@
         """
 
         #track the base of the current delta log
-        r = self.count()
+        r = len(self)
         t = r - 1
         node = None
 
@@ -1265,13 +1266,13 @@
         trust that the caller has saved the revisions that shouldn't be
         removed and that it'll readd them after this truncation.
         """
-        if self.count() == 0:
+        if len(self) == 0:
             return
 
         if isinstance(self.index, lazyindex):
             self._loadindexmap()
 
-        for rev in xrange(0, self.count()):
+        for rev in self:
             if self.index[rev][4] >= minlink:
                 break
         else:
@@ -1292,15 +1293,15 @@
         # then reset internal state in memory to forget those revisions
         self._cache = None
         self._chunkcache = None
-        for x in xrange(rev, self.count()):
+        for x in xrange(rev, len(self)):
             del self.nodemap[self.node(x)]
 
         del self.index[rev:-1]
 
     def checksize(self):
         expected = 0
-        if self.count():
-            expected = max(0, self.end(self.count() - 1))
+        if len(self):
+            expected = max(0, self.end(len(self) - 1))
 
         try:
             f = self.opener(self.datafile)
@@ -1321,10 +1322,10 @@
             di = actual - (i * s)
             if self._inline:
                 databytes = 0
-                for r in xrange(self.count()):
+                for r in self:
                     databytes += max(0, self.length(r))
                 dd = 0
-                di = actual - self.count() * s - databytes
+                di = actual - len(self) * s - databytes
         except IOError, inst:
             if inst.errno != errno.ENOENT:
                 raise
--- a/mercurial/streamclone.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/streamclone.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/ui.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/util.py	Sat Jun 28 09:28:01 2008 +0200
@@ -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."""
 
@@ -839,7 +845,7 @@
 
 # File system features
 
-def checkfolding(path):
+def checkcase(path):
     """
     Check whether the given path is on a case-sensitive filesystem
 
@@ -933,12 +939,6 @@
         return False
     return not (new_file_has_exec or exec_flags_cannot_flip)
 
-def execfunc(path, fallback):
-    '''return an is_exec() function with default to fallback'''
-    if checkexec(path):
-        return lambda x: is_exec(os.path.join(path, x))
-    return fallback
-
 def checklink(path):
     """check whether the given path is on a symlink-capable filesystem"""
     # mktemp is not racy because symlink creation will fail if the
@@ -951,12 +951,6 @@
     except (OSError, AttributeError):
         return False
 
-def linkfunc(path, fallback):
-    '''return an is_link() function with default to fallback'''
-    if checklink(path):
-        return lambda x: os.path.islink(os.path.join(path, x))
-    return fallback
-
 _umask = os.umask(0)
 os.umask(_umask)
 
--- a/mercurial/verify.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/mercurial/verify.py	Sat Jun 28 09:28:01 2008 +0200
@@ -7,7 +7,7 @@
 
 from node import nullid, short
 from i18n import _
-import revlog
+import revlog, util
 
 def verify(repo):
     lock = repo.lock()
@@ -17,265 +17,201 @@
         del lock
 
 def _verify(repo):
+    mflinkrevs = {}
     filelinkrevs = {}
     filenodes = {}
-    changesets = revisions = files = 0
-    firstbad = [None]
+    revisions = 0
+    badrevs = {}
     errors = [0]
     warnings = [0]
-    neededmanifests = {}
+    ui = repo.ui
+    cl = repo.changelog
+    mf = repo.manifest
 
     def err(linkrev, msg, filename=None):
         if linkrev != None:
-            if firstbad[0] != None:
-                firstbad[0] = min(firstbad[0], linkrev)
-            else:
-                firstbad[0] = linkrev
+            badrevs[linkrev] = True
         else:
-            linkrev = "?"
+            linkrev = '?'
         msg = "%s: %s" % (linkrev, msg)
         if filename:
             msg = "%s@%s" % (filename, msg)
-        repo.ui.warn(" " + msg + "\n")
+        ui.warn(" " + msg + "\n")
         errors[0] += 1
 
+    def exc(linkrev, msg, inst, filename=None):
+        if isinstance(inst, KeyboardInterrupt):
+            ui.warn(_("interrupted"))
+            raise
+        err(linkrev, "%s: %s" % (msg, inst), filename)
+
     def warn(msg):
-        repo.ui.warn(msg + "\n")
+        ui.warn(msg + "\n")
         warnings[0] += 1
 
-    def checksize(obj, name):
+    def checklog(obj, name):
+        if not len(obj) and (havecl or havemf):
+            err(0, _("empty or missing %s") % name)
+            return
+
         d = obj.checksize()
         if d[0]:
             err(None, _("data length off by %d bytes") % d[0], name)
         if d[1]:
             err(None, _("index contains %d extra bytes") % d[1], name)
 
-    def checkversion(obj, name):
         if obj.version != revlog.REVLOGV0:
             if not revlogv1:
                 warn(_("warning: `%s' uses revlog format 1") % name)
         elif revlogv1:
             warn(_("warning: `%s' uses revlog format 0") % name)
 
-    revlogv1 = repo.changelog.version != revlog.REVLOGV0
-    if repo.ui.verbose or not revlogv1:
-        repo.ui.status(_("repository uses revlog format %d\n") %
+    def checkentry(obj, i, node, seen, linkrevs, f):
+        lr = obj.linkrev(node)
+        if lr < 0 or (havecl and lr not in linkrevs):
+            t = "unexpected"
+            if lr < 0 or lr >= len(cl):
+                t = "nonexistent"
+            err(None, _("rev %d point to %s changeset %d") % (i, t, lr), f)
+            if linkrevs:
+                warn(_(" (expected %s)") % " ".join(map(str,linkrevs)))
+            lr = None # can't be trusted
+
+        try:
+            p1, p2 = obj.parents(node)
+            if p1 not in seen and p1 != nullid:
+                err(lr, _("unknown parent 1 %s of %s") %
+                    (short(p1), short(n)), f)
+            if p2 not in seen and p2 != nullid:
+                err(lr, _("unknown parent 2 %s of %s") %
+                    (short(p2), short(p1)), f)
+        except Exception, inst:
+            exc(lr, _("checking parents of %s") % short(node), inst, f)
+
+        if node in seen:
+            err(lr, _("duplicate revision %d (%d)") % (i, seen[n]), f)
+        seen[n] = i
+        return lr
+
+    revlogv1 = cl.version != revlog.REVLOGV0
+    if ui.verbose or not revlogv1:
+        ui.status(_("repository uses revlog format %d\n") %
                        (revlogv1 and 1 or 0))
 
-    havecl = havemf = 1
-    seen = {}
-    repo.ui.status(_("checking changesets\n"))
-    if repo.changelog.count() == 0 and repo.manifest.count() > 1:
-        havecl = 0
-        err(0, _("empty or missing 00changelog.i"))
-    else:
-        checksize(repo.changelog, "changelog")
-
-    for i in xrange(repo.changelog.count()):
-        changesets += 1
-        n = repo.changelog.node(i)
-        l = repo.changelog.linkrev(n)
-        if l != i:
-            err(i, _("incorrect link (%d) for changeset") %(l))
-        if n in seen:
-            err(i, _("duplicates changeset at revision %d") % seen[n])
-        seen[n] = i
-
-        for p in repo.changelog.parents(n):
-            if p not in repo.changelog.nodemap:
-                err(i, _("changeset has unknown parent %s") % short(p))
-        try:
-            changes = repo.changelog.read(n)
-        except KeyboardInterrupt:
-            repo.ui.warn(_("interrupted"))
-            raise
-        except Exception, inst:
-            err(i, _("unpacking changeset: %s") % inst)
-            continue
+    havecl = len(cl) > 0
+    havemf = len(mf) > 0
 
-        if changes[0] not in neededmanifests:
-            neededmanifests[changes[0]] = i
-
-        for f in changes[3]:
-            filelinkrevs.setdefault(f, []).append(i)
-
+    ui.status(_("checking changesets\n"))
     seen = {}
-    repo.ui.status(_("checking manifests\n"))
-    if repo.changelog.count() > 0 and repo.manifest.count() == 0:
-        havemf = 0
-        err(0, _("empty or missing 00manifest.i"))
-    else:
-        checkversion(repo.manifest, "manifest")
-        checksize(repo.manifest, "manifest")
-
-    for i in xrange(repo.manifest.count()):
-        n = repo.manifest.node(i)
-        l = repo.manifest.linkrev(n)
-
-        if l < 0 or (havecl and l >= repo.changelog.count()):
-            err(None, _("bad link (%d) at manifest revision %d") % (l, i))
-
-        if n in neededmanifests:
-            del neededmanifests[n]
-
-        if n in seen:
-            err(l, _("duplicates manifest from %d") % seen[n])
-
-        seen[n] = l
-
-        for p in repo.manifest.parents(n):
-            if p not in repo.manifest.nodemap:
-                err(l, _("manifest has unknown parent %s") % short(p))
+    checklog(cl, "changelog")
+    for i in repo:
+        n = cl.node(i)
+        checkentry(cl, i, n, seen, [i], "changelog")
 
         try:
-            for f, fn in repo.manifest.readdelta(n).iteritems():
-                fns = filenodes.setdefault(f, {})
-                if fn not in fns:
-                    fns[fn] = n
-        except KeyboardInterrupt:
-            repo.ui.warn(_("interrupted"))
-            raise
+            changes = cl.read(n)
+            mflinkrevs.setdefault(changes[0], []).append(i)
+            for f in changes[3]:
+                filelinkrevs.setdefault(f, []).append(i)
         except Exception, inst:
-            err(l, _("reading manifest delta: %s") % inst)
-            continue
-
-    repo.ui.status(_("crosschecking files in changesets and manifests\n"))
+            exc(i, _("unpacking changeset %s") % short(n), inst)
 
-    if havemf > 0:
-        nm = [(c, m) for m, c in neededmanifests.items()]
-        nm.sort()
-        for c, m in nm:
-            err(c, _("changeset refers to unknown manifest %s") % short(m))
-        del neededmanifests, nm
+    ui.status(_("checking manifests\n"))
+    seen = {}
+    checklog(mf, "manifest")
+    for i in mf:
+        n = mf.node(i)
+        lr = checkentry(mf, i, n, seen, mflinkrevs.get(n, []), "manifest")
+        if n in mflinkrevs:
+            del mflinkrevs[n]
 
-    if havecl:
-        fl = filenodes.keys()
-        fl.sort()
-        for f in fl:
-            if f not in filelinkrevs:
-                lrs = [repo.manifest.linkrev(n) for n in filenodes[f]]
-                lrs.sort()
-                err(lrs[0], _("in manifest but not in changeset"), f)
-        del fl
+        try:
+            for f, fn in mf.readdelta(n).iteritems():
+                if not f:
+                    err(lr, _("file without name in manifest"))
+                elif f != "/dev/null":
+                    fns = filenodes.setdefault(f, {})
+                    if fn not in fns:
+                        fns[fn] = n
+        except Exception, inst:
+            exc(lr, _("reading manifest delta %s") % short(n), inst)
+
+    ui.status(_("crosschecking files in changesets and manifests\n"))
 
     if havemf:
-        fl = filelinkrevs.keys()
-        fl.sort()
-        for f in fl:
+        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
+
+        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
 
-    repo.ui.status(_("checking files\n"))
-    ff = dict.fromkeys(filenodes.keys() + filelinkrevs.keys()).keys()
-    ff.sort()
-    for f in ff:
-        if f == "/dev/null":
-            continue
-        files += 1
-        if not f:
-            lr = filelinkrevs[f][0]
-            err(lr, _("file without name in manifest"))
-            continue
+    if havecl:
+        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)
+
+    ui.status(_("checking files\n"))
+    files = util.sort(util.unique(filenodes.keys() + filelinkrevs.keys()))
+    for f in files:
         fl = repo.file(f)
-        checkversion(fl, f)
-        checksize(fl, f)
-
-        if fl.count() == 0:
-            err(filelinkrevs[f][0], _("empty or missing revlog"), f)
-            continue
-
+        checklog(fl, f)
         seen = {}
-        nodes = {nullid: 1}
-        for i in xrange(fl.count()):
+        for i in fl:
             revisions += 1
             n = fl.node(i)
-            flr = fl.linkrev(n)
-
-            if flr < 0 or (havecl and flr not in filelinkrevs.get(f, [])):
-                if flr < 0 or flr >= repo.changelog.count():
-                    err(None, _("rev %d point to nonexistent changeset %d")
-                        % (i, flr), f)
-                else:
-                    err(None, _("rev %d points to unexpected changeset %d")
-                        % (i, flr), f)
-                if f in filelinkrevs:
-                    warn(_(" (expected %s)") % filelinkrevs[f][0])
-                flr = None # can't be trusted
-            else:
-                if havecl:
-                    filelinkrevs[f].remove(flr)
-
-            if n in seen:
-                err(flr, _("duplicate revision %d") % i, f)
+            lr = checkentry(fl, i, n, seen, filelinkrevs.get(f, []), f)
             if f in filenodes:
                 if havemf and n not in filenodes[f]:
-                    err(flr, _("%s not in manifests") % (short(n)), f)
+                    err(lr, _("%s not in manifests") % (short(n)), f)
                 else:
                     del filenodes[f][n]
 
             # verify contents
             try:
                 t = fl.read(n)
-            except KeyboardInterrupt:
-                repo.ui.warn(_("interrupted"))
-                raise
+                rp = fl.renamed(n)
+                if len(t) != fl.size(i):
+                    if not fl._readmeta(n): # ancient copy?
+                        err(lr, _("unpacked size is %s, %s expected") %
+                            (len(t), fl.size(i)), f)
             except Exception, inst:
-                err(flr, _("unpacking %s: %s") % (short(n), inst), f)
-
-            # verify parents
-            try:
-                (p1, p2) = fl.parents(n)
-                if p1 not in nodes:
-                    err(flr, _("unknown parent 1 %s of %s") %
-                        (short(p1), short(n)), f)
-                if p2 not in nodes:
-                    err(flr, _("unknown parent 2 %s of %s") %
-                            (short(p2), short(p1)), f)
-            except KeyboardInterrupt:
-                repo.ui.warn(_("interrupted"))
-                raise
-            except Exception, inst:
-                err(flr, _("checking parents of %s: %s") % (short(n), inst), f)
-            nodes[n] = 1
+                exc(lr, _("unpacking %s") % short(n), inst, f)
 
             # check renames
             try:
-                rp = fl.renamed(n)
                 if rp:
                     fl2 = repo.file(rp[0])
-                    if fl2.count() == 0:
-                        err(flr, _("empty or missing copy source revlog %s:%s")
+                    if not len(fl2):
+                        err(lr, _("empty or missing copy source revlog %s:%s")
                             % (rp[0], short(rp[1])), f)
                     elif rp[1] == nullid:
-                        err(flr, _("copy source revision is nullid %s:%s")
+                        err(lr, _("copy source revision is nullid %s:%s")
                             % (rp[0], short(rp[1])), f)
                     else:
                         rev = fl2.rev(rp[1])
-            except KeyboardInterrupt:
-                repo.ui.warn(_("interrupted"))
-                raise
             except Exception, inst:
-                err(flr, _("checking rename of %s: %s") %
-                    (short(n), inst), f)
+                exc(lr, _("checking rename of %s") % short(n), inst, f)
 
         # cross-check
         if f in filenodes:
-            fns = [(repo.manifest.linkrev(filenodes[f][n]), n)
-                   for n in filenodes[f]]
-            fns.sort()
-            for lr, node in fns:
+            fns = [(mf.linkrev(l), n) for n,l in filenodes[f].items()]
+            for lr, node in util.sort(fns):
                 err(lr, _("%s in manifests not found") % short(node), f)
 
-    repo.ui.status(_("%d files, %d changesets, %d total revisions\n") %
-                   (files, changesets, revisions))
-
+    ui.status(_("%d files, %d changesets, %d total revisions\n") %
+                   (len(files), len(cl), revisions))
     if warnings[0]:
-        repo.ui.warn(_("%d warnings encountered!\n") % warnings[0])
+        ui.warn(_("%d warnings encountered!\n") % warnings[0])
     if errors[0]:
-        repo.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
-        if firstbad[0]:
-            repo.ui.warn(_("(first damaged changeset appears to be %d)\n")
-                         % firstbad[0])
+        ui.warn(_("%d integrity errors encountered!\n") % errors[0])
+        if badrevs:
+            ui.warn(_("(first damaged changeset appears to be %d)\n")
+                    % min(badrevs))
         return 1
--- a/tests/test-children.out	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-children.out	Sat Jun 28 09:28:01 2008 +0200
@@ -5,6 +5,18 @@
 % hg children at revision 3 (tip)
 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
 % hg children at nullrev (should be 0 and 3)
+changeset:   0:4df8521a7374
+user:        test
+date:        Thu Jan 01 00:00:00 1970 +0000
+summary:     0
+
+changeset:   3:e2962852269d
+tag:         tip
+parent:      -1:000000000000
+user:        test
+date:        Thu Jan 01 00:00:03 1970 +0000
+summary:     3
+
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % hg children at revision 1 (should be 2)
 changeset:   2:8f5eea5023c2
--- a/tests/test-context.py	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-context.py	Sat Jun 28 09:28:01 2008 +0200
@@ -16,4 +16,4 @@
 repo.add(['foo'])
 repo.commit(text='commit1', date="0 0")
 
-print "workingfilectx.date =", repo.workingctx().filectx('foo').date()
+print "workingfilectx.date =", repo[None]['foo'].date()
--- a/tests/test-convert-filemap	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-convert-filemap	Sat Jun 28 09:28:01 2008 +0200
@@ -80,6 +80,7 @@
 	echo "include $i" >> "$fmap"
     done
     hg -q convert $opts --filemap "$fmap" --datesort source "$repo"
+    hg up -q -R "$repo"
     glog -R "$repo"
     hg -R "$repo" manifest --debug
 }
@@ -115,6 +116,7 @@
 rename copied copied2
 EOF
 hg -q convert --filemap renames.fmap --datesort source renames.repo
+hg up -q -R renames.repo
 glog -R renames.repo
 hg -R renames.repo manifest --debug
 hg --cwd renames.repo debugrename copied2
--- a/tests/test-convert-filemap.out	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-convert-filemap.out	Sat Jun 28 09:28:01 2008 +0200
@@ -29,7 +29,7 @@
 copied renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
 
 % foo: skip unwanted merges; use 1st parent in 1st merge, 2nd in 2nd
-o  3 "8: change foo" files: foo
+@  3 "8: change foo" files: foo
 |
 o  2 "6: change foo baz" files: foo
 |
@@ -39,7 +39,7 @@
 
 9a7b52012991e4873687192c3e17e61ba3e837a3 644   foo
 % bar: merges are not merges anymore
-o  4 "7: second merge; change bar" files: bar
+@  4 "7: second merge; change bar" files: bar
 |
 o  3 "5: change bar baz quux" files: bar
 |
@@ -51,7 +51,7 @@
 
 9463f52fe115e377cf2878d4fc548117211063f2 644   bar
 % baz: 1st merge is not a merge anymore; 2nd still is
-o    4 "7: second merge; change bar" files: baz
+@    4 "7: second merge; change bar" files: baz
 |\
 | o  3 "6: change foo baz" files: baz
 | |
@@ -63,7 +63,7 @@
 
 94c1be4dfde2ee8d78db8bbfcf81210813307c3d 644   baz
 % foo quux: we add additional merges when they are interesting
-o  8 "8: change foo" files: foo
+@  8 "8: change foo" files: foo
 |
 o    7 "7: second merge; change bar" files:
 |\
@@ -84,14 +84,14 @@
 9a7b52012991e4873687192c3e17e61ba3e837a3 644   foo
 bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644   quux
 % bar quux: partial conversion
-o  1 "3: change bar quux" files: bar quux
+@  1 "3: change bar quux" files: bar quux
 |
 o  0 "1: add bar quux; copy foo to copied" files: bar quux
 
 b79105bedc55102f394e90a789c9c380117c1b4a 644   bar
 db0421cc6b685a458c8d86c7d5c004f94429ea23 644   quux
 % bar quux: complete the partial conversion
-o  4 "7: second merge; change bar" files: bar
+@  4 "7: second merge; change bar" files: bar
 |
 o  3 "5: change bar baz quux" files: bar quux
 |
@@ -104,11 +104,11 @@
 9463f52fe115e377cf2878d4fc548117211063f2 644   bar
 bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644   quux
 % foo: partial conversion
-o  0 "0: add foo baz dir/" files: foo
+@  0 "0: add foo baz dir/" files: foo
 
 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644   foo
 % foo: complete the partial conversion
-o  3 "8: change foo" files: foo
+@  3 "8: change foo" files: foo
 |
 o  2 "6: change foo baz" files: foo
 |
@@ -118,12 +118,12 @@
 
 9a7b52012991e4873687192c3e17e61ba3e837a3 644   foo
 % copied: copied file; source not included in new repo
-o  0 "1: add bar quux; copy foo to copied" files: copied
+@  0 "1: add bar quux; copy foo to copied" files: copied
 
 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644   copied
 copied not renamed
 % foo copied: copied file; source included in new repo
-o  4 "8: change foo" files: foo
+@  4 "8: change foo" files: foo
 |
 o  3 "6: change foo baz" files: foo
 |
@@ -136,7 +136,7 @@
 6ca237634e1f6bee1b6db94292fb44f092a25842 644   copied
 9a7b52012991e4873687192c3e17e61ba3e837a3 644   foo
 copied renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
-o  4 "8: change foo" files: foo2
+@  4 "8: change foo" files: foo2
 |
 o  3 "6: change foo baz" files: foo2
 |
--- a/tests/test-convert-git	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-convert-git	Sat Jun 28 09:28:01 2008 +0200
@@ -53,7 +53,7 @@
 cd ..
 
 hg convert --datesort git-repo
-
+hg up -q -R git-repo-hg
 hg -R git-repo-hg tip -v
 
 count=10
@@ -117,12 +117,14 @@
 	echo "include $i" >> "$fmap"
     done
     hg -q convert $opts --filemap "$fmap" --datesort git-repo2 "$repo"
+    hg up -q -R "$repo"
     glog -R "$repo"
     hg -R "$repo" manifest --debug
 }
 
 echo '% full conversion'
 hg -q convert --datesort git-repo2 fullrepo
+hg up -q -R fullrepo
 glog -R fullrepo
 hg -R fullrepo manifest --debug
 
--- a/tests/test-convert-git.out	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-convert-git.out	Sat Jun 28 09:28:01 2008 +0200
@@ -24,7 +24,7 @@
 
 
 % full conversion
-o    9 "Discard change to foo" files: foo
+@    9 "Discard change to foo" files: foo
 |\
 | o  8 "change foo" files: foo
 | |
@@ -49,7 +49,7 @@
 9277c9cc8dd4576fc01a17939b4351e5ada93466 644   foo
 88dfeab657e8cf2cef3dec67b914f49791ae76b1 644   quux
 % foo bar baz: octopus merge
-o    8 "Discard change to foo" files: foo
+@    8 "Discard change to foo" files: foo
 |\
 | o  7 "change foo" files: foo
 | |
@@ -71,7 +71,7 @@
 354ae8da6e890359ef49ade27b68bbc361f3ca88 644   baz
 9277c9cc8dd4576fc01a17939b4351e5ada93466 644   foo
 % foo baz quux: only some parents of an octopus merge; "discard" a head
-o  6 "Discard change to foo" files: foo
+@  6 "Discard change to foo" files: foo
 |
 o  5 "change foo" files: foo
 |
--- a/tests/test-import	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-import	Sat Jun 28 09:28:01 2008 +0200
@@ -261,3 +261,15 @@
 hg status
 cat a
 cd ..
+
+echo % 'test paths outside repo root'
+mkdir outside
+touch outside/foo
+hg init inside
+cd inside
+hg import - <<EOF
+diff --git a/a b/b
+rename from ../outside/foo
+rename to bar
+EOF
+cd ..
--- a/tests/test-import.out	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-import.out	Sat Jun 28 09:28:01 2008 +0200
@@ -260,3 +260,6 @@
 adding a
 applying patch from stdin
 bb
+% test paths outside repo root
+applying patch from stdin
+abort: ../outside/foo not under root
--- a/tests/test-log.out	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-log.out	Sat Jun 28 09:28:01 2008 +0200
@@ -150,7 +150,6 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 (branch merge, don't forget to commit)
 % log -r . with two parents
-warning: working directory has two parents, tag '.' uses the first
 changeset:   3:e62f78d544b4
 parent:      1:3d5bf5654eda
 user:        test
--- a/tests/test-manifest	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-manifest	Sat Jun 28 09:28:01 2008 +0200
@@ -14,6 +14,10 @@
 hg init
 hg -q pull "$TESTDIR/test-manifest.hg"
 
+echo % should be empty
+hg manifest
+
+hg co
 hg manifest
 hg manifest -v
 hg manifest --debug
--- a/tests/test-manifest.out	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-manifest.out	Sat Jun 28 09:28:01 2008 +0200
@@ -1,3 +1,5 @@
+% should be empty
+3 files updated, 0 files merged, 0 files removed, 0 files unresolved
 a
 b/a
 l
--- a/tests/test-merge-remove.out	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-merge-remove.out	Sat Jun 28 09:28:01 2008 +0200
@@ -23,7 +23,6 @@
 M foo1
   foo
 % reverting foo1 and bar
-warning: working directory has two parents, tag '.' uses the first
 saving current version of bar as bar.orig
 reverting bar
 saving current version of foo1 as foo1.orig
--- a/tests/test-parseindex	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-parseindex	Sat Jun 28 09:28:01 2008 +0200
@@ -44,8 +44,8 @@
     return wrapper
 
 cl = changelog.changelog(opener('.hg/store'))
-print cl.count(), 'revisions:'
-for r in xrange(cl.count()):
+print len(cl), 'revisions:'
+for r in cl:
     print short(cl.node(r))
 EOF
 
--- a/tests/test-purge	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-purge	Sat Jun 28 09:28:01 2008 +0200
@@ -83,23 +83,15 @@
 rm r1
 # hide error messages to avoid changing the output when the text changes
 hg purge -p 2> /dev/null
-if [ $? -ne 0 ]; then
-    echo "refused to run"
-fi
-if [ -f untracked_file ]; then
-    echo "untracked_file still around"
-fi
-hg purge -p --force
+hg st
+
+hg purge -p
 hg purge -v 2> /dev/null
-if [ $? -ne 0 ]; then
-    echo "refused to run"
-fi
-if [ -f untracked_file ]; then
-    echo "untracked_file still around"
-fi
-hg purge -v --force
+hg st
+
+hg purge -v
 hg revert --all --quiet
-ls
+hg st -a
 
 echo '% tracked file in ignored directory (issue621)'
 echo directory >> .hgignore
--- a/tests/test-purge.out	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-purge.out	Sat Jun 28 09:28:01 2008 +0200
@@ -51,14 +51,12 @@
 directory
 r1
 % abort with missing files until we support name mangling filesystems
-refused to run
-untracked_file still around
 untracked_file
-refused to run
-untracked_file still around
+! r1
+? untracked_file
+untracked_file
 Removing file untracked_file
-directory
-r1
+! r1
 % tracked file in ignored directory (issue621)
 untracked_file
 Removing file untracked_file
--- a/tests/test-remove.out	Thu Jun 26 13:37:47 2008 -0700
+++ b/tests/test-remove.out	Sat Jun 28 09:28:01 2008 +0200
@@ -70,15 +70,15 @@
 adding test/bar
 adding test/foo
 % dir, options none
+removing test/bar
 removing test/foo
-removing test/bar
 R test/bar
 R test/foo
 ./foo
 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % dir, options -f
+removing test/bar
 removing test/foo
-removing test/bar
 R test/bar
 R test/foo
 ./foo
@@ -91,8 +91,8 @@
 ./test/foo
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 % dir, options -Af
+removing test/bar
 removing test/foo
-removing test/bar
 R test/bar
 R test/foo
 ./foo