changeset 14476:658eca733a35

merge with hg-i18n
author Martin Geisler <mg@lazybytes.net>
date Mon, 30 May 2011 12:20:36 +0200
parents d1a1578c5f78 (diff) ac9a89dbdc00 (current diff)
children f31e250c4380
files
diffstat 47 files changed, 1521 insertions(+), 473 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/graphlog.py	Mon May 30 11:52:17 2011 +0200
+++ b/hgext/graphlog.py	Mon May 30 12:20:36 2011 +0200
@@ -383,11 +383,11 @@
 
 def uisetup(ui):
     '''Initialize the extension.'''
-    _wrapcmd(ui, 'log', commands.table, graphlog)
-    _wrapcmd(ui, 'incoming', commands.table, gincoming)
-    _wrapcmd(ui, 'outgoing', commands.table, goutgoing)
+    _wrapcmd('log', commands.table, graphlog)
+    _wrapcmd('incoming', commands.table, gincoming)
+    _wrapcmd('outgoing', commands.table, goutgoing)
 
-def _wrapcmd(ui, cmd, table, wrapfn):
+def _wrapcmd(cmd, table, wrapfn):
     '''wrap the command'''
     def graph(orig, *args, **kwargs):
         if kwargs['graph']:
--- a/hgext/mq.py	Mon May 30 11:52:17 2011 +0200
+++ b/hgext/mq.py	Mon May 30 12:20:36 2011 +0200
@@ -620,7 +620,7 @@
         files = {}
         try:
             fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
-                                  cwd=repo.root, files=files, eolmode=None)
+                                  files=files, eolmode=None)
             return (True, list(files), fuzz)
         except Exception, inst:
             self.ui.note(str(inst) + '\n')
@@ -735,10 +735,9 @@
         if not keep:
             r = self.qrepo()
             if r:
-                r[None].remove(patches, True)
-            else:
-                for p in patches:
-                    os.unlink(self.join(p))
+                r[None].forget(patches)
+            for p in patches:
+                os.unlink(self.join(p))
 
         if numrevs:
             qfinished = self.applied[:numrevs]
@@ -873,6 +872,14 @@
                 raise util.Abort(_('"%s" cannot be used in the name of a patch')
                                  % c)
 
+    def checkpatchname(self, name, force=False):
+        self.check_reserved_name(name)
+        if not force and os.path.exists(self.join(name)):
+            if os.path.isdir(self.join(name)):
+                raise util.Abort(_('"%s" already exists as a directory')
+                                 % name)
+            else:
+                raise util.Abort(_('patch "%s" already exists') % name)
 
     def new(self, repo, patchfn, *pats, **opts):
         """options:
@@ -884,14 +891,8 @@
         if date:
             date = util.parsedate(date)
         diffopts = self.diffopts({'git': opts.get('git')})
-        self.check_reserved_name(patchfn)
-        if os.path.exists(self.join(patchfn)):
-            if os.path.isdir(self.join(patchfn)):
-                raise util.Abort(_('"%s" already exists as a directory')
-                                 % patchfn)
-            else:
-                raise util.Abort(_('patch "%s" already exists') % patchfn)
-
+        if opts.get('checkname', True):
+            self.checkpatchname(patchfn)
         inclsubs = self.check_substate(repo)
         if inclsubs:
             inclsubs.append('.hgsubstate')
@@ -1300,7 +1301,7 @@
                     except OSError, e:
                         if e.errno != errno.ENOENT:
                             raise
-                    repo.dirstate.forget(f)
+                    repo.dirstate.drop(f)
                 for f in m + r:
                     fctx = ctx[f]
                     repo.wwrite(f, fctx.data(), fctx.flags())
@@ -1478,7 +1479,7 @@
                 for f in mm:
                     repo.dirstate.normallookup(f)
                 for f in forget:
-                    repo.dirstate.forget(f)
+                    repo.dirstate.drop(f)
 
                 if not msg:
                     if not ph.message:
@@ -1739,10 +1740,6 @@
             if patchname in self.series:
                 raise util.Abort(_('patch %s is already in the series file')
                                  % patchname)
-        def checkfile(patchname):
-            if not force and os.path.exists(self.join(patchname)):
-                raise util.Abort(_('patch "%s" already exists')
-                                 % patchname)
 
         if rev:
             if files:
@@ -1790,9 +1787,8 @@
 
                 if not patchname:
                     patchname = normname('%d.diff' % r)
-                self.check_reserved_name(patchname)
                 checkseries(patchname)
-                checkfile(patchname)
+                self.checkpatchname(patchname, force)
                 self.full_series.insert(0, patchname)
 
                 patchf = self.opener(patchname, "w")
@@ -1819,8 +1815,7 @@
                     raise util.Abort(_("patch %s does not exist") % filename)
 
                 if patchname:
-                    self.check_reserved_name(patchname)
-                    checkfile(patchname)
+                    self.checkpatchname(patchname, force)
 
                     self.ui.write(_('renaming %s to %s\n')
                                         % (filename, patchname))
@@ -1829,11 +1824,13 @@
                     patchname = filename
 
             else:
+                if filename == '-' and not patchname:
+                    raise util.Abort(_('need --name to import a patch from -'))
+                elif not patchname:
+                    patchname = normname(os.path.basename(filename.rstrip('/')))
+                self.checkpatchname(patchname, force)
                 try:
                     if filename == '-':
-                        if not patchname:
-                            raise util.Abort(
-                                _('need --name to import a patch from -'))
                         text = sys.stdin.read()
                     else:
                         fp = url.open(self.ui, filename)
@@ -1841,10 +1838,6 @@
                         fp.close()
                 except (OSError, IOError):
                     raise util.Abort(_("unable to read file %s") % filename)
-                if not patchname:
-                    patchname = normname(os.path.basename(filename))
-                self.check_reserved_name(patchname)
-                checkfile(patchname)
                 patchf = self.opener(patchname, "w")
                 patchf.write(text)
                 patchf.close()
@@ -2599,12 +2592,7 @@
     if os.path.isdir(absdest):
         name = normname(os.path.join(name, os.path.basename(patch)))
         absdest = q.join(name)
-    if os.path.exists(absdest):
-        raise util.Abort(_('%s already exists') % absdest)
-
-    if name in q.series:
-        raise util.Abort(
-            _('A patch named %s already exists in the series file') % name)
+    q.checkpatchname(name)
 
     ui.note(_('renaming %s to %s\n') % (patch, name))
     i = q.find_series(patch)
@@ -2628,13 +2616,13 @@
         wlock = r.wlock()
         try:
             if r.dirstate[patch] == 'a':
-                r.dirstate.forget(patch)
+                r.dirstate.drop(patch)
                 r.dirstate.add(name)
             else:
                 if r.dirstate[name] == 'r':
                     wctx.undelete([name])
                 wctx.copy(patch, name)
-                wctx.remove([patch], False)
+                wctx.forget([patch])
         finally:
             wlock.release()
 
@@ -3281,7 +3269,7 @@
     entry = extensions.wrapcommand(commands.table, 'init', mqinit)
     entry[1].extend(mqopt)
 
-    nowrap = set(commands.norepo.split(" ") + ['qrecord'])
+    nowrap = set(commands.norepo.split(" "))
 
     def dotable(cmdtable):
         for cmd in cmdtable.keys():
--- a/hgext/patchbomb.py	Mon May 30 11:52:17 2011 +0200
+++ b/hgext/patchbomb.py	Mon May 30 12:20:36 2011 +0200
@@ -106,7 +106,7 @@
         while patchlines and not patchlines[0].strip():
             patchlines.pop(0)
 
-    ds = patch.diffstat(patchlines)
+    ds = patch.diffstat(patchlines, git=opts.get('git'))
     if opts.get('diffstat'):
         body += ds + '\n\n'
 
--- a/hgext/rebase.py	Mon May 30 11:52:17 2011 +0200
+++ b/hgext/rebase.py	Mon May 30 12:20:36 2011 +0200
@@ -577,11 +577,14 @@
                 # there was nothing to rebase we force an update
                 hg.update(repo, dest)
     else:
+        if opts.get('tool'):
+            raise util.Abort(_('--tool can only be used with --rebase'))
         orig(ui, repo, *args, **opts)
 
 def uisetup(ui):
     'Replace pull with a decorator to provide --rebase option'
     entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
     entry[1].append(('', 'rebase', None,
-                     _("rebase working directory to branch head"))
-)
+                     _("rebase working directory to branch head")))
+    entry[1].append(('t', 'tool', '',
+                     _("specify merge tool for rebase")))
--- a/hgext/record.py	Mon May 30 11:52:17 2011 +0200
+++ b/hgext/record.py	Mon May 30 12:20:36 2011 +0200
@@ -12,6 +12,9 @@
 from mercurial import util
 import copy, cStringIO, errno, os, re, shutil, tempfile
 
+cmdtable = {}
+command = cmdutil.command(cmdtable)
+
 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
 
 def scanpatch(fp):
@@ -341,6 +344,9 @@
     return sum([h for h in applied.itervalues()
                if h[0].special() or len(h) > 1], [])
 
+@command("record",
+         commands.table['^commit|ci'][1], # same options as commit
+          _('hg record [OPTION]... [FILE]...'))
 def record(ui, repo, *pats, **opts):
     '''interactively select changes to commit
 
@@ -368,8 +374,20 @@
 
     This command is not available when committing a merge.'''
 
-    dorecord(ui, repo, commands.commit, *pats, **opts)
+    dorecord(ui, repo, commands.commit, 'commit', False, *pats, **opts)
+
+def qrefresh(ui, repo, *pats, **opts):
+    mq = extensions.find('mq')
 
+    def committomq(ui, repo, *pats, **opts):
+        # At this point the working copy contains only changes that
+        # were accepted. All other changes were reverted.
+        # We can't pass *pats here since qrefresh will undo all other
+        # changed files in the patch that aren't in pats.
+        mq.refresh(ui, repo, **opts)
+
+    # backup all changed files
+    dorecord(ui, repo, committomq, 'qrefresh', True, *pats, **opts)
 
 def qrecord(ui, repo, patch, *pats, **opts):
     '''interactively record a new patch
@@ -383,15 +401,18 @@
     except KeyError:
         raise util.Abort(_("'mq' extension not loaded"))
 
+    repo.mq.checkpatchname(patch)
+
     def committomq(ui, repo, *pats, **opts):
+        opts['checkname'] = False
         mq.new(ui, repo, patch, *pats, **opts)
 
-    dorecord(ui, repo, committomq, *pats, **opts)
-
+    dorecord(ui, repo, committomq, 'qnew', False, *pats, **opts)
 
-def dorecord(ui, repo, commitfunc, *pats, **opts):
+def dorecord(ui, repo, commitfunc, cmdsuggest, backupall, *pats, **opts):
     if not ui.interactive():
-        raise util.Abort(_('running non-interactively, use commit instead'))
+        raise util.Abort(_('running non-interactively, use %s instead') %
+                         cmdsuggest)
 
     def recordfunc(ui, repo, message, match, opts):
         """This is generic record driver.
@@ -440,18 +461,22 @@
         modified = set(changes[0])
 
         # 2. backup changed files, so we can restore them in the end
+        if backupall:
+            tobackup = changed
+        else:
+            tobackup = [f for f in newfiles if f in modified]
+
         backups = {}
-        backupdir = repo.join('record-backups')
-        try:
-            os.mkdir(backupdir)
-        except OSError, err:
-            if err.errno != errno.EEXIST:
-                raise
+        if tobackup:
+            backupdir = repo.join('record-backups')
+            try:
+                os.mkdir(backupdir)
+            except OSError, err:
+                if err.errno != errno.EEXIST:
+                    raise
         try:
             # backup continues
-            for f in newfiles:
-                if f not in modified:
-                    continue
+            for f in tobackup:
                 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
                                                dir=backupdir)
                 os.close(fd)
@@ -512,7 +537,8 @@
                     # writing it.
                     shutil.copystat(tmpname, repo.wjoin(realname))
                     os.unlink(tmpname)
-                os.rmdir(backupdir)
+                if tobackup:
+                    os.rmdir(backupdir)
             except OSError:
                 pass
 
@@ -528,15 +554,9 @@
     finally:
         ui.write = oldwrite
 
-cmdtable = {
-    "record":
-        (record, commands.table['^commit|ci'][1], # same options as commit
-         _('hg record [OPTION]... [FILE]...')),
-    "qrecord":
-        (qrecord, {}, # placeholder until mq is available
-         _('hg qrecord [OPTION]... PATCH [FILE]...')),
-}
-
+cmdtable["qrecord"] = \
+    (qrecord, [], # placeholder until mq is available
+     _('hg qrecord [OPTION]... PATCH [FILE]...'))
 
 def uisetup(ui):
     try:
@@ -544,11 +564,22 @@
     except KeyError:
         return
 
-    qcmdtable = {
-    "qrecord":
-        (qrecord, mq.cmdtable['^qnew'][1], # same options as qnew
-         _('hg qrecord [OPTION]... PATCH [FILE]...')),
-    }
+    cmdtable["qrecord"] = \
+        (qrecord,
+         # same options as qnew, but copy them so we don't get
+         # -i/--interactive for qrecord
+         mq.cmdtable['^qnew'][1][:],
+         _('hg qrecord [OPTION]... PATCH [FILE]...'))
 
-    cmdtable.update(qcmdtable)
+    _wrapcmd('qnew', mq.cmdtable, qrecord, _("interactively record a new patch"))
+    _wrapcmd('qrefresh', mq.cmdtable, qrefresh,
+             _("interactively select changes to refresh"))
 
+def _wrapcmd(cmd, table, wrapfn, msg):
+    '''wrap the command'''
+    def wrapper(orig, *args, **kwargs):
+        if kwargs['interactive']:
+            return wrapfn(*args, **kwargs)
+        return orig(*args, **kwargs)
+    entry = extensions.wrapcommand(table, cmd, wrapper)
+    entry[1].append(('i', 'interactive', None, msg))
--- a/hgext/transplant.py	Mon May 30 11:52:17 2011 +0200
+++ b/hgext/transplant.py	Mon May 30 12:20:36 2011 +0200
@@ -15,6 +15,7 @@
 
 from mercurial.i18n import _
 import os, tempfile
+from mercurial.node import short
 from mercurial import bundlerepo, hg, merge, match
 from mercurial import patch, revlog, scmutil, util, error, cmdutil
 from mercurial import revset, templatekw
@@ -110,7 +111,7 @@
             lock = repo.lock()
             for rev in revs:
                 node = revmap[rev]
-                revstr = '%s:%s' % (rev, revlog.short(node))
+                revstr = '%s:%s' % (rev, short(node))
 
                 if self.applied(repo, node, p1):
                     self.ui.warn(_('skipping already applied revision %s\n') %
@@ -143,7 +144,7 @@
 
                 if parents[1] != revlog.nullid:
                     self.ui.note(_('skipping merge changeset %s:%s\n')
-                                 % (rev, revlog.short(node)))
+                                 % (rev, short(node)))
                     patchfile = None
                 else:
                     fd, patchfile = tempfile.mkstemp(prefix='hg-transplant-')
@@ -163,11 +164,11 @@
                                           filter=opts.get('filter'))
                         if n and domerge:
                             self.ui.status(_('%s merged at %s\n') % (revstr,
-                                      revlog.short(n)))
+                                      short(n)))
                         elif n:
                             self.ui.status(_('%s transplanted to %s\n')
-                                           % (revlog.short(node),
-                                              revlog.short(n)))
+                                           % (short(node),
+                                              short(n)))
                     finally:
                         if patchfile:
                             os.unlink(patchfile)
@@ -219,7 +220,7 @@
             # we don't translate messages inserted into commits
             message += '\n(transplanted from %s)' % revlog.hex(node)
 
-        self.ui.status(_('applying %s\n') % revlog.short(node))
+        self.ui.status(_('applying %s\n') % short(node))
         self.ui.note('%s %s\n%s\n' % (user, date, message))
 
         if not patchfile and not merge:
@@ -227,8 +228,7 @@
         if patchfile:
             try:
                 files = {}
-                patch.patch(self.ui, repo, patchfile, cwd=repo.root,
-                            files=files, eolmode=None)
+                patch.patch(self.ui, repo, patchfile, files=files, eolmode=None)
                 files = list(files)
                 if not files:
                     self.ui.warn(_('%s: empty changeset') % revlog.hex(node))
@@ -271,8 +271,8 @@
         '''recover last transaction and apply remaining changesets'''
         if os.path.exists(os.path.join(self.path, 'journal')):
             n, node = self.recover(repo)
-            self.ui.status(_('%s transplanted as %s\n') % (revlog.short(node),
-                                                           revlog.short(n)))
+            self.ui.status(_('%s transplanted as %s\n') % (short(node),
+                                                           short(n)))
         seriespath = os.path.join(self.path, 'series')
         if not os.path.exists(seriespath):
             self.transplants.write()
--- a/mercurial/bundlerepo.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/bundlerepo.py	Mon May 30 12:20:36 2011 +0200
@@ -326,15 +326,14 @@
     if bundlename or not other.local():
         # create a bundle (uncompressed if other repo is not local)
 
-        if onlyheads is None and other.capable('changegroupsubset'):
-            onlyheads = rheads
-
         if other.capable('getbundle'):
-            cg = other.getbundle('incoming', common=common, heads=onlyheads)
-        elif onlyheads is None:
+            cg = other.getbundle('incoming', common=common, heads=rheads)
+        elif onlyheads is None and not other.capable('changegroupsubset'):
+            # compat with older servers when pulling all remote heads
             cg = other.changegroup(incoming, "incoming")
+            rheads = None
         else:
-            cg = other.changegroupsubset(incoming, onlyheads, 'incoming')
+            cg = other.changegroupsubset(incoming, rheads, 'incoming')
         bundletype = other.local() and "HG10BZ" or "HG10UN"
         fname = bundle = changegroup.writebundle(cg, bundlename, bundletype)
         # keep written bundle?
@@ -346,7 +345,7 @@
             # this repo contains local and other now, so filter out local again
             common = repo.heads()
 
-    csets = localrepo.changelog.findmissing(common, onlyheads)
+    csets = localrepo.changelog.findmissing(common, rheads)
 
     def cleanup():
         if bundlerepo:
--- a/mercurial/changelog.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/changelog.py	Mon May 30 12:20:36 2011 +0200
@@ -205,6 +205,11 @@
 
     def add(self, manifest, files, desc, transaction, p1, p2,
                   user, date=None, extra=None):
+        # Convert to UTF-8 encoded bytestrings as the very first
+        # thing: calling any method on a localstr object will turn it
+        # into a str object and the cached UTF-8 string is thus lost.
+        user, desc = encoding.fromlocal(user), encoding.fromlocal(desc)
+
         user = user.strip()
         # An empty username or a username with a "\n" will make the
         # revision text contain two "\n\n" sequences -> corrupt
@@ -218,8 +223,6 @@
         # strip trailing whitespace and leading and trailing empty lines
         desc = '\n'.join([l.rstrip() for l in desc.splitlines()]).strip('\n')
 
-        user, desc = encoding.fromlocal(user), encoding.fromlocal(desc)
-
         if date:
             parseddate = "%d %d" % util.parsedate(date)
         else:
--- a/mercurial/cmdutil.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/cmdutil.py	Mon May 30 12:20:36 2011 +0200
@@ -1215,9 +1215,9 @@
     def cmd(name, options, synopsis=None):
         def decorator(func):
             if synopsis:
-                table[name] = func, options, synopsis
+                table[name] = func, options[:], synopsis
             else:
-                table[name] = func, options
+                table[name] = func, options[:]
             return func
         return decorator
 
--- a/mercurial/commands.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/commands.py	Mon May 30 12:20:36 2011 +0200
@@ -8,7 +8,7 @@
 from node import hex, bin, nullid, nullrev, short
 from lock import release
 from i18n import _, gettext
-import os, re, sys, difflib, time, tempfile
+import os, re, sys, difflib, time, tempfile, errno
 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
 import patch, help, url, encoding, templatekw, discovery
 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
@@ -2269,7 +2269,7 @@
         if ui.verbose or not m.exact(f):
             ui.status(_('removing %s\n') % m.rel(f))
 
-    repo[None].remove(forget, unlink=False)
+    repo[None].forget(forget)
     return errs
 
 @command('grep',
@@ -3105,8 +3105,8 @@
                 repo.dirstate.setbranch(branch or 'default')
 
             files = {}
-            patch.patch(ui, repo, tmpname, strip=strip, cwd=repo.root,
-                        files=files, eolmode=None, similarity=sim / 100.0)
+            patch.patch(ui, repo, tmpname, strip=strip, files=files,
+                        eolmode=None, similarity=sim / 100.0)
             files = list(files)
             if opts.get('no_commit'):
                 if message:
@@ -3392,9 +3392,10 @@
     displayer.close()
 
 @command('manifest',
-    [('r', 'rev', '', _('revision to display'), _('REV'))],
+    [('r', 'rev', '', _('revision to display'), _('REV')),
+     ('', 'all', False, _("list files from all revisions"))],
     _('[-r REV]'))
-def manifest(ui, repo, node=None, rev=None):
+def manifest(ui, repo, node=None, rev=None, **opts):
     """output the current or given revision of the project manifest
 
     Print a list of version controlled files for the given revision.
@@ -3404,8 +3405,30 @@
     With -v, print file permissions, symlink and executable bits.
     With --debug, print file revision hashes.
 
+    If option --all is specified, the list of all files from all revisions
+    is printed. This includes deleted and renamed files.
+
     Returns 0 on success.
     """
+    if opts.get('all'):
+        if rev or node:
+            raise util.Abort(_("can't specify a revision with --all"))
+
+        res = []
+        prefix = "data/"
+        suffix = ".i"
+        plen = len(prefix)
+        slen = len(suffix)
+        lock = repo.lock()
+        try:
+            for fn, b, size in repo.store.datafiles():
+                if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
+                    res.append(fn[plen:-slen])
+        finally:
+            lock.release()
+        for f in sorted(res):
+            ui.write("%s\n" % f)
+        return
 
     if rev and node:
         raise util.Abort(_("please specify just one revision"))
@@ -3871,6 +3894,9 @@
       -A     W  W  W  R
       -Af    R  R  R  R
 
+    Note that remove never deletes files in Added [A] state from the
+    working directory, not even if option --force is specified.
+
     This command schedules the files to be removed at the next commit.
     To undo a remove before that, see :hg:`revert`.
 
@@ -3892,15 +3918,15 @@
             ret = 1
 
     if force:
-        remove, forget = modified + deleted + clean, added
+        list = modified + deleted + clean + added
     elif after:
-        remove, forget = deleted, []
+        list = deleted
         for f in modified + added + clean:
             ui.warn(_('not removing %s: file still exists (use -f'
                       ' to force removal)\n') % m.rel(f))
             ret = 1
     else:
-        remove, forget = deleted + clean, []
+        list = deleted + clean
         for f in modified:
             ui.warn(_('not removing %s: file is modified (use -f'
                       ' to force removal)\n') % m.rel(f))
@@ -3910,12 +3936,25 @@
                       ' to force removal)\n') % m.rel(f))
             ret = 1
 
-    for f in sorted(remove + forget):
+    for f in sorted(list):
         if ui.verbose or not m.exact(f):
             ui.status(_('removing %s\n') % m.rel(f))
 
-    repo[None].forget(forget)
-    repo[None].remove(remove, unlink=not after)
+    wlock = repo.wlock()
+    try:
+        if not after:
+            for f in list:
+                if f in added:
+                    continue # we never unlink added files on remove
+                try:
+                    util.unlinkpath(repo.wjoin(f))
+                except OSError, inst:
+                    if inst.errno != errno.ENOENT:
+                        raise
+        repo[None].forget(list)
+    finally:
+        wlock.release()
+
     return ret
 
 @command('rename|move|mv',
@@ -4238,7 +4277,7 @@
             audit_path = scmutil.pathauditor(repo.root)
             for f in remove[0]:
                 if repo.dirstate[f] == 'a':
-                    repo.dirstate.forget(f)
+                    repo.dirstate.drop(f)
                     continue
                 audit_path(f)
                 try:
--- a/mercurial/context.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/context.py	Mon May 30 12:20:36 2011 +0200
@@ -835,14 +835,16 @@
         finally:
             wlock.release()
 
-    def forget(self, list):
+    def forget(self, files):
         wlock = self._repo.wlock()
         try:
-            for f in list:
+            for f in files:
                 if self._repo.dirstate[f] != 'a':
-                    self._repo.ui.warn(_("%s not added!\n") % f)
+                    self._repo.dirstate.remove(f)
+                elif f not in self._repo.dirstate:
+                    self._repo.ui.warn(_("%s not tracked!\n") % f)
                 else:
-                    self._repo.dirstate.forget(f)
+                    self._repo.dirstate.drop(f)
         finally:
             wlock.release()
 
@@ -852,20 +854,18 @@
             yield changectx(self._repo, a)
 
     def remove(self, list, unlink=False):
-        if unlink:
-            for f in list:
-                try:
-                    util.unlinkpath(self._repo.wjoin(f))
-                except OSError, inst:
-                    if inst.errno != errno.ENOENT:
-                        raise
         wlock = self._repo.wlock()
         try:
+            if unlink:
+                for f in list:
+                    try:
+                        util.unlinkpath(self._repo.wjoin(f))
+                    except OSError, inst:
+                        if inst.errno != errno.ENOENT:
+                            raise
             for f in list:
-                if unlink and os.path.lexists(self._repo.wjoin(f)):
-                    self._repo.ui.warn(_("%s still exists!\n") % f)
-                elif self._repo.dirstate[f] == 'a':
-                    self._repo.dirstate.forget(f)
+                if self._repo.dirstate[f] == 'a':
+                    self._repo.dirstate.drop(f)
                 elif f not in self._repo.dirstate:
                     self._repo.ui.warn(_("%s not tracked!\n") % f)
                 else:
--- a/mercurial/dirstate.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/dirstate.py	Mon May 30 12:20:36 2011 +0200
@@ -365,14 +365,11 @@
         if f in self._copymap:
             del self._copymap[f]
 
-    def forget(self, f):
-        '''Forget a file.'''
+    def drop(self, f):
+        '''Drop a file from the dirstate'''
         self._dirty = True
-        try:
-            self._droppath(f)
-            del self._map[f]
-        except KeyError:
-            self._ui.warn(_("not in dirstate: %s\n") % f)
+        self._droppath(f)
+        del self._map[f]
 
     def _normalize(self, path, isknown):
         normed = os.path.normcase(path)
--- a/mercurial/dispatch.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/dispatch.py	Mon May 30 12:20:36 2011 +0200
@@ -11,16 +11,22 @@
 import cmdutil, encoding
 import ui as uimod
 
+class request(object):
+    def __init__(self, args, ui=None):
+        self.args = args
+        self.ui = ui
+
 def run():
     "run the command in sys.argv"
-    sys.exit(dispatch(sys.argv[1:]))
+    sys.exit(dispatch(request(sys.argv[1:])))
 
-def dispatch(args):
-    "run the command specified in args"
+def dispatch(req):
+    "run the command specified in req.args"
     try:
-        u = uimod.ui()
-        if '--traceback' in args:
-            u.setconfig('ui', 'traceback', 'on')
+        if not req.ui:
+            req.ui = uimod.ui()
+        if '--traceback' in req.args:
+            req.ui.setconfig('ui', 'traceback', 'on')
     except util.Abort, inst:
         sys.stderr.write(_("abort: %s\n") % inst)
         if inst.hint:
@@ -33,12 +39,13 @@
         else:
             sys.stderr.write(_("hg: parse error: %s\n") % inst.args[0])
         return -1
-    return _runcatch(u, args)
+    return _runcatch(req)
 
-def _runcatch(ui, args):
+def _runcatch(req):
     def catchterm(*args):
         raise error.SignalInterrupt
 
+    ui = req.ui
     try:
         for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
             num = getattr(signal, name, None)
@@ -50,17 +57,17 @@
     try:
         try:
             # enter the debugger before command execution
-            if '--debugger' in args:
+            if '--debugger' in req.args:
                 ui.warn(_("entering debugger - "
                         "type c to continue starting hg or h for help\n"))
                 pdb.set_trace()
             try:
-                return _dispatch(ui, args)
+                return _dispatch(req)
             finally:
                 ui.flush()
         except:
             # enter the debugger when we hit an exception
-            if '--debugger' in args:
+            if '--debugger' in req.args:
                 traceback.print_exc()
                 pdb.post_mortem(sys.exc_info()[2])
             ui.traceback()
@@ -486,7 +493,10 @@
     os.chdir(cwd)
 
 _loaded = set()
-def _dispatch(ui, args):
+def _dispatch(req):
+    args = req.args
+    ui = req.ui
+
     shellaliasfn = _checkshellalias(ui, args)
     if shellaliasfn:
         return shellaliasfn()
@@ -596,7 +606,8 @@
                     repos = map(cmdutil.findrepo, args)
                     guess = repos[0]
                     if guess and repos.count(guess) == len(repos):
-                        return _dispatch(ui, ['--repository', guess] + fullargs)
+                        req.args = ['--repository', guess] + fullargs
+                        return _dispatch(req)
                 if not path:
                     raise error.RepoError(_("no repository found in %r"
                                             " (.hg not found)") % os.getcwd())
--- a/mercurial/extensions.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/extensions.py	Mon May 30 12:20:36 2011 +0200
@@ -21,13 +21,17 @@
 
 def find(name):
     '''return module with given extension name'''
+    mod = None
     try:
-        return _extensions[name]
+        mod =  _extensions[name]
     except KeyError:
         for k, v in _extensions.iteritems():
             if k.endswith('.' + name) or k.endswith('/' + name):
-                return v
+                mod = v
+                break
+    if not mod:
         raise KeyError(name)
+    return mod
 
 def loadpath(path, module_name):
     module_name = module_name.replace('.', '_')
--- a/mercurial/hg.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/hg.py	Mon May 30 12:20:36 2011 +0200
@@ -243,13 +243,15 @@
 
     src_lock = dest_lock = dir_cleanup = None
     try:
+        abspath = origsource
+        if islocal(origsource):
+            abspath = os.path.abspath(util.localpath(origsource))
+
         if islocal(dest):
             dir_cleanup = DirCleanup(dest)
 
-        abspath = origsource
         copy = False
         if src_repo.cancopy() and islocal(dest):
-            abspath = os.path.abspath(util.localpath(origsource))
             copy = not pull and not rev
 
         if copy:
--- a/mercurial/httpconnection.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/httpconnection.py	Mon May 30 12:20:36 2011 +0200
@@ -37,7 +37,7 @@
         self.write = self._data.write
         self._len = os.fstat(self._data.fileno()).st_size
         self._pos = 0
-        self._total = len(self) / 1024 * 2
+        self._total = self._len / 1024 * 2
 
     def read(self, *args, **kwargs):
         try:
--- a/mercurial/httprepo.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/httprepo.py	Mon May 30 12:20:36 2011 +0200
@@ -127,7 +127,8 @@
         if resp_url.endswith(qs):
             resp_url = resp_url[:-len(qs)]
         if self._url.rstrip('/') != resp_url.rstrip('/'):
-            self.ui.status(_('real URL is %s\n') % resp_url)
+            if not self.ui.quiet:
+                self.ui.warn(_('real URL is %s\n') % resp_url)
         self._url = resp_url
         try:
             proto = resp.getheader('content-type')
--- a/mercurial/localrepo.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/localrepo.py	Mon May 30 12:20:36 2011 +0200
@@ -1037,7 +1037,7 @@
             for f in changes[0] + changes[1]:
                 self.dirstate.normal(f)
             for f in changes[2]:
-                self.dirstate.forget(f)
+                self.dirstate.drop(f)
             self.dirstate.setparents(ret)
             ms.reset()
         finally:
--- a/mercurial/merge.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/merge.py	Mon May 30 12:20:36 2011 +0200
@@ -296,14 +296,15 @@
             if f != fd and move:
                 moves.append(f)
 
+    audit = scmutil.pathauditor(repo.root)
+
     # remove renamed files after safely stored
     for f in moves:
         if os.path.lexists(repo.wjoin(f)):
             repo.ui.debug("removing %s\n" % f)
+            audit(f)
             os.unlink(repo.wjoin(f))
 
-    audit_path = scmutil.pathauditor(repo.root)
-
     numupdates = len(action)
     for i, a in enumerate(action):
         f, m = a[:2]
@@ -313,7 +314,7 @@
             continue
         if m == "r": # remove
             repo.ui.note(_("removing %s\n") % f)
-            audit_path(f)
+            audit(f)
             if f == '.hgsubstate': # subrepo states need updating
                 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
             try:
@@ -328,6 +329,7 @@
                 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite)
                 continue
             f2, fd, flags, move = a[2:]
+            repo.wopener.audit(fd)
             r = ms.resolve(fd, wctx, mctx)
             if r is not None and r > 0:
                 unresolved += 1
@@ -340,6 +342,7 @@
             if (move and repo.dirstate.normalize(fd) != f
                 and os.path.lexists(repo.wjoin(f))):
                 repo.ui.debug("removing %s\n" % f)
+                audit(f)
                 os.unlink(repo.wjoin(f))
         elif m == "g": # get
             flags = a[2]
@@ -354,6 +357,7 @@
             f2, fd, flags = a[2:]
             if f:
                 repo.ui.note(_("moving %s to %s\n") % (f, fd))
+                audit(f)
                 t = wctx.filectx(f).data()
                 repo.wwrite(fd, t, flags)
                 util.unlinkpath(repo.wjoin(f))
@@ -370,6 +374,7 @@
                 repo.ui.warn(" %s\n" % nf)
         elif m == "e": # exec
             flags = a[2]
+            repo.wopener.audit(f)
             util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
     ms.commit()
     u.progress(_('updating'), None, total=numupdates, unit=_('files'))
@@ -385,12 +390,12 @@
             if branchmerge:
                 repo.dirstate.remove(f)
             else:
-                repo.dirstate.forget(f)
+                repo.dirstate.drop(f)
         elif m == "a": # re-add
             if not branchmerge:
                 repo.dirstate.add(f)
         elif m == "f": # forget
-            repo.dirstate.forget(f)
+            repo.dirstate.drop(f)
         elif m == "e": # exec change
             repo.dirstate.normallookup(f)
         elif m == "g": # get
@@ -420,7 +425,7 @@
                 if f2 == fd: # file not locally copied/moved
                     repo.dirstate.normallookup(fd)
                 if move:
-                    repo.dirstate.forget(f)
+                    repo.dirstate.drop(f)
         elif m == "d": # directory rename
             f2, fd, flag = a[2:]
             if not f2 and f not in repo.dirstate:
@@ -436,7 +441,7 @@
             else:
                 repo.dirstate.normal(fd)
                 if f:
-                    repo.dirstate.forget(f)
+                    repo.dirstate.drop(f)
 
 def update(repo, node, branchmerge, force, partial, ancestor=None):
     """
--- a/mercurial/minirst.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/minirst.py	Mon May 30 12:20:36 2011 +0200
@@ -467,10 +467,10 @@
         print
         return blocks
 
-    text = util.readfile(sys.argv[1])
+    text = sys.stdin.read()
     blocks = debug(findblocks, text)
     blocks = debug(findliteralblocks, blocks)
-    blocks, pruned = debug(prunecontainers, blocks, sys.argv[2:])
+    blocks, pruned = debug(prunecontainers, blocks, sys.argv[1:])
     blocks = debug(inlineliterals, blocks)
     blocks = debug(splitparagraphs, blocks)
     blocks = debug(updatefieldlists, blocks)
--- a/mercurial/patch.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/patch.py	Mon May 30 12:20:36 2011 +0200
@@ -330,11 +330,9 @@
 
 class linereader(object):
     # simple class to allow pushing lines back into the input stream
-    def __init__(self, fp, textmode=False):
+    def __init__(self, fp):
         self.fp = fp
         self.buf = []
-        self.textmode = textmode
-        self.eol = None
 
     def push(self, line):
         if line is not None:
@@ -345,15 +343,7 @@
             l = self.buf[0]
             del self.buf[0]
             return l
-        l = self.fp.readline()
-        if not self.eol:
-            if l.endswith('\r\n'):
-                self.eol = '\r\n'
-            elif l.endswith('\n'):
-                self.eol = '\n'
-        if self.textmode and l.endswith('\r\n'):
-            l = l[:-2] + '\n'
-        return l
+        return self.fp.readline()
 
     def __iter__(self):
         while 1:
@@ -366,15 +356,16 @@
     def __init__(self, ui):
         self.ui = ui
 
-    def readlines(self, fname):
-        """Return target file lines, or its content as a single line
-        for symlinks.
+    def getfile(self, fname):
+        """Return target file data and flags as a (data, (islink,
+        isexec)) tuple.
         """
         raise NotImplementedError
 
-    def writelines(self, fname, lines, mode):
-        """Write lines to target file. mode is a (islink, isexec)
-        tuple, or None if there is no mode information.
+    def setfile(self, fname, data, mode):
+        """Write data to target file fname and set its mode. mode is a
+        (islink, isexec) tuple. If data is None, the file content should
+        be left unchanged.
         """
         raise NotImplementedError
 
@@ -399,10 +390,6 @@
     def exists(self, fname):
         raise NotImplementedError
 
-    def setmode(self, fname, islink, isexec):
-        """Change target file mode."""
-        raise NotImplementedError
-
 class fsbackend(abstractbackend):
     def __init__(self, ui, basedir):
         super(fsbackend, self).__init__(ui)
@@ -411,31 +398,28 @@
     def _join(self, f):
         return os.path.join(self.opener.base, f)
 
-    def readlines(self, fname):
-        if os.path.islink(self._join(fname)):
-            return [os.readlink(self._join(fname))]
-        fp = self.opener(fname, 'r')
+    def getfile(self, fname):
+        path = self._join(fname)
+        if os.path.islink(path):
+            return (os.readlink(path), (True, False))
+        isexec, islink = False, False
         try:
-            return list(fp)
-        finally:
-            fp.close()
+            isexec = os.lstat(path).st_mode & 0100 != 0
+            islink = os.path.islink(path)
+        except OSError, e:
+            if e.errno != errno.ENOENT:
+                raise
+        return (self.opener.read(fname), (islink, isexec))
 
-    def writelines(self, fname, lines, mode):
-        if not mode:
-            # Preserve mode information
-            isexec, islink = False, False
-            try:
-                isexec = os.lstat(self._join(fname)).st_mode & 0100 != 0
-                islink = os.path.islink(self._join(fname))
-            except OSError, e:
-                if e.errno != errno.ENOENT:
-                    raise
+    def setfile(self, fname, data, mode):
+        islink, isexec = mode
+        if data is None:
+            util.setflags(self._join(fname), islink, isexec)
+            return
+        if islink:
+            self.opener.symlink(data, fname)
         else:
-            islink, isexec = mode
-        if islink:
-            self.opener.symlink(''.join(lines), fname)
-        else:
-            self.opener(fname, 'w').writelines(lines)
+            self.opener.write(fname, data)
             if isexec:
                 util.setflags(self._join(fname), False, True)
 
@@ -475,9 +459,6 @@
     def exists(self, fname):
         return os.path.lexists(self._join(fname))
 
-    def setmode(self, fname, islink, isexec):
-        util.setflags(self._join(fname), islink, isexec)
-
 class workingbackend(fsbackend):
     def __init__(self, ui, repo, similarity):
         super(workingbackend, self).__init__(ui, repo.root)
@@ -487,8 +468,8 @@
         self.changed = set()
         self.copied = []
 
-    def writelines(self, fname, lines, mode):
-        super(workingbackend, self).writelines(fname, lines, mode)
+    def setfile(self, fname, data, mode):
+        super(workingbackend, self).setfile(fname, data, mode)
         self.changed.add(fname)
 
     def unlink(self, fname):
@@ -501,10 +482,6 @@
         self.copied.append((src, dst))
         self.changed.add(dst)
 
-    def setmode(self, fname, islink, isexec):
-        super(workingbackend, self).setmode(fname, islink, isexec)
-        self.changed.add(fname)
-
     def close(self):
         wctx = self.repo[None]
         addremoved = set(self.changed)
@@ -512,7 +489,7 @@
             scmutil.dirstatecopy(self.ui, self.repo, wctx, src, dst)
             addremoved.discard(src)
         if (not self.similarity) and self.removed:
-            wctx.remove(sorted(self.removed))
+            wctx.forget(sorted(self.removed))
         if addremoved:
             cwd = self.repo.getcwd()
             if cwd:
@@ -540,7 +517,11 @@
         self.mode = mode
         if not missing:
             try:
-                self.lines = self.backend.readlines(fname)
+                data, mode = self.backend.getfile(fname)
+                if data:
+                    self.lines = data.splitlines(True)
+                if self.mode is None:
+                    self.mode = mode
                 if self.lines:
                     # Normalize line endings
                     if self.lines[0].endswith('\r\n'):
@@ -556,7 +537,8 @@
                         self.lines = nlines
                 self.exists = True
             except IOError:
-                pass
+                if self.mode is None:
+                    self.mode = (False, False)
         else:
             self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
 
@@ -585,7 +567,7 @@
                 rawlines.append(l)
             lines = rawlines
 
-        self.backend.writelines(fname, lines, mode)
+        self.backend.setfile(fname, ''.join(lines), mode)
 
     def printfile(self, warn):
         if self.fileprinted:
@@ -960,10 +942,11 @@
 
 class binhunk:
     'A binary patch file. Only understands literals so far.'
-    def __init__(self, gitpatch):
+    def __init__(self, gitpatch, lr):
         self.gitpatch = gitpatch
         self.text = None
         self.hunk = ['GIT binary patch\n']
+        self._read(lr)
 
     def createfile(self):
         return self.gitpatch.op == 'ADD'
@@ -977,7 +960,7 @@
     def new(self):
         return [self.text]
 
-    def extract(self, lr):
+    def _read(self, lr):
         line = lr.readline()
         self.hunk.append(line)
         while line and not line.startswith('literal '):
@@ -1032,7 +1015,13 @@
         count -= 1
     return path[:i].lstrip(), path[i:].rstrip()
 
-def selectfile(backend, afile_orig, bfile_orig, hunk, strip):
+def selectfile(backend, afile_orig, bfile_orig, hunk, strip, gp):
+    if gp:
+        # Git patches do not play games. Excluding copies from the
+        # following heuristic avoids a lot of confusion
+        fname = pathstrip(gp.path, strip - 1)[1]
+        missing = not hunk.createfile() and not backend.exists(fname)
+        return fname, missing
     nulla = afile_orig == "/dev/null"
     nullb = bfile_orig == "/dev/null"
     abase, afile = pathstrip(afile_orig, strip)
@@ -1098,7 +1087,7 @@
         fp = lr.fp
     except IOError:
         fp = cStringIO.StringIO(lr.fp.read())
-    gitlr = linereader(fp, lr.textmode)
+    gitlr = linereader(fp)
     gitlr.push(firstline)
     gitpatches = readgitpatch(gitlr)
     fp.seek(pos)
@@ -1112,13 +1101,12 @@
     - ("git", gitchanges): current diff is in git format, gitchanges
     maps filenames to gitpatch records. Unique event.
     """
-    changed = {}
     afile = ""
     bfile = ""
     state = None
     hunknum = 0
     emitfile = newfile = False
-    git = False
+    gitpatches = None
 
     # our states
     BFILE = 1
@@ -1129,47 +1117,47 @@
         x = lr.readline()
         if not x:
             break
-        if (state == BFILE and ((not context and x[0] == '@') or
-            ((context is not False) and x.startswith('***************')))):
-            if context is None and x.startswith('***************'):
-                context = True
-            gpatch = changed.get(bfile)
-            create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
-            remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
-            h = hunk(x, hunknum + 1, lr, context, create, remove)
-            hunknum += 1
-            if emitfile:
-                emitfile = False
-                yield 'file', (afile, bfile, h, gpatch and gpatch.mode or None)
-            yield 'hunk', h
-        elif state == BFILE and x.startswith('GIT binary patch'):
-            gpatch = changed[bfile]
-            h = binhunk(gpatch)
+        if state == BFILE and (
+            (not context and x[0] == '@')
+            or (context is not False and x.startswith('***************'))
+            or x.startswith('GIT binary patch')):
+            gp = None
+            if gitpatches and gitpatches[-1][0] == bfile:
+                gp = gitpatches.pop()[1]
+            if x.startswith('GIT binary patch'):
+                h = binhunk(gp, lr)
+            else:
+                if context is None and x.startswith('***************'):
+                    context = True
+                create = afile == '/dev/null' or gp and gp.op == 'ADD'
+                remove = bfile == '/dev/null' or gp and gp.op == 'DELETE'
+                h = hunk(x, hunknum + 1, lr, context, create, remove)
             hunknum += 1
             if emitfile:
                 emitfile = False
-                yield 'file', ('a/' + afile, 'b/' + bfile, h,
-                               gpatch and gpatch.mode or None)
-            h.extract(lr)
+                yield 'file', (afile, bfile, h, gp)
             yield 'hunk', h
         elif x.startswith('diff --git'):
-            # check for git diff, scanning the whole patch file if needed
             m = gitre.match(x)
-            if m:
-                afile, bfile = m.group(1, 2)
-                if not git:
-                    git = True
-                    gitpatches = scangitpatch(lr, x)
-                    yield 'git', gitpatches
-                    for gp in gitpatches:
-                        changed[gp.path] = gp
-                # else error?
-                # copy/rename + modify should modify target, not source
-                gp = changed.get(bfile)
-                if gp and (gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD')
-                           or gp.mode):
-                    afile = bfile
-                newfile = True
+            if not m:
+                continue
+            if gitpatches is None:
+                # scan whole input for git metadata
+                gitpatches = [('b/' + gp.path, gp) for gp
+                              in scangitpatch(lr, x)]
+                yield 'git', [g[1] for g in gitpatches
+                              if g[1].op in ('COPY', 'RENAME')]
+                gitpatches.reverse()
+            afile = 'a/' + m.group(1)
+            bfile = 'b/' + m.group(2)
+            while bfile != gitpatches[-1][0]:
+                gp = gitpatches.pop()[1]
+                yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp)
+            gp = gitpatches[-1][1]
+            # copy/rename + modify should modify target, not source
+            if gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD') or gp.mode:
+                afile = bfile
+            newfile = True
         elif x.startswith('---'):
             # check for a unified diff
             l2 = lr.readline()
@@ -1202,6 +1190,10 @@
             state = BFILE
             hunknum = 0
 
+    while gitpatches:
+        gp = gitpatches.pop()[1]
+        yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp)
+
 def applydiff(ui, fp, changed, backend, strip=1, eolmode='strict'):
     """Reads a patch from fp and tries to apply it.
 
@@ -1217,6 +1209,10 @@
                       eolmode=eolmode)
 
 def _applydiff(ui, fp, patcher, backend, changed, strip=1, eolmode='strict'):
+
+    def pstrip(p):
+        return pathstrip(p, strip - 1)[1]
+
     rejects = 0
     err = 0
     current_file = None
@@ -1233,10 +1229,29 @@
         elif state == 'file':
             if current_file:
                 rejects += current_file.close()
-            afile, bfile, first_hunk, mode = values
+                current_file = None
+            afile, bfile, first_hunk, gp = values
+            if gp:
+                path = pstrip(gp.path)
+                changed[path] = gp
+                if gp.op == 'DELETE':
+                    backend.unlink(path)
+                    continue
+                if gp.op == 'RENAME':
+                    backend.unlink(pstrip(gp.oldpath))
+                if gp.mode and not first_hunk:
+                    data = None
+                    if gp.op == 'ADD':
+                        # Added files without content have no hunk and
+                        # must be created
+                        data = ''
+                    backend.setfile(path, data, gp.mode)
+            if not first_hunk:
+                continue
             try:
+                mode = gp and gp.mode or None
                 current_file, missing = selectfile(backend, afile, bfile,
-                                                   first_hunk, strip)
+                                                   first_hunk, strip, gp)
                 current_file = patcher(ui, current_file, backend, mode,
                                        missing=missing, eolmode=eolmode)
             except PatchError, inst:
@@ -1246,105 +1261,58 @@
                 continue
         elif state == 'git':
             for gp in values:
-                gp.path = pathstrip(gp.path, strip - 1)[1]
-                if gp.oldpath:
-                    gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1]
-                if gp.op in ('COPY', 'RENAME'):
-                    backend.copy(gp.oldpath, gp.path)
-                changed[gp.path] = gp
+                backend.copy(pstrip(gp.oldpath), pstrip(gp.path))
         else:
             raise util.Abort(_('unsupported parser state: %s') % state)
 
     if current_file:
         rejects += current_file.close()
 
-    # Handle mode changes without hunk
-    removed = set()
-    for gp in changed.itervalues():
-        if not gp:
-            continue
-        if gp.op == 'DELETE':
-            removed.add(gp.path)
-            continue
-        if gp.op == 'RENAME':
-            removed.add(gp.oldpath)
-        if gp.mode:
-            if gp.op == 'ADD' and not backend.exists(gp.path):
-                # Added files without content have no hunk and must be created
-                backend.writelines(gp.path, [], gp.mode)
-            else:
-                backend.setmode(gp.path, gp.mode[0], gp.mode[1])
-    for path in sorted(removed):
-        backend.unlink(path)
-
     if rejects:
         return -1
     return err
 
-def _updatedir(ui, repo, patches, similarity=0):
-    '''Update dirstate after patch application according to metadata'''
-    if not patches:
-        return []
-    copies = []
-    removes = set()
-    cfiles = patches.keys()
-    cwd = repo.getcwd()
-    if cwd:
-        cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
-    for f in patches:
-        gp = patches[f]
-        if not gp:
-            continue
-        if gp.op == 'RENAME':
-            copies.append((gp.oldpath, gp.path))
-            removes.add(gp.oldpath)
-        elif gp.op == 'COPY':
-            copies.append((gp.oldpath, gp.path))
-        elif gp.op == 'DELETE':
-            removes.add(gp.path)
-
-    wctx = repo[None]
-    for src, dst in copies:
-        scmutil.dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
-    if (not similarity) and removes:
-        wctx.remove(sorted(removes))
-
-    scmutil.addremove(repo, cfiles, similarity=similarity)
-    files = patches.keys()
-    files.extend([r for r in removes if r not in files])
-    return sorted(files)
-
-def _externalpatch(patcher, patchname, ui, strip, cwd, files):
+def _externalpatch(ui, repo, patcher, patchname, strip, files,
+                   similarity):
     """use <patcher> to apply <patchname> to the working directory.
     returns whether patch was applied with fuzz factor."""
 
     fuzz = False
     args = []
+    cwd = repo.root
     if cwd:
         args.append('-d %s' % util.shellquote(cwd))
     fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
                                        util.shellquote(patchname)))
-
-    for line in fp:
-        line = line.rstrip()
-        ui.note(line + '\n')
-        if line.startswith('patching file '):
-            pf = util.parsepatchoutput(line)
-            printed_file = False
-            files.setdefault(pf, None)
-        elif line.find('with fuzz') >= 0:
-            fuzz = True
-            if not printed_file:
-                ui.warn(pf + '\n')
-                printed_file = True
-            ui.warn(line + '\n')
-        elif line.find('saving rejects to file') >= 0:
-            ui.warn(line + '\n')
-        elif line.find('FAILED') >= 0:
-            if not printed_file:
-                ui.warn(pf + '\n')
-                printed_file = True
-            ui.warn(line + '\n')
+    try:
+        for line in fp:
+            line = line.rstrip()
+            ui.note(line + '\n')
+            if line.startswith('patching file '):
+                pf = util.parsepatchoutput(line)
+                printed_file = False
+                files.setdefault(pf, None)
+            elif line.find('with fuzz') >= 0:
+                fuzz = True
+                if not printed_file:
+                    ui.warn(pf + '\n')
+                    printed_file = True
+                ui.warn(line + '\n')
+            elif line.find('saving rejects to file') >= 0:
+                ui.warn(line + '\n')
+            elif line.find('FAILED') >= 0:
+                if not printed_file:
+                    ui.warn(pf + '\n')
+                    printed_file = True
+                ui.warn(line + '\n')
+    finally:
+        if files:
+            cfiles = list(files)
+            cwd = repo.getcwd()
+            if cwd:
+                cfiles = [util.pathto(repo.root, cwd, f)
+                          for f in cfile]
+            scmutil.addremove(repo, cfiles, similarity=similarity)
     code = fp.close()
     if code:
         raise PatchError(_("patch command failed: %s") %
@@ -1379,7 +1347,7 @@
         raise PatchError(_('patch failed to apply'))
     return ret > 0
 
-def patch(ui, repo, patchname, strip=1, cwd=None, files=None, eolmode='strict',
+def patch(ui, repo, patchname, strip=1, files=None, eolmode='strict',
           similarity=0):
     """Apply <patchname> to the working directory.
 
@@ -1397,12 +1365,8 @@
         files = {}
     try:
         if patcher:
-            try:
-                return _externalpatch(patcher, patchname, ui, strip, cwd,
-                                      files)
-            finally:
-                touched = _updatedir(ui, repo, files, similarity)
-                files.update(dict.fromkeys(touched))
+            return _externalpatch(ui, repo, patcher, patchname, strip,
+                                  files, similarity)
         return internalpatch(ui, repo, patchname, strip, files, eolmode,
                              similarity)
     except PatchError, err:
@@ -1414,22 +1378,18 @@
     try:
         changed = set()
         for state, values in iterhunks(fp):
-            if state == 'hunk':
-                continue
-            elif state == 'file':
-                afile, bfile, first_hunk, mode = values
+            if state == 'file':
+                afile, bfile, first_hunk, gp = values
+                if gp:
+                    changed.add(pathstrip(gp.path, strip - 1)[1])
+                    if gp.op == 'RENAME':
+                        changed.add(pathstrip(gp.oldpath, strip - 1)[1])
+                if not first_hunk:
+                    continue
                 current_file, missing = selectfile(backend, afile, bfile,
-                                                   first_hunk, strip)
+                                                   first_hunk, strip, gp)
                 changed.add(current_file)
-            elif state == 'git':
-                for gp in values:
-                    gp.path = pathstrip(gp.path, strip - 1)[1]
-                    changed.add(gp.path)
-                    if gp.oldpath:
-                        gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1]
-                        if gp.op == 'RENAME':
-                            changed.add(gp.oldpath)
-            else:
+            elif state not in ('hunk', 'git'):
                 raise util.Abort(_('unsupported parser state: %s') % state)
         return changed
     finally:
@@ -1711,15 +1671,31 @@
             if text:
                 yield text
 
+def diffstatsum(stats):
+    maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False
+    for f, a, r, b in stats:
+        maxfile = max(maxfile, encoding.colwidth(f))
+        maxtotal = max(maxtotal, a + r)
+        addtotal += a
+        removetotal += r
+        binary = binary or b
+
+    return maxfile, maxtotal, addtotal, removetotal, binary
+
 def diffstatdata(lines):
     diffre = re.compile('^diff .*-r [a-z0-9]+\s(.*)$')
 
+    results = []
     filename, adds, removes = None, 0, 0
+
+    def addresult():
+        if filename:
+            isbinary = adds == 0 and removes == 0
+            results.append((filename, adds, removes, isbinary))
+
     for line in lines:
         if line.startswith('diff'):
-            if filename:
-                isbinary = adds == 0 and removes == 0
-                yield (filename, adds, removes, isbinary)
+            addresult()
             # set numbers to 0 anyway when starting new file
             adds, removes = 0, 0
             if line.startswith('diff --git'):
@@ -1731,28 +1707,13 @@
             adds += 1
         elif line.startswith('-') and not line.startswith('---'):
             removes += 1
-    if filename:
-        isbinary = adds == 0 and removes == 0
-        yield (filename, adds, removes, isbinary)
+    addresult()
+    return results
 
 def diffstat(lines, width=80, git=False):
     output = []
-    stats = list(diffstatdata(lines))
-
-    maxtotal, maxname = 0, 0
-    totaladds, totalremoves = 0, 0
-    hasbinary = False
-
-    sized = [(filename, adds, removes, isbinary, encoding.colwidth(filename))
-             for filename, adds, removes, isbinary in stats]
-
-    for filename, adds, removes, isbinary, namewidth in sized:
-        totaladds += adds
-        totalremoves += removes
-        maxname = max(maxname, namewidth)
-        maxtotal = max(maxtotal, adds + removes)
-        if isbinary:
-            hasbinary = True
+    stats = diffstatdata(lines)
+    maxname, maxtotal, totaladds, totalremoves, hasbinary = diffstatsum(stats)
 
     countwidth = len(str(maxtotal))
     if hasbinary and countwidth < 3:
@@ -1769,7 +1730,7 @@
         # if there were at least some changes.
         return max(i * graphwidth // maxtotal, int(bool(i)))
 
-    for filename, adds, removes, isbinary, namewidth in sized:
+    for filename, adds, removes, isbinary in stats:
         if git and isbinary:
             count = 'Bin'
         else:
@@ -1777,9 +1738,8 @@
         pluses = '+' * scale(adds)
         minuses = '-' * scale(removes)
         output.append(' %s%s |  %*s %s%s\n' %
-                      (filename, ' ' * (maxname - namewidth),
-                       countwidth, count,
-                       pluses, minuses))
+                      (filename, ' ' * (maxname - encoding.colwidth(filename)),
+                       countwidth, count, pluses, minuses))
 
     if stats:
         output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n')
--- a/mercurial/pure/osutil.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/pure/osutil.py	Mon May 30 12:20:36 2011 +0200
@@ -8,8 +8,6 @@
 import os
 import stat as statmod
 
-posixfile = open
-
 def _mode_to_kind(mode):
     if statmod.S_ISREG(mode):
         return statmod.S_IFREG
@@ -57,3 +55,131 @@
             result.append((fn, _mode_to_kind(st.st_mode)))
     return result
 
+if os.name != 'nt':
+    posixfile = open
+else:
+    import ctypes, ctypes.util
+
+    _kernel32 = ctypes.windll.kernel32
+
+    _DWORD = ctypes.c_ulong
+    _LPCSTR = _LPSTR = ctypes.c_char_p
+    _HANDLE = ctypes.c_void_p
+
+    _INVALID_HANDLE_VALUE = _HANDLE(-1).value
+
+    def _crtname():
+        try:
+            # find_msvcrt was introduced in Python 2.6
+            return ctypes.util.find_msvcrt()
+        except AttributeError:
+            return 'msvcr80.dll' # CPython 2.5
+
+    _crt = ctypes.PyDLL(_crtname())
+
+    # CreateFile 
+    _FILE_SHARE_READ = 0x00000001
+    _FILE_SHARE_WRITE = 0x00000002
+    _FILE_SHARE_DELETE = 0x00000004
+
+    _CREATE_ALWAYS = 2
+    _OPEN_EXISTING = 3
+    _OPEN_ALWAYS = 4
+
+    _GENERIC_READ = 0x80000000
+    _GENERIC_WRITE = 0x40000000
+
+    _FILE_ATTRIBUTE_NORMAL = 0x80
+
+    # _open_osfhandle
+    _O_RDONLY = 0x0000
+    _O_RDWR = 0x0002
+    _O_APPEND = 0x0008
+
+    _O_TEXT = 0x4000
+    _O_BINARY = 0x8000
+
+    # types of parameters of C functions used (required by pypy)
+
+    _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
+        _DWORD, _DWORD, _HANDLE]
+    _kernel32.CreateFileA.restype = _HANDLE
+
+    _crt._open_osfhandle.argtypes = [_HANDLE, ctypes.c_int]
+    _crt._open_osfhandle.restype = ctypes.c_int
+
+    def _raiseioerror(name):
+        err = ctypes.WinError()
+        raise IOError(err.errno, '%s: %s' % (name, err.strerror))
+
+    class posixfile(object):
+        '''a file object aiming for POSIX-like semantics
+
+        CPython's open() returns a file that was opened *without* setting the
+        _FILE_SHARE_DELETE flag, which causes rename and unlink to abort.
+        This even happens if any hardlinked copy of the file is in open state.
+        We set _FILE_SHARE_DELETE here, so files opened with posixfile can be
+        renamed and deleted while they are held open.
+        Note that if a file opened with posixfile is unlinked, the file
+        remains but cannot be opened again or be recreated under the same name,
+        until all reading processes have closed the file.'''
+
+        def __init__(self, name, mode='r', bufsize=-1):
+            if 'b' in mode:
+                flags = _O_BINARY
+            else:
+                flags = _O_TEXT
+
+            m0 = mode[0]
+            if m0 == 'r' and not '+' in mode:
+                flags |= _O_RDONLY
+                access = _GENERIC_READ
+            else:
+                # work around http://support.microsoft.com/kb/899149 and
+                # set _O_RDWR for 'w' and 'a', even if mode has no '+'
+                flags |= _O_RDWR
+                access = _GENERIC_READ | _GENERIC_WRITE
+
+            if m0 == 'r':
+                creation = _OPEN_EXISTING
+            elif m0 == 'w':
+                creation = _CREATE_ALWAYS
+            elif m0 == 'a':
+                creation = _OPEN_ALWAYS
+                flags |= _O_APPEND
+            else:
+                raise ValueError("invalid mode: %s" % mode)
+
+            fh = _kernel32.CreateFileA(name, access,
+                    _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
+                    None, creation, _FILE_ATTRIBUTE_NORMAL, None)
+            if fh == _INVALID_HANDLE_VALUE:
+                _raiseioerror(name)
+
+            # for CPython we must use the same CRT as Python uses,
+            # or the os.fdopen call below will abort with
+            #   "OSError: [Errno 9] Bad file descriptor"
+            fd = _crt._open_osfhandle(fh, flags)
+            if fd == -1:
+                _kernel32.CloseHandle(fh)
+                _raiseioerror(name)
+
+            f = os.fdopen(fd, mode, bufsize)
+            # unfortunately, f.name is '<fdopen>' at this point -- so we store
+            # the name on this wrapper. We cannot just assign to f.name,
+            # because that attribute is read-only.
+            object.__setattr__(self, 'name', name)
+            object.__setattr__(self, '_file', f)
+
+        def __iter__(self):
+            return self._file
+
+        def __getattr__(self, name):
+            return getattr(self._file, name)
+
+        def __setattr__(self, name, value):
+            '''mimics the read-only attributes of Python file objects
+            by raising 'TypeError: readonly attribute' if someone tries:
+              f = posixfile('foo.txt')
+              f.name = 'bla'  '''
+            return self._file.__setattr__(name, value)
--- a/mercurial/pure/parsers.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/pure/parsers.py	Mon May 30 12:20:36 2011 +0200
@@ -56,6 +56,9 @@
             n += 1
             off += s
 
+    if off != len(data):
+        raise ValueError('corrupt index file')
+
     if index:
         e = list(index[0])
         type = gettype(e[0])
--- a/mercurial/revlog.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/revlog.py	Mon May 30 12:20:36 2011 +0200
@@ -12,7 +12,7 @@
 """
 
 # import stuff from node for others to import from revlog
-from node import bin, hex, nullid, nullrev, short #@UnusedImport
+from node import bin, hex, nullid, nullrev
 from i18n import _
 import ancestor, mdiff, parsers, error, util, dagutil
 import struct, zlib, errno
--- a/mercurial/scmutil.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/scmutil.py	Mon May 30 12:20:36 2011 +0200
@@ -256,6 +256,9 @@
             f.close()
             self._fixfilemode(dst)
 
+    def audit(self, path):
+        self.auditor(path)
+
 class filteropener(abstractopener):
     '''Wrapper opener for filtering filenames with a function.'''
 
@@ -620,7 +623,7 @@
         wctx = repo[None]
         wlock = repo.wlock()
         try:
-            wctx.remove(deleted)
+            wctx.forget(deleted)
             wctx.add(unknown)
             for new, old in copies.iteritems():
                 wctx.copy(old, new)
--- a/mercurial/subrepo.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/subrepo.py	Mon May 30 12:20:36 2011 +0200
@@ -79,9 +79,8 @@
 
 def writestate(repo, state):
     """rewrite .hgsubstate in (outer) repo with these subrepo states"""
-    repo.wwrite('.hgsubstate',
-                ''.join(['%s %s\n' % (state[s][1], s)
-                         for s in sorted(state)]), '')
+    lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)]
+    repo.wwrite('.hgsubstate', ''.join(lines), '')
 
 def submerge(repo, wctx, mctx, actx, overwrite):
     """delegated from merge.applyupdates: merging of .hgsubstate file
@@ -137,6 +136,10 @@
         elif ld == a: # remote removed, local unchanged
             debug(s, "remote removed, remove")
             wctx.sub(s).remove()
+        elif a == nullstate: # not present in remote or ancestor
+            debug(s, "local added, keep")
+            sm[s] = l
+            continue
         else:
             if repo.ui.promptchoice(
                 _(' local changed subrepository %s which remote removed\n'
@@ -757,6 +760,9 @@
         base = self._gitcommand(['merge-base', r1, r2])
         return base == r1
 
+    def _gitisbare(self):
+        return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
+
     def _gitbranchmap(self):
         '''returns 2 things:
         a map from git branch to revision
@@ -819,6 +825,8 @@
     def dirty(self, ignoreupdate=False):
         if self._gitmissing():
             return True
+        if self._gitisbare():
+            return True
         if not ignoreupdate and self._state[1] != self._gitstate():
             # different version checked out
             return True
@@ -830,7 +838,7 @@
         source, revision, kind = state
         self._fetch(source, revision)
         # if the repo was set to be bare, unbare it
-        if self._gitcommand(['config', '--bool', 'core.bare']) == 'true':
+        if self._gitisbare():
             self._gitcommand(['config', 'core.bare', 'false'])
             if self._gitstate() == revision:
                 self._gitcommand(['reset', '--hard', 'HEAD'])
--- a/mercurial/templatekw.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/templatekw.py	Mon May 30 12:20:36 2011 +0200
@@ -185,12 +185,9 @@
     """:diffstat: String. Statistics of changes with the following format:
     "modified files: +added/-removed lines"
     """
-    files, adds, removes = 0, 0, 0
-    for i in patch.diffstatdata(util.iterlines(ctx.diff())):
-        files += 1
-        adds += i[1]
-        removes += i[2]
-    return '%s: +%s/-%s' % (files, adds, removes)
+    stats = patch.diffstatdata(util.iterlines(ctx.diff()))
+    maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
+    return '%s: +%s/-%s' % (len(stats), adds, removes)
 
 def showextras(**args):
     templ = args['templ']
--- a/mercurial/templates/static/style-coal.css	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/templates/static/style-coal.css	Mon May 30 12:20:36 2011 +0200
@@ -221,6 +221,8 @@
   border-left: 3px solid #999;
   margin: 1em 0 1em 0;
   padding: .3em;
+  white-space: pre;
+  font-family: monospace;
 }
 
 /* Graph */
--- a/mercurial/wireproto.py	Mon May 30 11:52:17 2011 +0200
+++ b/mercurial/wireproto.py	Mon May 30 12:20:36 2011 +0200
@@ -139,7 +139,7 @@
         remote server as a bundle. Return an integer indicating the
         result of the push (see localrepository.addchangegroup()).'''
 
-        if self.capable('unbundlehash'):
+        if heads != ['force'] and self.capable('unbundlehash'):
             heads = encodelist(['hashed',
                                 util.sha1(''.join(sorted(heads))).digest()])
         else:
@@ -288,7 +288,7 @@
         success = 0
     return "%s %s\n" % (success, r)
 
-def known(repo, proto, nodes):
+def known(repo, proto, nodes, others):
     return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
 
 def pushkey(repo, proto, namespace, key, old, new):
@@ -412,7 +412,7 @@
     'getbundle': (getbundle, '*'),
     'heads': (heads, ''),
     'hello': (hello, ''),
-    'known': (known, 'nodes'),
+    'known': (known, 'nodes *'),
     'listkeys': (listkeys, 'namespace'),
     'lookup': (lookup, 'key'),
     'pushkey': (pushkey, 'namespace key old new'),
--- a/tests/hghave	Mon May 30 11:52:17 2011 +0200
+++ b/tests/hghave	Mon May 30 12:20:36 2011 +0200
@@ -212,7 +212,7 @@
     "icasefs": (has_icasefs, "case insensitive file system"),
     "inotify": (has_inotify, "inotify extension support"),
     "lsprof": (has_lsprof, "python lsprof module"),
-    "mtn": (has_mtn, "monotone client (> 0.31)"),
+    "mtn": (has_mtn, "monotone client (>= 1.0)"),
     "outer-repo": (has_outer_repo, "outer repo"),
     "p4": (has_p4, "Perforce server and client"),
     "pyflakes": (has_pyflakes, "Pyflakes python linter"),
--- a/tests/notcapable	Mon May 30 11:52:17 2011 +0200
+++ b/tests/notcapable	Mon May 30 12:20:36 2011 +0200
@@ -10,7 +10,7 @@
 def extsetup():
     extensions.wrapfunction(repo.repository, 'capable', wrapper)
 def wrapper(orig, self, name, *args, **kwargs):
-    if name == '$CAP':
+    if name in '$CAP'.split(' '):
         return False
     return orig(self, name, *args, **kwargs)
 EOF
--- a/tests/run-tests.py	Mon May 30 11:52:17 2011 +0200
+++ b/tests/run-tests.py	Mon May 30 12:20:36 2011 +0200
@@ -733,11 +733,9 @@
     else:
         return None # not a supported test, don't record
 
-    if options.blacklist:
-        filename = options.blacklist.get(test)
-        if filename is not None:
-            skip("blacklisted")
-            return None
+    if options.blacklist and filename in options.blacklist:
+        skip("blacklisted")
+        return None
 
     if options.retest and not os.path.exists(test + ".err"):
         ignore("not retesting")
@@ -921,7 +919,11 @@
 
     optcopy = dict(options.__dict__)
     optcopy['jobs'] = 1
+
+    blacklist = optcopy['blacklist'] or []
     del optcopy['blacklist']
+    blacklisted = []
+
     if optcopy['with_hg'] is None:
         optcopy['with_hg'] = os.path.join(BINDIR, "hg")
     optcopy.pop('anycoverage', None)
@@ -943,7 +945,11 @@
         for job in jobs:
             if not tests:
                 break
-            job.append(tests.pop())
+            test = tests.pop()
+            if test in blacklist:
+                blacklisted.append(test)
+            else:
+                job.append(test)
     fps = {}
 
     for j, job in enumerate(jobs):
@@ -981,9 +987,12 @@
         vlog('pid %d exited, status %d' % (pid, status))
         failures |= status
     print
+    skipped += len(blacklisted)
     if not options.noskips:
         for s in skips:
             print "Skipped %s: %s" % (s[0], s[1])
+        for s in blacklisted:
+            print "Skipped %s: blacklisted" % s
     for s in fails:
         print "Failed %s: %s" % (s[0], s[1])
 
--- a/tests/test-check-pyflakes.t	Mon May 30 11:52:17 2011 +0200
+++ b/tests/test-check-pyflakes.t	Mon May 30 12:20:36 2011 +0200
@@ -6,7 +6,6 @@
   mercurial/commands.py:*: 'bdiff' imported but unused (glob)
   mercurial/commands.py:*: 'mpatch' imported but unused (glob)
   mercurial/commands.py:*: 'osutil' imported but unused (glob)
-  mercurial/revlog.py:*: 'short' imported but unused (glob)
   hgext/inotify/linux/__init__.py:*: 'from _inotify import *' used; unable to detect undefined names (glob)
   mercurial/util.py:*: 'from posix import *' used; unable to detect undefined names (glob)
   mercurial/windows.py:*: 'from win32 import *' used; unable to detect undefined names (glob)
--- a/tests/test-context.py	Mon May 30 11:52:17 2011 +0200
+++ b/tests/test-context.py	Mon May 30 12:20:36 2011 +0200
@@ -1,5 +1,5 @@
 import os
-from mercurial import hg, ui
+from mercurial import hg, ui, context, encoding
 
 u = ui.ui()
 
@@ -17,3 +17,16 @@
 repo.commit(text='commit1', date="0 0")
 
 print "workingfilectx.date =", repo[None]['foo'].date()
+
+# test memctx with non-ASCII commit message
+
+def filectxfn(repo, memctx, path):
+    return context.memfilectx("foo", "")
+
+ctx = context.memctx(repo, ['tip', None],
+                     encoding.tolocal("Gr\xc3\xbcezi!"),
+                     ["foo"], filectxfn)
+ctx.commit()
+for enc in "ASCII", "Latin-1", "UTF-8":
+    encoding.encoding = enc
+    print "%-8s: %s" % (enc, repo["tip"].description())
--- a/tests/test-context.py.out	Mon May 30 11:52:17 2011 +0200
+++ b/tests/test-context.py.out	Mon May 30 12:20:36 2011 +0200
@@ -1,1 +1,4 @@
 workingfilectx.date = (1000, 0)
+ASCII   : Gr?ezi!
+Latin-1 : Grüezi!
+UTF-8   : Grüezi!
--- a/tests/test-convert-mtn.t	Mon May 30 11:52:17 2011 +0200
+++ b/tests/test-convert-mtn.t	Mon May 30 12:20:36 2011 +0200
@@ -39,12 +39,12 @@
   $ python -c 'file("bin", "wb").write("a\\x00b")'
   $ echo c > c
   $ mtn add a dir/b dir/d c bin
-  mtn: adding a to workspace manifest
-  mtn: adding bin to workspace manifest
-  mtn: adding c to workspace manifest
-  mtn: adding dir to workspace manifest
-  mtn: adding dir/b to workspace manifest
-  mtn: adding dir/d to workspace manifest
+  mtn: adding 'a' to workspace manifest
+  mtn: adding 'bin' to workspace manifest
+  mtn: adding 'c' to workspace manifest
+  mtn: adding 'dir' to workspace manifest
+  mtn: adding 'dir/b' to workspace manifest
+  mtn: adding 'dir/d' to workspace manifest
   $ mtn ci -m initialize
   mtn: beginning commit on branch 'com.selenic.test'
   mtn: committed revision 0f6e5e4f2e7d2a8ef312408f57618abf026afd90
@@ -52,12 +52,12 @@
 update monotone working directory
 
   $ mtn mv a dir/a
-  mtn: skipping dir, already accounted for in workspace
-  mtn: renaming a to dir/a in workspace manifest
+  mtn: skipping 'dir', already accounted for in workspace
+  mtn: renaming 'a' to 'dir/a' in workspace manifest
   $ echo a >> dir/a
   $ echo b >> dir/b
   $ mtn drop c
-  mtn: dropping c from workspace manifest
+  mtn: dropping 'c' from workspace manifest
   $ python -c 'file("bin", "wb").write("b\\x00c")'
   $ mtn ci -m update1
   mtn: beginning commit on branch 'com.selenic.test'
@@ -77,11 +77,11 @@
   $ cd workingdir
   $ echo e > e
   $ mtn add e
-  mtn: adding e to workspace manifest
+  mtn: adding 'e' to workspace manifest
   $ mtn drop dir/b
-  mtn: dropping dir/b from workspace manifest
+  mtn: dropping 'dir/b' from workspace manifest
   $ mtn mv bin bin2
-  mtn: renaming bin to bin2 in workspace manifest
+  mtn: renaming 'bin' to 'bin2' in workspace manifest
   $ mtn ci -m 'update2 "with" quotes'
   mtn: beginning commit on branch 'com.selenic.test'
   mtn: committed revision ebe58335d85d8cb176b6d0a12be04f5314b998da
@@ -93,17 +93,17 @@
   $ echo file1 > dir1/subdir1/file1
   $ echo file2 > dir1/subdir2_other/file1
   $ mtn add dir1/subdir1/file1 dir1/subdir2_other/file1
-  mtn: adding dir1 to workspace manifest
-  mtn: adding dir1/subdir1 to workspace manifest
-  mtn: adding dir1/subdir1/file1 to workspace manifest
-  mtn: adding dir1/subdir2_other to workspace manifest
-  mtn: adding dir1/subdir2_other/file1 to workspace manifest
+  mtn: adding 'dir1' to workspace manifest
+  mtn: adding 'dir1/subdir1' to workspace manifest
+  mtn: adding 'dir1/subdir1/file1' to workspace manifest
+  mtn: adding 'dir1/subdir2_other' to workspace manifest
+  mtn: adding 'dir1/subdir2_other/file1' to workspace manifest
   $ mtn ci -m createdir1
   mtn: beginning commit on branch 'com.selenic.test'
   mtn: committed revision a8d62bc04fee4d2936d28e98bbcc81686dd74306
   $ mtn rename dir1/subdir1 dir1/subdir2
-  mtn: skipping dir1, already accounted for in workspace
-  mtn: renaming dir1/subdir1 to dir1/subdir2 in workspace manifest
+  mtn: skipping 'dir1', already accounted for in workspace
+  mtn: renaming 'dir1/subdir1' to 'dir1/subdir2' in workspace manifest
   $ mtn ci -m movedir1
   mtn: beginning commit on branch 'com.selenic.test'
   mtn: committed revision 2c3d241bbbfe538b1b51d910f5676407e3f4d3a6
@@ -111,12 +111,12 @@
 test subdirectory move
 
   $ mtn mv dir dir2
-  mtn: renaming dir to dir2 in workspace manifest
+  mtn: renaming 'dir' to 'dir2' in workspace manifest
   $ echo newfile > dir2/newfile
   $ mtn drop dir2/d
-  mtn: dropping dir2/d from workspace manifest
+  mtn: dropping 'dir2/d' from workspace manifest
   $ mtn add dir2/newfile
-  mtn: adding dir2/newfile to workspace manifest
+  mtn: adding 'dir2/newfile' to workspace manifest
   $ mtn ci -m movedir
   mtn: beginning commit on branch 'com.selenic.test'
   mtn: committed revision fdb5a02dae8bfce3a79b3393680af471016e1b4c
@@ -132,10 +132,10 @@
   mtn: beginning commit on branch 'com.selenic.test'
   mtn: committed revision 8bbf76d717001d24964e4604739fdcd0f539fc88
   $ mtn drop -R dir2/dir
-  mtn: dropping dir2/dir/subdir/f from workspace manifest
-  mtn: dropping dir2/dir/subdir from workspace manifest
-  mtn: dropping dir2/dir/emptydir from workspace manifest
-  mtn: dropping dir2/dir from workspace manifest
+  mtn: dropping 'dir2/dir/subdir/f' from workspace manifest
+  mtn: dropping 'dir2/dir/subdir' from workspace manifest
+  mtn: dropping 'dir2/dir/emptydir' from workspace manifest
+  mtn: dropping 'dir2/dir' from workspace manifest
   $ mtn ci -m dropdirectory
   mtn: beginning commit on branch 'com.selenic.test'
   mtn: committed revision 2323d4bc324e6c82628dc04d47a9fd32ad24e322
@@ -145,18 +145,18 @@
   $ mkdir -p dir3/d1
   $ echo a > dir3/a
   $ mtn add dir3/a dir3/d1
-  mtn: adding dir3 to workspace manifest
-  mtn: adding dir3/a to workspace manifest
-  mtn: adding dir3/d1 to workspace manifest
+  mtn: adding 'dir3' to workspace manifest
+  mtn: adding 'dir3/a' to workspace manifest
+  mtn: adding 'dir3/d1' to workspace manifest
   $ mtn ci -m dirfilemove
   mtn: beginning commit on branch 'com.selenic.test'
   mtn: committed revision 47b192f720faa622f48c68d1eb075b26d405aa8b
   $ mtn mv dir3/a dir3/d1/a
-  mtn: skipping dir3/d1, already accounted for in workspace
-  mtn: renaming dir3/a to dir3/d1/a in workspace manifest
+  mtn: skipping 'dir3/d1', already accounted for in workspace
+  mtn: renaming 'dir3/a' to 'dir3/d1/a' in workspace manifest
   $ mtn mv dir3/d1 dir3/d2
-  mtn: skipping dir3, already accounted for in workspace
-  mtn: renaming dir3/d1 to dir3/d2 in workspace manifest
+  mtn: skipping 'dir3', already accounted for in workspace
+  mtn: renaming 'dir3/d1' to 'dir3/d2' in workspace manifest
   $ mtn ci -m dirfilemove2
   mtn: beginning commit on branch 'com.selenic.test'
   mtn: committed revision 8b543a400d3ee7f6d4bb1835b9b9e3747c8cb632
@@ -167,17 +167,17 @@
   $ mkdir dir5
   $ echo a > dir4/a
   $ mtn add dir4/a dir5
-  mtn: adding dir4 to workspace manifest
-  mtn: adding dir4/a to workspace manifest
-  mtn: adding dir5 to workspace manifest
+  mtn: adding 'dir4' to workspace manifest
+  mtn: adding 'dir4/a' to workspace manifest
+  mtn: adding 'dir5' to workspace manifest
   $ mtn ci -m dirdirmove
   mtn: beginning commit on branch 'com.selenic.test'
   mtn: committed revision 466e0b2afc7a55aa2b4ab2f57cb240bb6cd66fc7
   $ mtn mv dir5 dir6
-  mtn: renaming dir5 to dir6 in workspace manifest
+  mtn: renaming 'dir5' to 'dir6' in workspace manifest
   $ mtn mv dir4 dir6/dir4
-  mtn: skipping dir6, already accounted for in workspace
-  mtn: renaming dir4 to dir6/dir4 in workspace manifest
+  mtn: skipping 'dir6', already accounted for in workspace
+  mtn: renaming 'dir4' to 'dir6/dir4' in workspace manifest
   $ mtn ci -m dirdirmove2
   mtn: beginning commit on branch 'com.selenic.test'
   mtn: committed revision 3d1f77ebad0c23a5d14911be3b670f990991b749
@@ -189,21 +189,21 @@
   $ echo b > dir7/dir9/b
   $ echo c > dir7/c
   $ mtn add -R dir7
-  mtn: adding dir7 to workspace manifest
-  mtn: adding dir7/c to workspace manifest
-  mtn: adding dir7/dir9 to workspace manifest
-  mtn: adding dir7/dir9/b to workspace manifest
-  mtn: adding dir7/dir9/dir8 to workspace manifest
-  mtn: adding dir7/dir9/dir8/a to workspace manifest
+  mtn: adding 'dir7' to workspace manifest
+  mtn: adding 'dir7/c' to workspace manifest
+  mtn: adding 'dir7/dir9' to workspace manifest
+  mtn: adding 'dir7/dir9/b' to workspace manifest
+  mtn: adding 'dir7/dir9/dir8' to workspace manifest
+  mtn: adding 'dir7/dir9/dir8/a' to workspace manifest
   $ mtn ci -m divergentdirmove
   mtn: beginning commit on branch 'com.selenic.test'
   mtn: committed revision 08a08511f18b428d840199b062de90d0396bc2ed
   $ mtn mv dir7 dir7-2
-  mtn: renaming dir7 to dir7-2 in workspace manifest
+  mtn: renaming 'dir7' to 'dir7-2' in workspace manifest
   $ mtn mv dir7-2/dir9 dir9-2
-  mtn: renaming dir7-2/dir9 to dir9-2 in workspace manifest
+  mtn: renaming 'dir7-2/dir9' to 'dir9-2' in workspace manifest
   $ mtn mv dir9-2/dir8 dir8-2
-  mtn: renaming dir9-2/dir8 to dir8-2 in workspace manifest
+  mtn: renaming 'dir9-2/dir8' to 'dir8-2' in workspace manifest
   $ mtn ci -m divergentdirmove2
   mtn: beginning commit on branch 'com.selenic.test'
   mtn: committed revision 4a736634505795f17786fffdf2c9cbf5b11df6f6
@@ -214,7 +214,7 @@
   $ $TESTDIR/md5sum.py large-file
   5d6de8a95c3b6bf9e0ffb808ba5299c1  large-file
   $ mtn add large-file
-  mtn: adding large-file to workspace manifest
+  mtn: adding 'large-file' to workspace manifest
   $ mtn ci -m largefile
   mtn: beginning commit on branch 'com.selenic.test'
   mtn: committed revision f0a20fecd10dc4392d18fe69a03f1f4919d3387b
--- a/tests/test-debugcomplete.t	Mon May 30 11:52:17 2011 +0200
+++ b/tests/test-debugcomplete.t	Mon May 30 12:20:36 2011 +0200
@@ -246,7 +246,7 @@
   import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity
   incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
   locate: rev, print0, fullpath, include, exclude
-  manifest: rev
+  manifest: rev, all
   outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
   parents: rev, style, template
   paths: 
--- a/tests/test-diffstat.t	Mon May 30 11:52:17 2011 +0200
+++ b/tests/test-diffstat.t	Mon May 30 12:20:36 2011 +0200
@@ -2,18 +2,22 @@
   $ cd repo
   $ i=0; while [ "$i" -lt 213 ]; do echo a >> a; i=`expr $i + 1`; done
   $ hg add a
+  $ cp a b
+  $ hg add b
 
 Wide diffstat:
 
   $ hg diff --stat
    a |  213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-   1 files changed, 213 insertions(+), 0 deletions(-)
+   b |  213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+   2 files changed, 426 insertions(+), 0 deletions(-)
 
 diffstat width:
 
   $ COLUMNS=24 hg diff --config ui.interactive=true --stat
    a |  213 ++++++++++++++
-   1 files changed, 213 insertions(+), 0 deletions(-)
+   b |  213 ++++++++++++++
+   2 files changed, 426 insertions(+), 0 deletions(-)
 
   $ hg ci -m adda
 
@@ -31,19 +35,19 @@
 
   $ hg ci -m appenda
 
-  $ printf '\0' > b
-  $ hg add b
+  $ printf '\0' > c
+  $ hg add c
 
 Binary diffstat:
 
   $ hg diff --stat
-   b |    0 
+   c |    0 
    1 files changed, 0 insertions(+), 0 deletions(-)
 
 Binary git diffstat:
 
   $ hg diff --stat --git
-   b |  Bin 
+   c |  Bin 
    1 files changed, 0 insertions(+), 0 deletions(-)
 
   $ hg ci -m createb
--- a/tests/test-dispatch.py	Mon May 30 11:52:17 2011 +0200
+++ b/tests/test-dispatch.py	Mon May 30 12:20:36 2011 +0200
@@ -7,7 +7,8 @@
     Prints command and result value, but does not handle quoting.
     """
     print "running: %s" % (cmd,)
-    result = dispatch.dispatch(cmd.split())
+    req = dispatch.request(cmd.split())
+    result = dispatch.dispatch(req)
     print "result: %r" % (result,)
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-duplicateoptions.py	Mon May 30 12:20:36 2011 +0200
@@ -0,0 +1,30 @@
+import os
+from mercurial import ui, commands, extensions
+
+ignore = set(['highlight', 'win32text'])
+
+if os.name != 'nt':
+    ignore.add('win32mbcs')
+
+disabled = [ext for ext in extensions.disabled().keys() if ext not in ignore]
+
+hgrc = open(os.environ["HGRCPATH"], 'w')
+hgrc.write('[extensions]\n')
+
+for ext in disabled:
+    hgrc.write(ext + '=\n')
+
+hgrc.close()
+
+u = ui.ui()
+extensions.loadall(u)
+
+for cmd, entry in commands.table.iteritems():
+    seenshort = set()
+    seenlong = set()
+    for option in entry[1]:
+        if (option[0] and option[0] in seenshort) or \
+           (option[1] and option[1] in seenlong):
+            print "command '" + cmd + "' has duplicate option " + str(option)
+        seenshort.add(option[0])
+        seenlong.add(option[1])
--- a/tests/test-git-import.t	Mon May 30 11:52:17 2011 +0200
+++ b/tests/test-git-import.t	Mon May 30 12:20:36 2011 +0200
@@ -383,3 +383,61 @@
   a   0         -1 unset               b
   $ hg ci -m done
   $ cd ..
+
+Renames and strip
+
+  $ hg init renameandstrip
+  $ cd renameandstrip
+  $ echo a > a
+  $ hg ci -Am adda
+  adding a
+  $ hg import --no-commit -p2 - <<EOF
+  > diff --git a/foo/a b/foo/b
+  > rename from foo/a
+  > rename to foo/b
+  > EOF
+  applying patch from stdin
+  $ hg st --copies
+  A b
+    a
+  R a
+  $ cd ..
+
+Pure copy with existing destination
+
+  $ hg init copytoexisting
+  $ cd copytoexisting
+  $ echo a > a
+  $ echo b > b
+  $ hg ci -Am add
+  adding a
+  adding b
+  $ hg import --no-commit - <<EOF
+  > diff --git a/a b/b
+  > copy from a
+  > copy to b
+  > EOF
+  applying patch from stdin
+  abort: cannot create b: destination already exists
+  [255]
+  $ cat b
+  b
+
+Copy and changes with existing destination
+
+  $ hg import --no-commit - <<EOF
+  > diff --git a/a b/b
+  > copy from a
+  > copy to b
+  > --- a/a
+  > +++ b/b
+  > @@ -1,1 +1,2 @@
+  > a
+  > +b
+  > EOF
+  applying patch from stdin
+  abort: cannot create b: destination already exists
+  [255]
+  $ cat b
+  b
+  $ cd ..
--- a/tests/test-manifest.t	Mon May 30 11:52:17 2011 +0200
+++ b/tests/test-manifest.t	Mon May 30 12:20:36 2011 +0200
@@ -53,6 +53,10 @@
   b/a
   l
 
+  $ hg manifest --all
+  a
+  b/a
+  l
 
 The next two calls are expected to abort:
 
--- a/tests/test-mq-qimport.t	Mon May 30 11:52:17 2011 +0200
+++ b/tests/test-mq-qimport.t	Mon May 30 12:20:36 2011 +0200
@@ -202,3 +202,26 @@
   $ hg qser
   this-name-is-better
   url.diff
+
+qimport with bad name, should abort before reading file
+
+  $ hg qimport non-existant-file --name .hg
+  abort: patch name cannot begin with ".hg"
+  [255]
+
+qimport http:// patch with leading slashes in url
+
+set up hgweb
+
+  $ cd ..
+  $ hg init served
+  $ cd served
+  $ echo a > a
+  $ hg ci -Am patch
+  adding a
+  $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
+  $ cat hg.pid >> $DAEMON_PIDS
+
+  $ cd ../repo
+  $ hg qimport http://localhost:$HGPORT/raw-rev/0///
+  adding 0 to series file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qrefresh-interactive.t	Mon May 30 12:20:36 2011 +0200
@@ -0,0 +1,348 @@
+Create configuration
+
+  $ echo "[ui]" >> $HGRCPATH
+  $ echo "interactive=true" >> $HGRCPATH
+
+help qrefresh (no record)
+
+  $ echo "[extensions]" >> $HGRCPATH
+  $ echo "mq=" >> $HGRCPATH
+  $ hg help qrefresh
+  hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...
+  
+  update the current patch
+  
+      If any file patterns are provided, the refreshed patch will contain only
+      the modifications that match those patterns; the remaining modifications
+      will remain in the working directory.
+  
+      If -s/--short is specified, files currently included in the patch will be
+      refreshed just like matched files and remain in the patch.
+  
+      If -e/--edit is specified, Mercurial will start your configured editor for
+      you to enter a message. In case qrefresh fails, you will find a backup of
+      your message in ".hg/last-message.txt".
+  
+      hg add/remove/copy/rename work as usual, though you might want to use git-
+      style patches (-g/--git or [diff] git=1) to track copies and renames. See
+      the diffs help topic for more information on the git diff format.
+  
+      Returns 0 on success.
+  
+  options:
+  
+   -e --edit                 edit commit message
+   -g --git                  use git extended diff format
+   -s --short                refresh only files already in the patch and
+                             specified files
+   -U --currentuser          add/update author field in patch with current user
+   -u --user USER            add/update author field in patch with given user
+   -D --currentdate          add/update date field in patch with current date
+   -d --date DATE            add/update date field in patch with given date
+   -I --include PATTERN [+]  include names matching the given patterns
+   -X --exclude PATTERN [+]  exclude names matching the given patterns
+   -m --message TEXT         use text as commit message
+   -l --logfile FILE         read commit message from file
+  
+  [+] marked option can be specified multiple times
+  
+  use "hg -v help qrefresh" to show global options
+
+help qrefresh (record)
+
+  $ echo "record=" >> $HGRCPATH
+  $ hg help qrefresh
+  hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...
+  
+  update the current patch
+  
+      If any file patterns are provided, the refreshed patch will contain only
+      the modifications that match those patterns; the remaining modifications
+      will remain in the working directory.
+  
+      If -s/--short is specified, files currently included in the patch will be
+      refreshed just like matched files and remain in the patch.
+  
+      If -e/--edit is specified, Mercurial will start your configured editor for
+      you to enter a message. In case qrefresh fails, you will find a backup of
+      your message in ".hg/last-message.txt".
+  
+      hg add/remove/copy/rename work as usual, though you might want to use git-
+      style patches (-g/--git or [diff] git=1) to track copies and renames. See
+      the diffs help topic for more information on the git diff format.
+  
+      Returns 0 on success.
+  
+  options:
+  
+   -e --edit                 edit commit message
+   -g --git                  use git extended diff format
+   -s --short                refresh only files already in the patch and
+                             specified files
+   -U --currentuser          add/update author field in patch with current user
+   -u --user USER            add/update author field in patch with given user
+   -D --currentdate          add/update date field in patch with current date
+   -d --date DATE            add/update date field in patch with given date
+   -I --include PATTERN [+]  include names matching the given patterns
+   -X --exclude PATTERN [+]  exclude names matching the given patterns
+   -m --message TEXT         use text as commit message
+   -l --logfile FILE         read commit message from file
+   -i --interactive          interactively select changes to refresh
+  
+  [+] marked option can be specified multiple times
+  
+  use "hg -v help qrefresh" to show global options
+
+  $ hg init a
+  $ cd a
+
+Base commit
+
+  $ cat > 1.txt <<EOF
+  > 1
+  > 2
+  > 3
+  > 4
+  > 5
+  > EOF
+  $ cat > 2.txt <<EOF
+  > a
+  > b
+  > c
+  > d
+  > e
+  > f
+  > EOF
+
+  $ mkdir dir
+  $ cat > dir/a.txt <<EOF
+  > hello world
+  > 
+  > someone
+  > up
+  > there
+  > loves
+  > me
+  > EOF
+
+  $ hg add 1.txt 2.txt dir/a.txt
+  $ hg commit -m aaa
+  $ hg qnew -d '0 0' patch
+
+Changing files
+
+  $ sed -e 's/2/2 2/;s/4/4 4/' 1.txt > 1.txt.new
+  $ sed -e 's/b/b b/' 2.txt > 2.txt.new
+  $ sed -e 's/hello world/hello world!/' dir/a.txt > dir/a.txt.new
+
+  $ mv -f 1.txt.new 1.txt
+  $ mv -f 2.txt.new 2.txt
+  $ mv -f dir/a.txt.new dir/a.txt
+
+Whole diff
+
+  $ hg diff --nodates
+  diff -r ed27675cb5df 1.txt
+  --- a/1.txt
+  +++ b/1.txt
+  @@ -1,5 +1,5 @@
+   1
+  -2
+  +2 2
+   3
+  -4
+  +4 4
+   5
+  diff -r ed27675cb5df 2.txt
+  --- a/2.txt
+  +++ b/2.txt
+  @@ -1,5 +1,5 @@
+   a
+  -b
+  +b b
+   c
+   d
+   e
+  diff -r ed27675cb5df dir/a.txt
+  --- a/dir/a.txt
+  +++ b/dir/a.txt
+  @@ -1,4 +1,4 @@
+  -hello world
+  +hello world!
+   
+   someone
+   up
+
+partial qrefresh
+
+  $ hg qrefresh -i -d '0 0' <<EOF
+  > y
+  > y
+  > n
+  > y
+  > y
+  > n
+  > EOF
+  diff --git a/1.txt b/1.txt
+  2 hunks, 2 lines changed
+  examine changes to '1.txt'? [Ynsfdaq?] 
+  @@ -1,3 +1,3 @@
+   1
+  -2
+  +2 2
+   3
+  record change 1/4 to '1.txt'? [Ynsfdaq?] 
+  @@ -3,3 +3,3 @@
+   3
+  -4
+  +4 4
+   5
+  record change 2/4 to '1.txt'? [Ynsfdaq?] 
+  diff --git a/2.txt b/2.txt
+  1 hunks, 1 lines changed
+  examine changes to '2.txt'? [Ynsfdaq?] 
+  @@ -1,5 +1,5 @@
+   a
+  -b
+  +b b
+   c
+   d
+   e
+  record change 3/4 to '2.txt'? [Ynsfdaq?] 
+  diff --git a/dir/a.txt b/dir/a.txt
+  1 hunks, 1 lines changed
+  examine changes to 'dir/a.txt'? [Ynsfdaq?] 
+
+After partial qrefresh 'tip'
+
+  $ hg tip -p
+  changeset:   1:0738af1a8211
+  tag:         patch
+  tag:         qbase
+  tag:         qtip
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     [mq]: patch
+  
+  diff -r 1fd39ab63a33 -r 0738af1a8211 1.txt
+  --- a/1.txt	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/1.txt	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,5 +1,5 @@
+   1
+  -2
+  +2 2
+   3
+   4
+   5
+  diff -r 1fd39ab63a33 -r 0738af1a8211 2.txt
+  --- a/2.txt	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/2.txt	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,5 +1,5 @@
+   a
+  -b
+  +b b
+   c
+   d
+   e
+  
+After partial qrefresh 'diff'
+
+  $ hg diff --nodates
+  diff -r 0738af1a8211 1.txt
+  --- a/1.txt
+  +++ b/1.txt
+  @@ -1,5 +1,5 @@
+   1
+   2 2
+   3
+  -4
+  +4 4
+   5
+  diff -r 0738af1a8211 dir/a.txt
+  --- a/dir/a.txt
+  +++ b/dir/a.txt
+  @@ -1,4 +1,4 @@
+  -hello world
+  +hello world!
+   
+   someone
+   up
+
+qrefresh interactively everything else
+
+  $ hg qrefresh -i -d '0 0' <<EOF
+  > y
+  > y
+  > y
+  > y
+  > EOF
+  diff --git a/1.txt b/1.txt
+  1 hunks, 1 lines changed
+  examine changes to '1.txt'? [Ynsfdaq?] 
+  @@ -1,5 +1,5 @@
+   1
+   2 2
+   3
+  -4
+  +4 4
+   5
+  record change 1/2 to '1.txt'? [Ynsfdaq?] 
+  diff --git a/dir/a.txt b/dir/a.txt
+  1 hunks, 1 lines changed
+  examine changes to 'dir/a.txt'? [Ynsfdaq?] 
+  @@ -1,4 +1,4 @@
+  -hello world
+  +hello world!
+   
+   someone
+   up
+  record change 2/2 to 'dir/a.txt'? [Ynsfdaq?] 
+
+After final qrefresh 'tip'
+
+  $ hg tip -p
+  changeset:   1:2c3f66afeed9
+  tag:         patch
+  tag:         qbase
+  tag:         qtip
+  tag:         tip
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     [mq]: patch
+  
+  diff -r 1fd39ab63a33 -r 2c3f66afeed9 1.txt
+  --- a/1.txt	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/1.txt	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,5 +1,5 @@
+   1
+  -2
+  +2 2
+   3
+  -4
+  +4 4
+   5
+  diff -r 1fd39ab63a33 -r 2c3f66afeed9 2.txt
+  --- a/2.txt	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/2.txt	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,5 +1,5 @@
+   a
+  -b
+  +b b
+   c
+   d
+   e
+  diff -r 1fd39ab63a33 -r 2c3f66afeed9 dir/a.txt
+  --- a/dir/a.txt	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/dir/a.txt	Thu Jan 01 00:00:00 1970 +0000
+  @@ -1,4 +1,4 @@
+  -hello world
+  +hello world!
+   
+   someone
+   up
+  
+
+After qrefresh 'diff'
+
+  $ hg diff --nodates
--- a/tests/test-qrecord.t	Mon May 30 11:52:17 2011 +0200
+++ b/tests/test-qrecord.t	Mon May 30 12:20:36 2011 +0200
@@ -99,9 +99,24 @@
   abort: 'mq' extension not loaded
   [255]
 
+help (bad mq)
+
+  $ echo "mq=nonexistant" >> $HGRCPATH
+  $ hg help qrecord
+  *** failed to import extension mq from nonexistant: [Errno 2] No such file or directory
+  hg qrecord [OPTION]... PATCH [FILE]...
+  
+  interactively record a new patch
+  
+      See "hg help qnew" & "hg help record" for more information and usage.
+  
+  use "hg -v help qrecord" to show global options
+
 help (mq present)
 
-  $ echo "mq="              >> $HGRCPATH
+  $ sed 's/mq=nonexistant/mq=/' $HGRCPATH > hgrc.tmp
+  $ mv hgrc.tmp $HGRCPATH
+
   $ hg help qrecord
   hg qrecord [OPTION]... PATCH [FILE]...
   
@@ -121,6 +136,7 @@
    -X --exclude PATTERN [+]  exclude names matching the given patterns
    -m --message TEXT         use text as commit message
    -l --logfile FILE         read commit message from file
+      --mq                   operate on patch repository
   
   [+] marked option can be specified multiple times
   
@@ -204,6 +220,12 @@
    someone
    up
 
+qrecord with bad patch name, should abort before prompting
+
+  $ hg qrecord .hg
+  abort: patch name cannot begin with ".hg"
+  [255]
+
 qrecord a.patch
 
   $ hg qrecord -d '0 0' -m aaa a.patch <<EOF
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-treediscovery-legacy.t	Mon May 30 12:20:36 2011 +0200
@@ -0,0 +1,310 @@
+Tests discovery against servers without getbundle support:
+
+  $ cat >> $HGRCPATH <<EOF
+  > [ui]
+  > logtemplate="{rev} {node|short}: {desc} {branches}\n"
+  > [extensions]
+  > graphlog=
+  > EOF
+  $ cp $HGRCPATH $HGRCPATH-withcap
+
+  $ CAP="getbundle known changegroupsubset"
+  $ . "$TESTDIR/notcapable"
+  $ cp $HGRCPATH $HGRCPATH-nocap
+  $ cp $HGRCPATH-withcap $HGRCPATH
+
+Setup HTTP server control:
+
+  $ remote=http://localhost:$HGPORT/
+  $ export remote
+  $ start() {
+  >   echo '[web]' > $1/.hg/hgrc
+  >   echo 'push_ssl = false' >> $1/.hg/hgrc
+  >   echo 'allow_push = *' >> $1/.hg/hgrc
+  >   cp $HGRCPATH-nocap $HGRCPATH
+  >   hg serve -R $1 -p $HGPORT -d --pid-file=hg.pid -E errors.log
+  >   cat hg.pid >> $DAEMON_PIDS
+  > }
+  $ stop() {
+  >   "$TESTDIR/killdaemons.py"
+  >   cp $HGRCPATH-withcap $HGRCPATH
+  > }
+
+Both are empty:
+
+  $ hg init empty1
+  $ hg init empty2
+  $ start empty2
+  $ hg incoming -R empty1 $remote
+  comparing with http://localhost:$HGPORT/
+  no changes found
+  [1]
+  $ hg outgoing -R empty1 $remote
+  comparing with http://localhost:$HGPORT/
+  no changes found
+  [1]
+  $ hg pull -R empty1 $remote
+  pulling from http://localhost:$HGPORT/
+  no changes found
+  $ hg push -R empty1 $remote
+  pushing to http://localhost:$HGPORT/
+  no changes found
+  $ stop
+
+Base repo:
+
+  $ hg init main
+  $ cd main
+  $ hg debugbuilddag -mo '+2:tbase @name1 +3:thead1 <tbase @name2 +4:thead2 @both /thead1 +2:tmaintip'
+  $ hg glog
+  o  11 a19bfa7e7328: r11 both
+  |
+  o  10 8b6bad1512e1: r10 both
+  |
+  o    9 025829e08038: r9 both
+  |\
+  | o  8 d8f638ac69e9: r8 name2
+  | |
+  | o  7 b6b4d315a2ac: r7 name2
+  | |
+  | o  6 6c6f5d5f3c11: r6 name2
+  | |
+  | o  5 70314b29987d: r5 name2
+  | |
+  o |  4 e71dbbc70e03: r4 name1
+  | |
+  o |  3 2c8d5d5ec612: r3 name1
+  | |
+  o |  2 a7892891da29: r2 name1
+  |/
+  o  1 0019a3b924fd: r1
+  |
+  o  0 d57206cc072a: r0
+  
+  $ cd ..
+  $ start main
+
+Full clone:
+
+  $ hg clone main full
+  updating to branch default
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd full
+  $ hg incoming $remote
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  no changes found
+  [1]
+  $ hg outgoing $remote
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  no changes found
+  [1]
+  $ hg pull $remote
+  pulling from http://localhost:$HGPORT/
+  searching for changes
+  no changes found
+  $ hg push $remote
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  no changes found
+  $ cd ..
+
+Local is empty:
+
+  $ cd empty1
+  $ hg incoming $remote --rev name1
+  comparing with http://localhost:$HGPORT/
+  abort: cannot look up remote changes; remote repository does not support the 'changegroupsubset' capability!
+  [255]
+  $ hg incoming $remote
+  comparing with http://localhost:$HGPORT/
+  0 d57206cc072a: r0 
+  1 0019a3b924fd: r1 
+  2 a7892891da29: r2 name1
+  3 2c8d5d5ec612: r3 name1
+  4 e71dbbc70e03: r4 name1
+  5 70314b29987d: r5 name2
+  6 6c6f5d5f3c11: r6 name2
+  7 b6b4d315a2ac: r7 name2
+  8 d8f638ac69e9: r8 name2
+  9 025829e08038: r9 both
+  10 8b6bad1512e1: r10 both
+  11 a19bfa7e7328: r11 both
+  $ hg outgoing $remote
+  comparing with http://localhost:$HGPORT/
+  no changes found
+  [1]
+  $ hg push $remote
+  pushing to http://localhost:$HGPORT/
+  no changes found
+  $ hg pull $remote
+  pulling from http://localhost:$HGPORT/
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 12 changesets with 24 changes to 2 files
+  (run 'hg update' to get a working copy)
+  $ hg incoming $remote
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  no changes found
+  [1]
+  $ cd ..
+
+Local is subset:
+
+  $ cp $HGRCPATH-withcap $HGRCPATH
+  $ hg clone main subset --rev name2 ; cd subset
+  adding changesets
+  adding manifests
+  adding file changes
+  added 6 changesets with 12 changes to 2 files
+  updating to branch name2
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cp $HGRCPATH-nocap $HGRCPATH
+  $ hg incoming $remote
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  6 a7892891da29: r2 name1
+  7 2c8d5d5ec612: r3 name1
+  8 e71dbbc70e03: r4 name1
+  9 025829e08038: r9 both
+  10 8b6bad1512e1: r10 both
+  11 a19bfa7e7328: r11 both
+  $ hg outgoing $remote
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  no changes found
+  [1]
+  $ hg push $remote
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  no changes found
+  $ hg pull $remote
+  pulling from http://localhost:$HGPORT/
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 6 changesets with 12 changes to 2 files
+  (run 'hg update' to get a working copy)
+  $ hg incoming $remote
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  no changes found
+  [1]
+  $ cd ..
+
+Remote is empty:
+
+  $ stop ; start empty2
+  $ cd main
+  $ hg incoming $remote
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  no changes found
+  [1]
+  $ hg outgoing $remote
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  0 d57206cc072a: r0 
+  1 0019a3b924fd: r1 
+  2 a7892891da29: r2 name1
+  3 2c8d5d5ec612: r3 name1
+  4 e71dbbc70e03: r4 name1
+  5 70314b29987d: r5 name2
+  6 6c6f5d5f3c11: r6 name2
+  7 b6b4d315a2ac: r7 name2
+  8 d8f638ac69e9: r8 name2
+  9 025829e08038: r9 both
+  10 8b6bad1512e1: r10 both
+  11 a19bfa7e7328: r11 both
+  $ hg pull $remote
+  pulling from http://localhost:$HGPORT/
+  searching for changes
+  no changes found
+  $ hg push $remote
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 12 changesets with 24 changes to 2 files
+  $ hg outgoing $remote
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  no changes found
+  [1]
+  $ cd ..
+
+Local is superset:
+
+  $ stop
+  $ hg clone main subset2 --rev name2
+  adding changesets
+  adding manifests
+  adding file changes
+  added 6 changesets with 12 changes to 2 files
+  updating to branch name2
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ start subset2
+  $ cd main
+  $ hg incoming $remote
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  no changes found
+  [1]
+  $ hg outgoing $remote
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  2 a7892891da29: r2 name1
+  3 2c8d5d5ec612: r3 name1
+  4 e71dbbc70e03: r4 name1
+  9 025829e08038: r9 both
+  10 8b6bad1512e1: r10 both
+  11 a19bfa7e7328: r11 both
+  $ hg pull $remote
+  pulling from http://localhost:$HGPORT/
+  searching for changes
+  no changes found
+  $ hg push $remote
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  abort: push creates new remote branches: both, name1!
+  (use 'hg push --new-branch' to create new remote branches)
+  [255]
+  $ hg push $remote --new-branch
+  pushing to http://localhost:$HGPORT/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 6 changesets with 12 changes to 2 files
+  $ hg outgoing $remote
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  no changes found
+  [1]
+  $ cd ..
+
+Partial pull:
+
+  $ stop ; start main
+  $ hg clone $remote partial --rev name2
+  abort: partial pull cannot be done because other repository doesn't support changegroupsubset.
+  [255]
+  $ hg init partial; cd partial
+  $ hg incoming $remote --rev name2
+  comparing with http://localhost:$HGPORT/
+  abort: cannot look up remote changes; remote repository does not support the 'changegroupsubset' capability!
+  [255]
+  $ hg pull $remote --rev name2
+  pulling from http://localhost:$HGPORT/
+  abort: partial pull cannot be done because other repository doesn't support changegroupsubset.
+  [255]
+  $ cd ..
+
+  $ stop
+
--- a/tests/test-treediscovery.t	Mon May 30 11:52:17 2011 +0200
+++ b/tests/test-treediscovery.t	Mon May 30 12:20:36 2011 +0200
@@ -277,5 +277,47 @@
   [1]
   $ cd ..
 
+Partial pull:
+
+  $ stop ; start main
+  $ hg clone $remote partial --rev name2
+  adding changesets
+  adding manifests
+  adding file changes
+  added 6 changesets with 12 changes to 2 files
+  updating to branch name2
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd partial
+  $ hg incoming $remote
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  6 a7892891da29: r2 name1
+  7 2c8d5d5ec612: r3 name1
+  8 e71dbbc70e03: r4 name1
+  9 025829e08038: r9 both
+  10 8b6bad1512e1: r10 both
+  11 a19bfa7e7328: r11 both
+  $ hg incoming $remote --rev name1
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  6 a7892891da29: r2 name1
+  7 2c8d5d5ec612: r3 name1
+  8 e71dbbc70e03: r4 name1
+  $ hg pull $remote --rev name1
+  pulling from http://localhost:$HGPORT/
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 3 changesets with 6 changes to 2 files (+1 heads)
+  (run 'hg heads' to see heads)
+  $ hg incoming $remote
+  comparing with http://localhost:$HGPORT/
+  searching for changes
+  9 025829e08038: r9 both
+  10 8b6bad1512e1: r10 both
+  11 a19bfa7e7328: r11 both
+  $ cd ..
+
   $ stop