Merge with crew-stable
authorPatrick Mezard <pmezard@gmail.com>
Sat, 13 Mar 2010 20:02:46 +0100
changeset 10676 13341047d517
parent 10671 001ffc2b3d22 (diff)
parent 10675 3c05ecffe20d (current diff)
child 10677 f2558a8228be
Merge with crew-stable
hgext/rebase.py
mercurial/hgweb/hgwebdir_mod.py
--- a/contrib/hgdiff	Thu Mar 11 00:28:31 2010 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-#!/usr/bin/env python
-
-import os, sys, struct, stat
-import difflib
-import re
-from optparse import OptionParser
-from mercurial.bdiff import bdiff, blocks
-from mercurial.mdiff import bunidiff, diffopts
-
-VERSION="0.3"
-usage = "usage: %prog [options] file1 file2"
-parser = OptionParser(usage=usage)
-
-parser.add_option("-d", "--difflib", action="store_true", default=False)
-parser.add_option('-x', '--count', default=1)
-parser.add_option('-c', '--context', type="int", default=3)
-parser.add_option('-p', '--show-c-function', action="store_true", default=False)
-parser.add_option('-w', '--ignore-all-space', action="store_true",
-                  default=False)
-
-(options, args) = parser.parse_args()
-
-if not args:
-    parser.print_help()
-    sys.exit(1)
-
-# simple utility function to put all the
-# files from a directory tree into a dict
-def buildlist(names, top):
-    tlen = len(top)
-    for root, dirs, files in os.walk(top):
-        l = root[tlen + 1:]
-        for x in files:
-            p = os.path.join(root, x)
-            st = os.lstat(p)
-            if stat.S_ISREG(st.st_mode):
-                names[os.path.join(l, x)] = (st.st_dev, st.st_ino)
-
-def diff_files(file1, file2):
-    if file1 is None:
-        b = file(file2).read().splitlines(True)
-        l1 = "--- %s\n" % (file2)
-        l2 = "+++ %s\n" % (file2)
-        l3 = "@@ -0,0 +1,%d @@\n" % len(b)
-        l = [l1, l2, l3] + ["+" + e for e in b]
-    elif file2 is None:
-        a = file(file1).read().splitlines(True)
-        l1 = "--- %s\n" % (file1)
-        l2 = "+++ %s\n" % (file1)
-        l3 = "@@ -1,%d +0,0 @@\n" % len(a)
-        l = [l1, l2, l3] + ["-" + e for e in a]
-    else:
-        t1 = file(file1).read()
-        t2 = file(file2).read()
-        l1 = t1.splitlines(True)
-        l2 = t2.splitlines(True)
-        if options.difflib:
-            l = difflib.unified_diff(l1, l2, file1, file2)
-        else:
-            l = bunidiff(t1, t2, l1, l2, file1, file2,
-                         diffopts(context=options.context,
-                                  showfunc=options.show_c_function,
-                                  ignorews=options.ignore_all_space))
-    for x in l:
-        if x[-1] != '\n':
-            x += "\n\ No newline at end of file\n"
-        print x,
-
-file1 = args[0]
-file2 = args[1]
-
-if os.path.isfile(file1) and os.path.isfile(file2):
-    diff_files(file1, file2)
-elif os.path.isdir(file1):
-    if not os.path.isdir(file2):
-        sys.stderr.write("file types don't match\n")
-        sys.exit(1)
-
-    d1 = {}
-    d2 = {}
-
-    buildlist(d1, file1)
-    buildlist(d2, file2)
-    keys = d1.keys()
-    keys.sort()
-    for x in keys:
-        if x not in d2:
-            f2 = None
-        else:
-            f2 = os.path.join(file2, x)
-            st1 = d1[x]
-            st2 = d2[x]
-            del d2[x]
-            if st1[0] == st2[0] and st1[1] == st2[1]:
-                sys.stderr.write("%s is a hard link\n" % x)
-                continue
-        x = os.path.join(file1, x)
-        diff_files(x, f2)
-    keys = d2.keys()
-    keys.sort()
-    for x in keys:
-        f1 = None
-        x = os.path.join(file2, x)
-        diff_files(f1, x)
-
--- a/contrib/shrink-revlog.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/contrib/shrink-revlog.py	Sat Mar 13 20:02:46 2010 +0100
@@ -24,50 +24,81 @@
 from mercurial import changegroup
 from mercurial.i18n import _
 
-def toposort(ui, rl):
+
+def postorder(start, edges):
+    result = []
+    visit = list(start)
+    finished = set()
 
-    children = {}
-    root = []
-    # build children and roots
+    while visit:
+        cur = visit[-1]
+        for p in edges[cur]:
+            if p not in finished:
+                visit.append(p)
+                break
+        else:
+            result.append(cur)
+            finished.add(cur)
+            visit.pop()
+
+    return result
+
+def toposort_reversepostorder(ui, rl):
+    # postorder of the reverse directed graph
+
+    # map rev to list of parent revs (p2 first)
+    parents = {}
+    heads = set()
     ui.status(_('reading revs\n'))
     try:
-        for i in rl:
-            ui.progress(_('reading'), i, total=len(rl))
-            children[i] = []
-            parents = [p for p in rl.parentrevs(i) if p != node.nullrev]
-            # in case of duplicate parents
-            if len(parents) == 2 and parents[0] == parents[1]:
-                del parents[1]
-            for p in parents:
-                assert p in children
-                children[p].append(i)
-
-            if len(parents) == 0:
-                root.append(i)
+        for rev in rl:
+            ui.progress(_('reading'), rev, total=len(rl))
+            (p1, p2) = rl.parentrevs(rev)
+            if p1 == p2 == node.nullrev:
+                parents[rev] = ()       # root node
+            elif p1 == p2 or p2 == node.nullrev:
+                parents[rev] = (p1,)    # normal node
+            else:
+                parents[rev] = (p2, p1) # merge node
+            heads.add(rev)
+            for p in parents[rev]:
+                heads.discard(p)
     finally:
         ui.progress(_('reading'), None, total=len(rl))
 
-    # XXX this is a reimplementation of the 'branchsort' topo sort
-    # algorithm in hgext.convert.convcmd... would be nice not to duplicate
-    # the algorithm
+    heads = list(heads)
+    heads.sort(reverse=True)
+
     ui.status(_('sorting revs\n'))
-    visit = root
-    ret = []
-    while visit:
-        i = visit.pop(0)
-        ret.append(i)
-        if i not in children:
-            # This only happens if some node's p1 == p2, which can
-            # happen in the manifest in certain circumstances.
-            continue
-        next = []
-        for c in children.pop(i):
-            parents_unseen = [p for p in rl.parentrevs(c)
-                              if p != node.nullrev and p in children]
-            if len(parents_unseen) == 0:
-                next.append(c)
-        visit = next + visit
-    return ret
+    return postorder(heads, parents)
+
+def toposort_postorderreverse(ui, rl):
+    # reverse-postorder of the reverse directed graph
+
+    children = {}
+    roots = set()
+    ui.status(_('reading revs\n'))
+    try:
+        for rev in rl:
+            ui.progress(_('reading'), rev, total=len(rl))
+            (p1, p2) = rl.parentrevs(rev)
+            if p1 == p2 == node.nullrev:
+                roots.add(rev)
+            children[rev] = []
+            if p1 != node.nullrev:
+                children[p1].append(rev)
+            if p2 != node.nullrev:
+                children[p2].append(rev)
+    finally:
+        ui.progress(_('reading'), None, total=len(rl))
+
+    root = list(roots)
+    roots.sort()
+
+    ui.status(_('sorting revs\n'))
+    result = postorder(roots, children)
+    result.reverse()
+    return result
 
 def writerevs(ui, r1, r2, order, tr):
 
@@ -118,9 +149,15 @@
              % (shrink_percent, shrink_factor))
 
 def shrink(ui, repo, **opts):
+    """shrink a revlog by reordering revisions
+
+    Rewrites all the entries in some revlog of the current repository
+    (by default, the manifest log) to save space.
+
+    Different sort algorithms have different performance
+    characteristics.  Use ``--sort`` to select a sort algorithm so you
+    can determine which works best for your data.
     """
-    Shrink revlog by re-ordering revisions. Will operate on manifest for
-    the given repository if no other revlog is specified."""
 
     if not repo.local():
         raise util.Abort(_('not a local repository: %s') % repo.root)
@@ -139,6 +176,12 @@
             raise util.Abort(_('--revlog option must specify a revlog in %s, '
                                'not %s') % (store, indexfn))
 
+    sortname = opts['sort']
+    try:
+        toposort = globals()['toposort_' + sortname]
+    except KeyError:
+        raise util.Abort(_('no such toposort algorithm: %s') % sortname)
+
     if not os.path.exists(indexfn):
         raise util.Abort(_('no such file: %s') % indexfn)
     if '00changelog' in indexfn:
@@ -187,6 +230,15 @@
     try:
         try:
             order = toposort(ui, r1)
+
+            suboptimal = 0
+            for i in xrange(1, len(order)):
+                parents = [p for p in r1.parentrevs(order[i])
+                           if p != node.nullrev]
+                if parents and order[i - 1] not in parents:
+                    suboptimal += 1
+            ui.note(_('%d suboptimal nodes\n') % suboptimal)
+
             writerevs(ui, r1, r2, order, tr)
             report(ui, r1, r2)
             tr.close()
@@ -229,6 +281,7 @@
     'shrink': (shrink,
                [('', 'revlog', '', _('index (.i) file of the revlog to shrink')),
                 ('n', 'dry-run', None, _('do not shrink, simulate only')),
+                ('', 'sort', 'reversepostorder', 'name of sort algorithm to use'),
                 ],
                _('hg shrink [--revlog PATH]'))
 }
--- a/contrib/wix/mercurial.wxs	Thu Mar 11 00:28:31 2010 +0900
+++ b/contrib/wix/mercurial.wxs	Sat Mar 13 20:02:46 2010 +0100
@@ -37,6 +37,9 @@
     <Property Id='INSTALLEDMERCURIALPRODUCTS' Secure='yes'></Property>
     <Property Id='REINSTALLMODE'>amus</Property>
 
+    <!--Auto-accept the license page-->
+    <Property Id='LicenseAccepted'>1</Property>
+
     <Directory Id='TARGETDIR' Name='SourceDir'>
       <Directory Id='ProgramFilesFolder' Name='PFiles'>
         <Directory Id='INSTALLDIR' Name='Mercurial'>
--- a/hgext/churn.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/hgext/churn.py	Sat Mar 13 20:02:46 2010 +0100
@@ -48,7 +48,7 @@
             tmpl.show(ctx)
             return ui.popbuffer()
 
-    state = {'count': 0, 'pct': 0}
+    state = {'count': 0}
     rate = {}
     df = False
     if opts.get('date'):
@@ -74,20 +74,13 @@
             lines = changedlines(ui, repo, ctx1, ctx, fns)
             rate[key] = [r + l for r, l in zip(rate.get(key, (0, 0)), lines)]
 
-        if opts.get('progress'):
-            state['count'] += 1
-            newpct = int(100.0 * state['count'] / max(len(repo), 1))
-            if state['pct'] < newpct:
-                state['pct'] = newpct
-                ui.write("\r" + _("generating stats: %d%%") % state['pct'])
-                sys.stdout.flush()
+        state['count'] += 1
+        ui.progress(_('churning changes'), state['count'], total=len(repo))
 
     for ctx in cmdutil.walkchangerevs(repo, m, opts, prep):
         continue
 
-    if opts.get('progress'):
-        ui.write("\r")
-        sys.stdout.flush()
+    ui.progress(_('churning changes'), None)
 
     return rate
 
@@ -188,6 +181,6 @@
           ('s', 'sort', False, _('sort by key (default: sort by count)')),
           ('', 'diffstat', False, _('display added/removed lines separately')),
           ('', 'aliases', '', _('file with email aliases')),
-          ('', 'progress', None, _('show progress'))],
-         _("hg churn [-d DATE] [-r REV] [--aliases FILE] [--progress] [FILE]")),
+          ],
+         _("hg churn [-d DATE] [-r REV] [--aliases FILE] [FILE]")),
 }
--- a/hgext/hgcia.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/hgext/hgcia.py	Sat Mar 13 20:02:46 2010 +0100
@@ -113,7 +113,7 @@
 
         n = self.ctx.node()
         pbuf = patchbuf()
-        patch.export(self.cia.repo, [n], fp=pbuf)
+        cmdutil.export(self.cia.repo, [n], fp=pbuf)
         return patch.diffstat(pbuf.lines) or ''
 
     def logmsg(self):
--- a/hgext/inotify/__init__.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/hgext/inotify/__init__.py	Sat Mar 13 20:02:46 2010 +0100
@@ -41,7 +41,7 @@
         # to start an inotify server if it won't start.
         _inotifyon = True
 
-        def status(self, match, subrepos, ignored, clean, unknown=True):
+        def status(self, match, subrepos, ignored, clean, unknown):
             files = match.files()
             if '.' in files:
                 files = []
--- a/hgext/keyword.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/hgext/keyword.py	Sat Mar 13 20:02:46 2010 +0100
@@ -1,6 +1,6 @@
 # keyword.py - $Keyword$ expansion for Mercurial
 #
-# Copyright 2007-2009 Christian Ebert <blacktrash@gmx.net>
+# Copyright 2007-2010 Christian Ebert <blacktrash@gmx.net>
 #
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
@@ -79,7 +79,6 @@
 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
 from mercurial import patch, localrepo, templater, templatefilters, util, match
 from mercurial.hgweb import webcommands
-from mercurial.lock import release
 from mercurial.node import nullid
 from mercurial.i18n import _
 import re, shutil, tempfile
@@ -251,10 +250,8 @@
     '''Bails out if [keyword] configuration is not active.
     Returns status of working directory.'''
     if kwt:
-        unknown = (opts.get('unknown') or opts.get('all')
-                   or opts.get('untracked'))
         return repo.status(match=cmdutil.match(repo, pats, opts), clean=True,
-                           unknown=unknown)
+                           unknown=opts.get('unknown') or opts.get('all'))
     if ui.configitems('keyword'):
         raise util.Abort(_('[keyword] patterns cannot match'))
     raise util.Abort(_('no [keyword] patterns configured'))
@@ -264,17 +261,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 = status[:4]
-    if modified or added or removed or deleted:
-        raise util.Abort(_('outstanding uncommitted changes'))
-    wlock = lock = None
+    wlock = repo.wlock()
     try:
-        wlock = repo.wlock()
-        lock = repo.lock()
-        kwt.overwrite(None, expand, status[6])
+        status = _status(ui, repo, kwt, *pats, **opts)
+        modified, added, removed, deleted, unknown, ignored, clean = status
+        if modified or added or removed or deleted:
+            raise util.Abort(_('outstanding uncommitted changes'))
+        kwt.overwrite(None, expand, clean)
     finally:
-        release(lock, wlock)
+        wlock.release()
 
 def demo(ui, repo, *args, **opts):
     '''print [keywordmaps] configuration and an expansion example
@@ -398,7 +393,7 @@
     cwd = pats and repo.getcwd() or ''
     modified, added, removed, deleted, unknown, ignored, clean = status
     files = []
-    if not (opts.get('unknown') or opts.get('untracked')) or opts.get('all'):
+    if not opts.get('unknown') or opts.get('all'):
         files = sorted(modified + added + clean)
     wctx = repo[None]
     kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
@@ -485,15 +480,10 @@
                 del self.commitctx
 
         def kwcommitctx(self, ctx, error=False):
-            wlock = lock = None
-            try:
-                wlock = self.wlock()
-                lock = self.lock()
-                n = super(kwrepo, self).commitctx(ctx, error)
-                kwt.overwrite(n, True, None)
-                return n
-            finally:
-                release(lock, wlock)
+            n = super(kwrepo, self).commitctx(ctx, error)
+            # no lock needed, only called from repo.commit() which already locks
+            kwt.overwrite(n, True, None)
+            return n
 
     # monkeypatches
     def kwpatchfile_init(orig, self, ui, fname, opener,
@@ -540,9 +530,6 @@
          [('A', 'all', None, _('show keyword status flags of all files')),
           ('i', 'ignore', None, _('show files excluded from expansion')),
           ('u', 'unknown', None, _('only show unknown (not tracked) files')),
-          ('a', 'all', None,
-           _('show keyword status flags of all files (DEPRECATED)')),
-          ('u', 'untracked', None, _('only show untracked files (DEPRECATED)')),
          ] + commands.walkopts,
          _('hg kwfiles [OPTION]... [FILE]...')),
     'kwshrink': (shrink, commands.walkopts,
--- a/hgext/mq.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/hgext/mq.py	Sat Mar 13 20:02:46 2010 +0100
@@ -616,7 +616,7 @@
         return (True, files, fuzz)
 
     def apply(self, repo, series, list=False, update_status=True,
-              strict=False, patchdir=None, merge=None, all_files={}):
+              strict=False, patchdir=None, merge=None, all_files=None):
         wlock = lock = tr = None
         try:
             wlock = repo.wlock()
@@ -641,7 +641,7 @@
             self.removeundo(repo)
 
     def _apply(self, repo, series, list=False, update_status=True,
-               strict=False, patchdir=None, merge=None, all_files={}):
+               strict=False, patchdir=None, merge=None, all_files=None):
         '''returns (error, hash)
         error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
         # TODO unify with commands.py
@@ -674,7 +674,8 @@
 
             if ph.haspatch:
                 (patcherr, files, fuzz) = self.patch(repo, pf)
-                all_files.update(files)
+                if all_files is not None:
+                    all_files.update(files)
                 patcherr = not patcherr
             else:
                 self.ui.warn(_("patch %s is empty\n") % patchname)
@@ -1071,7 +1072,7 @@
                 end = self.series.index(patch, start) + 1
 
             s = self.series[start:end]
-            all_files = {}
+            all_files = set()
             try:
                 if mergeq:
                     ret = self.mergepatch(repo, mergeq, s, diffopts)
@@ -1081,11 +1082,10 @@
                 self.ui.warn(_('cleaning up working directory...'))
                 node = repo.dirstate.parents()[0]
                 hg.revert(repo, node, None)
-                unknown = repo.status(unknown=True)[4]
                 # only remove unknown files that we know we touched or
                 # created while patching
-                for f in unknown:
-                    if f in all_files:
+                for f in all_files:
+                    if f not in repo.dirstate:
                         util.unlink(repo.wjoin(f))
                 self.ui.warn(_('done\n'))
                 raise
@@ -1104,10 +1104,6 @@
             wlock.release()
 
     def pop(self, repo, patch=None, force=False, update=True, all=False):
-        def getfile(f, rev, flags):
-            t = repo.file(f).read(rev)
-            repo.wwrite(f, t, flags)
-
         wlock = repo.wlock()
         try:
             if patch:
@@ -1175,8 +1171,7 @@
             # form of hg.update.
             if update:
                 qp = self.qparents(repo, rev)
-                changes = repo.changelog.read(qp)
-                mmap = repo.manifest.read(changes[0])
+                ctx = repo[qp]
                 m, a, r, d = repo.status(qp, top)[:4]
                 if d:
                     raise util.Abort(_("deletions found between repo revs"))
@@ -1189,11 +1184,9 @@
                     try: os.removedirs(os.path.dirname(repo.wjoin(f)))
                     except: pass
                     repo.dirstate.forget(f)
-                for f in m:
-                    getfile(f, mmap[f], mmap.flags(f))
-                for f in r:
-                    getfile(f, mmap[f], mmap.flags(f))
                 for f in m + r:
+                    fctx = ctx[f]
+                    repo.wwrite(f, fctx.data(), fctx.flags())
                     repo.dirstate.normal(f)
                 repo.dirstate.setparents(qp, nullid)
             for patch in reversed(self.applied[start:end]):
@@ -1681,7 +1674,7 @@
                 self.full_series.insert(0, patchname)
 
                 patchf = self.opener(patchname, "w")
-                patch.export(repo, [n], fp=patchf, opts=diffopts)
+                cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
                 patchf.close()
 
                 se = statusentry(hex(n), patchname)
--- a/hgext/patchbomb.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/hgext/patchbomb.py	Sat Mar 13 20:02:46 2010 +0100
@@ -249,7 +249,7 @@
     def getpatches(revs):
         for r in cmdutil.revrange(repo, revs):
             output = cStringIO.StringIO()
-            patch.export(repo, [r], fp=output,
+            cmdutil.export(repo, [r], fp=output,
                          opts=patch.diffopts(ui, opts))
             yield output.getvalue().split('\n')
 
--- a/hgext/rebase.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/hgext/rebase.py	Sat Mar 13 20:02:46 2010 +0100
@@ -14,7 +14,7 @@
 http://mercurial.selenic.com/wiki/RebaseExtension
 '''
 
-from mercurial import util, repair, merge, cmdutil, commands, error
+from mercurial import hg, util, repair, merge, cmdutil, commands, error
 from mercurial import extensions, ancestor, copies, patch
 from mercurial.commands import templateopts
 from mercurial.node import nullrev
@@ -502,7 +502,14 @@
 
         cmdutil.bail_if_changed(repo)
         revsprepull = len(repo)
-        orig(ui, repo, *args, **opts)
+        origpostincoming = commands.postincoming
+        def _dummy(*args, **kwargs):
+            pass
+        commands.postincoming = _dummy
+        try:
+            orig(ui, repo, *args, **opts)
+        finally:
+            commands.postincoming = origpostincoming
         revspostpull = len(repo)
         if revspostpull > revsprepull:
             rebase(ui, repo, **opts)
@@ -510,7 +517,7 @@
             dest = repo[branch].rev()
             if dest != repo['.'].rev():
                 # there was nothing to rebase we force an update
-                merge.update(repo, dest, False, False, False)
+                hg.update(repo, dest)
     else:
         orig(ui, repo, *args, **opts)
 
--- a/mercurial/cmdutil.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/mercurial/cmdutil.py	Sat Mar 13 20:02:46 2010 +0100
@@ -289,11 +289,18 @@
     '''find renamed files -- yields (before, after, score) tuples'''
     copies = {}
     ctx = repo['.']
-    for r in removed:
+    for i, r in enumerate(removed):
+        repo.ui.progress(_('looking for similarities'), i, total=len(removed))
         if r not in ctx:
             continue
         fctx = ctx.filectx(r)
 
+        # lazily load text
+        @util.cachefunc
+        def data():
+            orig = fctx.data()
+            return orig, mdiff.splitnewlines(orig)
+
         def score(text):
             if not len(text):
                 return 0.0
@@ -301,14 +308,13 @@
                 return 1.0
             if threshold == 1.0:
                 return 0.0
-            orig = fctx.data()
+            orig, lines = data()
             # bdiff.blocks() returns blocks of matching lines
             # count the number of bytes in each
             equal = 0
-            alines = mdiff.splitnewlines(text)
             matches = bdiff.blocks(text, orig)
             for x1, x2, y1, y2 in matches:
-                for line in alines[x1:x2]:
+                for line in lines[y1:y2]:
                     equal += len(line)
 
             lengths = len(text) + len(orig)
@@ -319,6 +325,7 @@
             myscore = score(repo.wread(a))
             if myscore >= bestscore:
                 copies[a] = (r, myscore)
+    repo.ui.progress(_('looking for similarities'), None, total=len(removed))
 
     for dest, v in copies.iteritems():
         source, score = v
@@ -356,9 +363,7 @@
             removed.append(abs)
         elif repo.dirstate[abs] == 'a':
             added.append(abs)
-    if not dry_run:
-        repo.remove(deleted)
-        repo.add(unknown)
+    copies = {}
     if similarity > 0:
         for old, new, score in findrenames(repo, added + unknown,
                                            removed + deleted, similarity):
@@ -366,8 +371,17 @@
                 repo.ui.status(_('recording removal of %s as rename to %s '
                                  '(%d%% similar)\n') %
                                (m.rel(old), m.rel(new), score * 100))
-            if not dry_run:
+            copies[new] = old
+
+    if not dry_run:
+        wlock = repo.wlock()
+        try:
+            repo.remove(deleted)
+            repo.add(unknown)
+            for new, old in copies.iteritems():
                 repo.copy(old, new)
+        finally:
+            wlock.release()
 
 def copy(ui, repo, pats, opts, rename=False):
     # called with the repo lock held
@@ -646,6 +660,46 @@
     if runfn:
         return runfn()
 
+def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
+           opts=None):
+    '''export changesets as hg patches.'''
+
+    total = len(revs)
+    revwidth = max([len(str(rev)) for rev in revs])
+
+    def single(rev, seqno, fp):
+        ctx = repo[rev]
+        node = ctx.node()
+        parents = [p.node() for p in ctx.parents() if p]
+        branch = ctx.branch()
+        if switch_parent:
+            parents.reverse()
+        prev = (parents and parents[0]) or nullid
+
+        if not fp:
+            fp = make_file(repo, template, node, total=total, seqno=seqno,
+                           revwidth=revwidth, mode='ab')
+        if fp != sys.stdout and hasattr(fp, 'name'):
+            repo.ui.note("%s\n" % fp.name)
+
+        fp.write("# HG changeset patch\n")
+        fp.write("# User %s\n" % ctx.user())
+        fp.write("# Date %d %d\n" % ctx.date())
+        if branch and (branch != 'default'):
+            fp.write("# Branch %s\n" % branch)
+        fp.write("# Node ID %s\n" % hex(node))
+        fp.write("# Parent  %s\n" % hex(prev))
+        if len(parents) > 1:
+            fp.write("# Parent  %s\n" % hex(parents[1]))
+        fp.write(ctx.description().rstrip())
+        fp.write("\n\n")
+
+        for chunk in patch.diff(repo, prev, node, opts=opts):
+            fp.write(chunk)
+
+    for seqno, rev in enumerate(revs):
+        single(rev, seqno + 1, fp)
+
 class changeset_printer(object):
     '''show changeset information when templating not requested.'''
 
--- a/mercurial/commands.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/mercurial/commands.py	Sat Mar 13 20:02:46 2010 +0100
@@ -12,8 +12,8 @@
 import hg, util, revlog, bundlerepo, extensions, copies, error
 import patch, help, mdiff, url, encoding, templatekw
 import archival, changegroup, cmdutil, sshserver, hbisect
-from hgweb import server
-import merge as merge_
+from hgweb import server, hgweb_mod, hgwebdir_mod
+import merge as mergemod
 import minirst
 
 # Commands start here, listed alphabetically
@@ -158,8 +158,10 @@
     By default, the revision used is the parent of the working
     directory; use -r/--rev to specify a different revision.
 
-    To specify the type of archive to create, use -t/--type. Valid
-    types are:
+    The archive type is automatically detected based on file
+    extension (or override using -t/--type).
+
+    Valid types are:
 
     :``files``: a directory full of files (default)
     :``tar``:   tar archive, uncompressed
@@ -184,16 +186,32 @@
     dest = cmdutil.make_filename(repo, dest, node)
     if os.path.realpath(dest) == repo.root:
         raise util.Abort(_('repository root cannot be destination'))
-    matchfn = cmdutil.match(repo, [], opts)
-    kind = opts.get('type') or 'files'
+
+    def guess_type():
+        exttypes = {
+            'tar': ['.tar'],
+            'tbz2': ['.tbz2', '.tar.bz2'],
+            'tgz': ['.tgz', '.tar.gz'],
+            'zip': ['.zip'],
+        }
+
+        for type, extensions in exttypes.items():
+            if util.any(dest.endswith(ext) for ext in extensions):
+                return type
+        return None
+
+    kind = opts.get('type') or guess_type() or 'files'
     prefix = opts.get('prefix')
+
     if dest == '-':
         if kind == 'files':
             raise util.Abort(_('cannot archive plain files to stdout'))
         dest = sys.stdout
         if not prefix:
             prefix = os.path.basename(repo.root) + '-%h'
+
     prefix = cmdutil.make_filename(repo, prefix, node)
+    matchfn = cmdutil.match(repo, [], opts)
     archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
                      matchfn, prefix)
 
@@ -1208,7 +1226,7 @@
         ui.note(_('exporting patches:\n'))
     else:
         ui.note(_('exporting patch:\n'))
-    patch.export(repo, revs, template=opts.get('output'),
+    cmdutil.export(repo, revs, template=opts.get('output'),
                  switch_parent=opts.get('switch_parent'),
                  opts=patch.diffopts(ui, opts))
 
@@ -2578,7 +2596,7 @@
         raise util.Abort(_('no files or directories specified; '
                            'use --all to remerge all files'))
 
-    ms = merge_.mergestate(repo)
+    ms = mergemod.mergestate(repo)
     m = cmdutil.match(repo, pats, opts)
 
     for f in ms:
@@ -2868,6 +2886,10 @@
     By default, the server logs accesses to stdout and errors to
     stderr. Use the -A/--accesslog and -E/--errorlog options to log to
     files.
+
+    To have the server choose a free port number to listen on, specify
+    a port number of 0; in this case, the server will print the port
+    number it uses.
     """
 
     if opts["stdio"]:
@@ -2877,25 +2899,35 @@
         s = sshserver.sshserver(ui, repo)
         s.serve_forever()
 
+    # this way we can check if something was given in the command-line
+    if opts.get('port'):
+        opts['port'] = int(opts.get('port'))
+
     baseui = repo and repo.baseui or ui
     optlist = ("name templates style address port prefix ipv6"
-               " accesslog errorlog webdir_conf certificate encoding")
+               " accesslog errorlog certificate encoding")
     for o in optlist.split():
-        if opts.get(o, None):
-            baseui.setconfig("web", o, str(opts[o]))
-            if (repo is not None) and (repo.ui != baseui):
-                repo.ui.setconfig("web", o, str(opts[o]))
-
-    if repo is None and not ui.config("web", "webdir_conf"):
-        raise error.RepoError(_("There is no Mercurial repository here"
-                                " (.hg not found)"))
+        val = opts.get(o, '')
+        if val in (None, ''): # should check against default options instead
+            continue
+        baseui.setconfig("web", o, val)
+        if repo and repo.ui != baseui:
+            repo.ui.setconfig("web", o, val)
+
+    if opts.get('webdir_conf'):
+        app = hgwebdir_mod.hgwebdir(opts['webdir_conf'], ui)
+    elif repo is not None:
+        app = hgweb_mod.hgweb(hg.repository(repo.ui, repo.root))
+    else:
+        raise error.RepoError(_("There is no Mercurial repository"
+                                " here (.hg not found)"))
 
     class service(object):
         def init(self):
             util.set_signal_handler()
-            self.httpd = server.create_server(baseui, repo)
-
-            if not ui.verbose:
+            self.httpd = server.create_server(ui, app)
+
+            if opts['port'] and not ui.verbose:
                 return
 
             if self.httpd.prefix:
@@ -2916,8 +2948,12 @@
             fqaddr = self.httpd.fqaddr
             if ':' in fqaddr:
                 fqaddr = '[%s]' % fqaddr
-            ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
-                      (fqaddr, port, prefix, bindaddr, self.httpd.port))
+            if opts['port']:
+                write = ui.status
+            else:
+                write = ui.write
+            write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
+                  (fqaddr, port, prefix, bindaddr, self.httpd.port))
 
         def run(self):
             self.httpd.serve_forever()
@@ -3047,7 +3083,7 @@
         ui.status(m)
 
     st = list(repo.status(unknown=True))[:6]
-    ms = merge_.mergestate(repo)
+    ms = mergemod.mergestate(repo)
     st.append([f for f in ms if ms[f] == 'u'])
     labels = [_('%d modified'), _('%d added'), _('%d removed'),
               _('%d deleted'), _('%d unknown'), _('%d ignored'),
@@ -3771,7 +3807,8 @@
           ('d', 'daemon', None, _('run server in background')),
           ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
           ('E', 'errorlog', '', _('name of error log file to write to')),
-          ('p', 'port', 0, _('port to listen on (default: 8000)')),
+          # use string type, then we can check if something was passed
+          ('p', 'port', '', _('port to listen on (default: 8000')),
           ('a', 'address', '',
            _('address to listen on (default: all interfaces)')),
           ('', 'prefix', '',
--- a/mercurial/dispatch.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/mercurial/dispatch.py	Sat Mar 13 20:02:46 2010 +0100
@@ -9,7 +9,7 @@
 import os, sys, atexit, signal, pdb, socket, errno, shlex, time
 import util, commands, hg, fancyopts, extensions, hook, error
 import cmdutil, encoding
-import ui as _ui
+import ui as uimod
 
 def run():
     "run the command in sys.argv"
@@ -18,7 +18,7 @@
 def dispatch(args):
     "run the command specified in args"
     try:
-        u = _ui.ui()
+        u = uimod.ui()
         if '--traceback' in args:
             u.setconfig('ui', 'traceback', 'on')
     except util.Abort, inst:
--- a/mercurial/graphmod.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/mercurial/graphmod.py	Sat Mar 13 20:02:46 2010 +0100
@@ -115,7 +115,7 @@
                 edges.append((ecol, next.index(eid), colors[eid]))
             elif eid == cur:
                 for p in parents:
-                    edges.append((ecol, next.index(p), colors[p]))
+                    edges.append((ecol, next.index(p), color))
 
         # Yield and move on
         yield (cur, type, data, (col, color), edges)
--- a/mercurial/hg.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/mercurial/hg.py	Sat Mar 13 20:02:46 2010 +0100
@@ -10,8 +10,8 @@
 from lock import release
 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
 import lock, util, extensions, error, encoding, node
-import merge as _merge
-import verify as _verify
+import merge as mergemod
+import verify as verifymod
 import errno, os, shutil
 
 def _local(path):
@@ -358,7 +358,7 @@
 
 def update(repo, node):
     """update the working directory to node, merging linear changes"""
-    stats = _merge.update(repo, node, False, False, None)
+    stats = mergemod.update(repo, node, False, False, None)
     _showstats(repo, stats)
     if stats[3]:
         repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
@@ -369,14 +369,14 @@
 
 def clean(repo, node, show_stats=True):
     """forcibly switch the working directory to node, clobbering changes"""
-    stats = _merge.update(repo, node, False, True, None)
+    stats = mergemod.update(repo, node, False, True, None)
     if show_stats:
         _showstats(repo, stats)
     return stats[3] > 0
 
 def merge(repo, node, force=None, remind=True):
     """branch merge with node, resolving changes"""
-    stats = _merge.update(repo, node, True, force, False)
+    stats = mergemod.update(repo, node, True, force, False)
     _showstats(repo, stats)
     if stats[3]:
         repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
@@ -387,8 +387,8 @@
 
 def revert(repo, node, choose):
     """revert changes to revision in node without updating dirstate"""
-    return _merge.update(repo, node, False, True, choose)[3] > 0
+    return mergemod.update(repo, node, False, True, choose)[3] > 0
 
 def verify(repo):
     """verify the consistency of a repository"""
-    return _verify.verify(repo)
+    return verifymod.verify(repo)
--- a/mercurial/hgweb/hgwebdir_mod.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/mercurial/hgweb/hgwebdir_mod.py	Sat Mar 13 20:02:46 2010 +0100
@@ -195,11 +195,8 @@
                     yield {"type" : i[0], "extension": i[1],
                            "node": nodeid, "url": url}
 
-        sortdefault = None, False
-        def entries(sortcolumn="", descending=False, subdir="", **map):
+        def rawentries(subdir="", **map):
 
-            rows = []
-            parity = paritygen(self.stripecount)
             descend = self.ui.configbool('web', 'descend', True)
             for name, path in self.repos:
 
@@ -251,19 +248,19 @@
                            lastchange=d,
                            lastchange_sort=d[1]-d[0],
                            archives=archivelist(u, "tip", url))
-                if (not sortcolumn or (sortcolumn, descending) == sortdefault):
-                    # fast path for unsorted output
-                    row['parity'] = parity.next()
-                    yield row
-                else:
-                    rows.append((row["%s_sort" % sortcolumn], row))
-            if rows:
-                rows.sort()
-                if descending:
-                    rows.reverse()
-                for key, row in rows:
-                    row['parity'] = parity.next()
-                    yield row
+                yield row
+
+        sortdefault = None, False
+        def entries(sortcolumn="", descending=False, subdir="", **map):
+            rows = rawentries(subdir=subdir, **map)
+
+            if sortcolumn and sortdefault != (sortcolumn, descending):
+                sortkey = '%s_sort' % sortcolumn
+                rows = sorted(rows, key=lambda x: x[sortkey],
+                              reverse=descending)
+            for row, parity in zip(rows, paritygen(self.stripecount)):
+                row['parity'] = parity
+                yield row
 
         self.refresh()
         sortable = ["name", "description", "contact", "lastchange"]
--- a/mercurial/hgweb/server.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/mercurial/hgweb/server.py	Sat Mar 13 20:02:46 2010 +0100
@@ -8,8 +8,6 @@
 
 import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
 from mercurial import hg, util, error
-from hgweb_mod import hgweb
-from hgwebdir_mod import hgwebdir
 from mercurial.i18n import _
 
 def _splitURI(uri):
@@ -195,104 +193,85 @@
             self.close_connection = True
             pass
 
-def create_server(ui, repo):
-    use_threads = True
-
-    def openlog(opt, default):
-        if opt and opt != '-':
-            return open(opt, 'a')
-        return default
-
-    if repo is None:
-        myui = ui
+try:
+    from threading import activeCount
+    _mixin = SocketServer.ThreadingMixIn
+except ImportError:
+    if hasattr(os, "fork"):
+        _mixin = SocketServer.ForkingMixIn
     else:
-        myui = repo.ui
-    address = myui.config("web", "address", "")
-    port = int(myui.config("web", "port", 8000))
-    prefix = myui.config("web", "prefix", "")
-    if prefix:
-        prefix = "/" + prefix.strip("/")
-    use_ipv6 = myui.configbool("web", "ipv6")
-    webdir_conf = myui.config("web", "webdir_conf")
-    ssl_cert = myui.config("web", "certificate")
-    accesslog = openlog(myui.config("web", "accesslog", "-"), sys.stdout)
-    errorlog = openlog(myui.config("web", "errorlog", "-"), sys.stderr)
+        class _mixin:
+            pass
+
+def openlog(opt, default):
+    if opt and opt != '-':
+        return open(opt, 'a')
+    return default
 
-    if use_threads:
-        try:
-            from threading import activeCount
-        except ImportError:
-            use_threads = False
+class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
 
-    if use_threads:
-        _mixin = SocketServer.ThreadingMixIn
-    else:
-        if hasattr(os, "fork"):
-            _mixin = SocketServer.ForkingMixIn
-        else:
-            class _mixin:
-                pass
+    # SO_REUSEADDR has broken semantics on windows
+    if os.name == 'nt':
+        allow_reuse_address = 0
 
-    class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
+    def __init__(self, ui, app, addr, handler, **kwargs):
+        BaseHTTPServer.HTTPServer.__init__(self, addr, handler, **kwargs)
+        self.daemon_threads = True
+        self.application = app
 
-        # SO_REUSEADDR has broken semantics on windows
-        if os.name == 'nt':
-            allow_reuse_address = 0
+        ssl_cert = ui.config('web', 'certificate')
+        if ssl_cert:
+            try:
+                from OpenSSL import SSL
+                ctx = SSL.Context(SSL.SSLv23_METHOD)
+            except ImportError:
+                raise util.Abort(_("SSL support is unavailable"))
+            ctx.use_privatekey_file(ssl_cert)
+            ctx.use_certificate_file(ssl_cert)
+            sock = socket.socket(self.address_family, self.socket_type)
+            self.socket = SSL.Connection(ctx, sock)
+            self.server_bind()
+            self.server_activate()
 
-        def __init__(self, *args, **kargs):
-            BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs)
-            self.accesslog = accesslog
-            self.errorlog = errorlog
-            self.daemon_threads = True
-            def make_handler():
-                if webdir_conf:
-                    hgwebobj = hgwebdir(webdir_conf, ui)
-                elif repo is not None:
-                    hgwebobj = hgweb(hg.repository(repo.ui, repo.root))
-                else:
-                    raise error.RepoError(_("There is no Mercurial repository"
-                                            " here (.hg not found)"))
-                return hgwebobj
-            self.application = make_handler()
+        prefix = ui.config('web', 'prefix', '')
+        if prefix:
+            prefix = '/' + prefix.strip('/')
+        self.prefix = prefix
 
-            if ssl_cert:
-                try:
-                    from OpenSSL import SSL
-                    ctx = SSL.Context(SSL.SSLv23_METHOD)
-                except ImportError:
-                    raise util.Abort(_("SSL support is unavailable"))
-                ctx.use_privatekey_file(ssl_cert)
-                ctx.use_certificate_file(ssl_cert)
-                sock = socket.socket(self.address_family, self.socket_type)
-                self.socket = SSL.Connection(ctx, sock)
-                self.server_bind()
-                self.server_activate()
+        alog = openlog(ui.config('web', 'accesslog', '-'), sys.stdout)
+        elog = openlog(ui.config('web', 'errorlog', '-'), sys.stderr)
+        self.accesslog = alog
+        self.errorlog = elog
+
+        self.addr, self.port = self.socket.getsockname()[0:2]
+        self.fqaddr = socket.getfqdn(addr[0])
 
-            self.addr, self.port = self.socket.getsockname()[0:2]
-            self.prefix = prefix
-            self.fqaddr = socket.getfqdn(address)
-
-    class IPv6HTTPServer(MercurialHTTPServer):
-        address_family = getattr(socket, 'AF_INET6', None)
+class IPv6HTTPServer(MercurialHTTPServer):
+    address_family = getattr(socket, 'AF_INET6', None)
+    def __init__(self, *args, **kwargs):
+        if self.address_family is None:
+            raise error.RepoError(_('IPv6 is not available on this system'))
+        super(IPv6HTTPServer, self).__init__(*args, **kwargs)
 
-        def __init__(self, *args, **kwargs):
-            if self.address_family is None:
-                raise error.RepoError(_('IPv6 is not available on this system'))
-            super(IPv6HTTPServer, self).__init__(*args, **kwargs)
+def create_server(ui, app):
 
-    if ssl_cert:
+    if ui.config('web', 'certificate'):
         handler = _shgwebhandler
     else:
         handler = _hgwebhandler
 
+    if ui.configbool('web', 'ipv6'):
+        cls = IPv6HTTPServer
+    else:
+        cls = MercurialHTTPServer
+
     # ugly hack due to python issue5853 (for threaded use)
     import mimetypes; mimetypes.init()
 
+    address = ui.config('web', 'address', '')
+    port = int(ui.config('web', 'port', 8000))
     try:
-        if use_ipv6:
-            return IPv6HTTPServer((address, port), handler)
-        else:
-            return MercurialHTTPServer((address, port), handler)
+        return cls(ui, app, (address, port), handler)
     except socket.error, inst:
         raise util.Abort(_("cannot start server at '%s:%d': %s")
                          % (address, port, inst.args[1]))
--- a/mercurial/localrepo.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/mercurial/localrepo.py	Sat Mar 13 20:02:46 2010 +0100
@@ -11,9 +11,9 @@
 import changelog, dirstate, filelog, manifest, context
 import lock, transaction, store, encoding
 import util, extensions, hook, error
-import match as match_
-import merge as merge_
-import tags as tags_
+import match as matchmod
+import merge as mergemod
+import tags as tagsmod
 from lock import release
 import weakref, stat, errno, os, time, inspect
 propertycache = util.propertycache
@@ -207,7 +207,7 @@
         if '.hgtags' not in self.dirstate:
             self.add(['.hgtags'])
 
-        m = match_.exact(self.root, '', ['.hgtags'])
+        m = matchmod.exact(self.root, '', ['.hgtags'])
         tagnode = self.commit(message, user, date, extra=extra, match=m)
 
         for name in names:
@@ -268,8 +268,8 @@
         alltags = {}                    # map tag name to (node, hist)
         tagtypes = {}
 
-        tags_.findglobaltags(self.ui, self, alltags, tagtypes)
-        tags_.readlocaltags(self.ui, self, alltags, tagtypes)
+        tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
+        tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
 
         # Build the return dicts.  Have to re-encode tag names because
         # the tags module always uses UTF-8 (in order not to lose info
@@ -503,7 +503,7 @@
             for pat, cmd in self.ui.configitems(filter):
                 if cmd == '!':
                     continue
-                mf = match_.match(self.root, '', [pat])
+                mf = matchmod.match(self.root, '', [pat])
                 fn = None
                 params = cmd
                 for name, filterfn in self._datafilters.iteritems():
@@ -767,7 +767,7 @@
             raise util.Abort('%s: %s' % (f, msg))
 
         if not match:
-            match = match_.always(self.root, '')
+            match = matchmod.always(self.root, '')
 
         if not force:
             vdirs = []
@@ -824,7 +824,7 @@
                 and self[None].branch() == self['.'].branch()):
                 return None
 
-            ms = merge_.mergestate(self)
+            ms = mergemod.mergestate(self)
             for f in changes[0]:
                 if f in ms and ms[f] == 'u':
                     raise util.Abort(_("unresolved merge conflicts "
@@ -996,7 +996,7 @@
 
         working = ctx2.rev() is None
         parentworking = working and ctx1 == self['.']
-        match = match or match_.always(self.root, self.getcwd())
+        match = match or matchmod.always(self.root, self.getcwd())
         listignored, listclean, listunknown = ignored, clean, unknown
 
         # load earliest manifest first for caching reasons
--- a/mercurial/mdiff.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/mercurial/mdiff.py	Sat Mar 13 20:02:46 2010 +0100
@@ -125,12 +125,12 @@
     else:
         al = splitnewlines(a)
         bl = splitnewlines(b)
-        l = list(bunidiff(a, b, al, bl, "a/" + fn1, "b/" + fn2, opts=opts))
+        l = list(_unidiff(a, b, al, bl, opts=opts))
         if not l:
             return ""
-        # difflib uses a space, rather than a tab
-        l[0] = "%s%s" % (l[0][:-2], datetag(ad))
-        l[1] = "%s%s" % (l[1][:-2], datetag(bd))
+
+        l.insert(0, "--- a/%s%s" % (fn1, datetag(ad)))
+        l.insert(1, "+++ b/%s%s" % (fn2, datetag(bd)))
 
     for ln in xrange(len(l)):
         if l[ln][-1] != '\n':
@@ -141,11 +141,10 @@
 
     return "".join(l)
 
-# somewhat self contained replacement for difflib.unified_diff
+# creates a headerless unified diff
 # t1 and t2 are the text to be diffed
 # l1 and l2 are the text broken up into lines
-# header1 and header2 are the filenames for the diff output
-def bunidiff(t1, t2, l1, l2, header1, header2, opts=defaultopts):
+def _unidiff(t1, t2, l1, l2, opts=defaultopts):
     def contextend(l, len):
         ret = l + opts.context
         if ret > len:
@@ -158,10 +157,7 @@
             return 0
         return ret
 
-    def yieldhunk(hunk, header):
-        if header:
-            for x in header:
-                yield x
+    def yieldhunk(hunk):
         (astart, a2, bstart, b2, delta) = hunk
         aend = contextend(a2, len(l1))
         alen = aend - astart
@@ -184,8 +180,6 @@
         for x in xrange(a2, aend):
             yield ' ' + l1[x]
 
-    header = ["--- %s\t\n" % header1, "+++ %s\t\n" % header2]
-
     if opts.showfunc:
         funcre = re.compile('\w')
 
@@ -236,11 +230,8 @@
                 astart = hunk[1]
                 bstart = hunk[3]
             else:
-                for x in yieldhunk(hunk, header):
+                for x in yieldhunk(hunk):
                     yield x
-                # we only want to yield the header if the files differ, and
-                # we only want to yield it once.
-                header = None
         if prev:
             # we've joined the previous hunk, record the new ending points.
             hunk[1] = a2
@@ -255,7 +246,7 @@
         delta[len(delta):] = ['+' + x for x in new]
 
     if hunk:
-        for x in yieldhunk(hunk, header):
+        for x in yieldhunk(hunk):
             yield x
 
 def patchtext(bin):
--- a/mercurial/patch.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/mercurial/patch.py	Sat Mar 13 20:02:46 2010 +0100
@@ -1175,20 +1175,6 @@
         return -1
     return err
 
-def diffopts(ui, opts=None, untrusted=False):
-    def get(key, name=None, getter=ui.configbool):
-        return ((opts and opts.get(key)) or
-                getter('diff', name or key, None, untrusted=untrusted))
-    return mdiff.diffopts(
-        text=opts and opts.get('text'),
-        git=get('git'),
-        nodates=get('nodates'),
-        showfunc=get('show_function', 'showfunc'),
-        ignorews=get('ignore_all_space', 'ignorews'),
-        ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
-        ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
-        context=get('unified', getter=ui.config))
-
 def updatedir(ui, repo, patches, similarity=0):
     '''Update dirstate after patch application according to metadata'''
     if not patches:
@@ -1376,6 +1362,20 @@
 class GitDiffRequired(Exception):
     pass
 
+def diffopts(ui, opts=None, untrusted=False):
+    def get(key, name=None, getter=ui.configbool):
+        return ((opts and opts.get(key)) or
+                getter('diff', name or key, None, untrusted=untrusted))
+    return mdiff.diffopts(
+        text=opts and opts.get('text'),
+        git=get('git'),
+        nodates=get('nodates'),
+        showfunc=get('show_function', 'showfunc'),
+        ignorews=get('ignore_all_space', 'ignorews'),
+        ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
+        ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
+        context=get('unified', getter=ui.config))
+
 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
          losedatafn=None):
     '''yields diff of changes to files between two nodes, or node and
@@ -1551,47 +1551,6 @@
             if text:
                 yield text
 
-def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
-           opts=None):
-    '''export changesets as hg patches.'''
-
-    total = len(revs)
-    revwidth = max([len(str(rev)) for rev in revs])
-
-    def single(rev, seqno, fp):
-        ctx = repo[rev]
-        node = ctx.node()
-        parents = [p.node() for p in ctx.parents() if p]
-        branch = ctx.branch()
-        if switch_parent:
-            parents.reverse()
-        prev = (parents and parents[0]) or nullid
-
-        if not fp:
-            fp = cmdutil.make_file(repo, template, node, total=total,
-                                   seqno=seqno, revwidth=revwidth,
-                                   mode='ab')
-        if fp != sys.stdout and hasattr(fp, 'name'):
-            repo.ui.note("%s\n" % fp.name)
-
-        fp.write("# HG changeset patch\n")
-        fp.write("# User %s\n" % ctx.user())
-        fp.write("# Date %d %d\n" % ctx.date())
-        if branch and (branch != 'default'):
-            fp.write("# Branch %s\n" % branch)
-        fp.write("# Node ID %s\n" % hex(node))
-        fp.write("# Parent  %s\n" % hex(prev))
-        if len(parents) > 1:
-            fp.write("# Parent  %s\n" % hex(parents[1]))
-        fp.write(ctx.description().rstrip())
-        fp.write("\n\n")
-
-        for chunk in diff(repo, prev, node, opts=opts):
-            fp.write(chunk)
-
-    for seqno, rev in enumerate(revs):
-        single(rev, seqno + 1, fp)
-
 def diffstatdata(lines):
     filename, adds, removes = None, 0, 0
     for line in lines:
--- a/mercurial/pure/osutil.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/mercurial/pure/osutil.py	Sat Mar 13 20:02:46 2010 +0100
@@ -6,25 +6,25 @@
 # GNU General Public License version 2 or any later version.
 
 import os
-import stat as _stat
+import stat as statmod
 
 posixfile = open
 
 def _mode_to_kind(mode):
-    if _stat.S_ISREG(mode):
-        return _stat.S_IFREG
-    if _stat.S_ISDIR(mode):
-        return _stat.S_IFDIR
-    if _stat.S_ISLNK(mode):
-        return _stat.S_IFLNK
-    if _stat.S_ISBLK(mode):
-        return _stat.S_IFBLK
-    if _stat.S_ISCHR(mode):
-        return _stat.S_IFCHR
-    if _stat.S_ISFIFO(mode):
-        return _stat.S_IFIFO
-    if _stat.S_ISSOCK(mode):
-        return _stat.S_IFSOCK
+    if statmod.S_ISREG(mode):
+        return statmod.S_IFREG
+    if statmod.S_ISDIR(mode):
+        return statmod.S_IFDIR
+    if statmod.S_ISLNK(mode):
+        return statmod.S_IFLNK
+    if statmod.S_ISBLK(mode):
+        return statmod.S_IFBLK
+    if statmod.S_ISCHR(mode):
+        return statmod.S_IFCHR
+    if statmod.S_ISFIFO(mode):
+        return statmod.S_IFIFO
+    if statmod.S_ISSOCK(mode):
+        return statmod.S_IFSOCK
     return mode
 
 def listdir(path, stat=False, skip=None):
@@ -49,7 +49,7 @@
     names.sort()
     for fn in names:
         st = os.lstat(prefix + fn)
-        if fn == skip and _stat.S_ISDIR(st.st_mode):
+        if fn == skip and statmod.S_ISDIR(st.st_mode):
             return []
         if stat:
             result.append((fn, _mode_to_kind(st.st_mode), st))
--- a/mercurial/subrepo.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/mercurial/subrepo.py	Sat Mar 13 20:02:46 2010 +0100
@@ -225,8 +225,9 @@
             self._repo.lookup(revision)
         except error.RepoError:
             self._repo._subsource = source
-            self._repo.ui.status(_('pulling subrepo %s\n') % self._path)
             srcurl = _abssource(self._repo)
+            self._repo.ui.status(_('pulling subrepo %s from %s\n')
+                                 % (self._path, srcurl))
             other = hg.repository(self._repo.ui, srcurl)
             self._repo.pull(other)
 
--- a/mercurial/templatefilters.py	Thu Mar 11 00:28:31 2010 +0900
+++ b/mercurial/templatefilters.py	Sat Mar 13 20:02:46 2010 +0100
@@ -14,15 +14,13 @@
         return "".join([stringify(t) for t in thing if t is not None])
     return str(thing)
 
-agescales = [("second", 1),
-             ("minute", 60),
-             ("hour", 3600),
-             ("day", 3600 * 24),
+agescales = [("year", 3600 * 24 * 365),
+             ("month", 3600 * 24 * 30),
              ("week", 3600 * 24 * 7),
-             ("month", 3600 * 24 * 30),
-             ("year", 3600 * 24 * 365)]
-
-agescales.reverse()
+             ("day", 3600 * 24),
+             ("hour", 3600),
+             ("minute", 60),
+             ("second", 1),]
 
 def age(date):
     '''turn a (timestamp, tzoff) tuple into an age string.'''
--- a/tests/test-archive	Thu Mar 11 00:28:31 2010 +0900
+++ b/tests/test-archive	Sat Mar 13 20:02:46 2010 +0100
@@ -74,6 +74,20 @@
 hg archive -t tgz -p %b-%h test-%h.tar.gz
 gzip -dc test-$QTIP.tar.gz | tar tf - 2>/dev/null | sed "s/$QTIP/TIP/"
 
+hg archive autodetected_test.tar
+tar tf autodetected_test.tar
+
+# The '-t' should override autodetection
+hg archive -t tar autodetect_override_test.zip
+tar tf autodetect_override_test.zip
+
+for ext in tar tar.gz tgz tar.bz2 tbz2 zip; do
+    hg archive auto_test.$ext
+    if [ -d auto_test.$ext ]; then
+        echo "extension $ext was not autodetected."
+    fi
+done
+
 cat > md5comp.py <<EOF
 try:
     from hashlib import md5
--- a/tests/test-archive.out	Thu Mar 11 00:28:31 2010 +0900
+++ b/tests/test-archive.out	Sat Mar 13 20:02:46 2010 +0100
@@ -45,6 +45,14 @@
 test-TIP/bar
 test-TIP/baz/bletch
 test-TIP/foo
+autodetected_test/.hg_archival.txt
+autodetected_test/bar
+autodetected_test/baz/bletch
+autodetected_test/foo
+autodetect_override_test.zip/.hg_archival.txt
+autodetect_override_test.zip/bar
+autodetect_override_test.zip/baz/bletch
+autodetect_override_test.zip/foo
 True
 abort: archive prefix contains illegal components
 Archive:  test.zip
--- a/tests/test-rebase-pull.out	Thu Mar 11 00:28:31 2010 +0900
+++ b/tests/test-rebase-pull.out	Sat Mar 13 20:02:46 2010 +0100
@@ -10,7 +10,6 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files (+1 heads)
-(run 'hg heads' to see heads, 'hg merge' to merge)
 saving bundle to 
 adding branch
 adding changesets
@@ -39,8 +38,8 @@
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
-(run 'hg update' to get a working copy)
 nothing to rebase
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 @  2
 |
 
--- a/tests/test-serve	Thu Mar 11 00:28:31 2010 +0900
+++ b/tests/test-serve	Sat Mar 13 20:02:46 2010 +0100
@@ -2,8 +2,10 @@
 
 hgserve()
 {
-    hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid -E errors.log -v $@ \
-        | sed -e 's/:[0-9][0-9]*//g' -e 's/http:\/\/[^/]*\//http:\/\/localhost\//'
+    hg serve -a localhost -d --pid-file=hg.pid -E errors.log -v $@ \
+        | sed -e "s/:$HGPORT1\\([^0-9]\\)/:HGPORT1\1/g" \
+              -e "s/:$HGPORT2\\([^0-9]\\)/:HGPORT2\1/g" \
+              -e 's/http:\/\/[^/]*\//http:\/\/localhost\//'
     cat hg.pid >> "$DAEMON_PIDS"
     echo % errors
     cat errors.log
@@ -17,6 +19,7 @@
 
 echo '[web]' > .hg/hgrc
 echo 'accesslog = access.log' >> .hg/hgrc
+echo "port = $HGPORT1" >> .hg/hgrc
 
 echo % Without -v
 hg serve -a localhost -p $HGPORT -d --pid-file=hg.pid -E errors.log
@@ -30,6 +33,9 @@
 echo % With -v
 hgserve
 
+echo % With -v and -p HGPORT2
+hgserve -p "$HGPORT2"
+
 echo % With --prefix foo
 hgserve --prefix foo
 
--- a/tests/test-serve.out	Thu Mar 11 00:28:31 2010 +0900
+++ b/tests/test-serve.out	Sat Mar 13 20:02:46 2010 +0100
@@ -2,17 +2,20 @@
 access log created - .hg/hgrc respected
 % errors
 % With -v
-listening at http://localhost/ (bound to 127.0.0.1)
+listening at http://localhost/ (bound to 127.0.0.1:HGPORT1)
+% errors
+% With -v and -p HGPORT2
+listening at http://localhost/ (bound to 127.0.0.1:HGPORT2)
 % errors
 % With --prefix foo
-listening at http://localhost/foo/ (bound to 127.0.0.1)
+listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
 % errors
 % With --prefix /foo
-listening at http://localhost/foo/ (bound to 127.0.0.1)
+listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
 % errors
 % With --prefix foo/
-listening at http://localhost/foo/ (bound to 127.0.0.1)
+listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
 % errors
 % With --prefix /foo/
-listening at http://localhost/foo/ (bound to 127.0.0.1)
+listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
 % errors
--- a/tests/test-subrepo	Thu Mar 11 00:28:31 2010 +0900
+++ b/tests/test-subrepo	Sat Mar 13 20:02:46 2010 +0100
@@ -78,7 +78,7 @@
 
 echo % clone
 cd ..
-hg clone t tc
+hg clone t tc | sed 's|from .*/sub|from .../sub|g'
 cd tc
 hg debugsub
 
@@ -102,7 +102,8 @@
 echo % pull
 cd ../tc
 hg pull | sed 's/ .*sub/ ...sub/g'
-hg up # should pull t
+# should pull t
+hg up | sed 's|from .*/sub|from .../sub|g'
 cat t/t
 
 echo % bogus subrepo path aborts
--- a/tests/test-subrepo.out	Thu Mar 11 00:28:31 2010 +0900
+++ b/tests/test-subrepo.out	Sat Mar 13 20:02:46 2010 +0100
@@ -108,19 +108,19 @@
 >>>>>>> other
 % clone
 updating to branch default
-pulling subrepo s
+pulling subrepo s from .../sub/t/s
 requesting all changes
 adding changesets
 adding manifests
 adding file changes
 added 4 changesets with 5 changes to 3 files
-pulling subrepo ss
+pulling subrepo ss from .../sub/t/s/ss
 requesting all changes
 adding changesets
 adding manifests
 adding file changes
 added 1 changesets with 1 changes to 1 files
-pulling subrepo t
+pulling subrepo t from .../sub/t/t
 requesting all changes
 adding changesets
 adding manifests
@@ -197,7 +197,7 @@
 adding file changes
 added 1 changesets with 1 changes to 1 files
 (run 'hg update' to get a working copy)
-pulling subrepo t
+pulling subrepo t from .../sub/t/t
 searching for changes
 adding changesets
 adding manifests