changeset 14628:33f620027b58

merge with i18n
author Matt Mackall <mpm@selenic.com>
date Tue, 14 Jun 2011 20:43:04 -0500
parents f03c82d1f50a (diff) 166776f97c9c (current diff)
children c3f2152e423d 71b9c29eb44a
files
diffstat 74 files changed, 2287 insertions(+), 1068 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Tue Jun 14 00:31:56 2011 +0400
+++ b/.hgignore	Tue Jun 14 20:43:04 2011 -0500
@@ -26,6 +26,7 @@
 doc/*.[0-9].gendoc.txt
 doc/*.[0-9].{x,ht}ml
 MANIFEST
+MANIFEST.in
 patches
 mercurial/__version__.py
 mercurial.egg-info
@@ -35,6 +36,7 @@
 cscope.*
 i18n/hg.pot
 locale/*/LC_MESSAGES/hg.mo
+hgext/__index__.py
 
 # files installed with a local --pure build
 mercurial/base85.py
--- a/Makefile	Tue Jun 14 00:31:56 2011 +0400
+++ b/Makefile	Tue Jun 14 20:43:04 2011 -0500
@@ -46,7 +46,7 @@
 	-$(PYTHON) setup.py clean --all # ignore errors from this command
 	find . \( -name '*.py[cdo]' -o -name '*.so' \) -exec rm -f '{}' ';'
 	rm -f $(addprefix mercurial/,$(notdir $(wildcard mercurial/pure/*.py)))
-	rm -f MANIFEST tests/*.err
+	rm -f MANIFEST MANIFEST.in tests/*.err
 	rm -rf build mercurial/locale
 	$(MAKE) -C doc clean
 
@@ -69,14 +69,14 @@
 MANIFEST-doc:
 	$(MAKE) -C doc MANIFEST
 
-MANIFEST: MANIFEST-doc
-	hg manifest > MANIFEST
-	echo mercurial/__version__.py >> MANIFEST
-	cat doc/MANIFEST >> MANIFEST
+MANIFEST.in: MANIFEST-doc
+	hg manifest | sed -e 's/^/include /' > MANIFEST.in
+	echo include mercurial/__version__.py >> MANIFEST.in
+	sed -e 's/^/include /' < doc/MANIFEST >> MANIFEST.in
 
 dist:	tests dist-notests
 
-dist-notests:	doc MANIFEST
+dist-notests:	doc MANIFEST.in
 	TAR_OPTIONS="--owner=root --group=root --mode=u+w,go-w,a+rX-s" $(PYTHON) setup.py -q sdist
 
 check: tests
--- a/contrib/check-code.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/contrib/check-code.py	Tue Jun 14 20:43:04 2011 -0500
@@ -177,6 +177,7 @@
      "always assign an opened file to a variable, and close it afterwards"),
     (r'[\s\(](open|file)\([^)]*\)\.',
      "always assign an opened file to a variable, and close it afterwards"),
+    (r'(?i)descendent', "the proper spelling is descendAnt"),
   ],
   # warnings
   [
--- a/hgext/bugzilla.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/hgext/bugzilla.py	Tue Jun 14 20:43:04 2011 -0500
@@ -197,7 +197,7 @@
 
 XMLRPC+email example configuration. This uses the Bugzilla at
 ``http://my-project.org/bugzilla``, logging in as user
-``bugmail@my-project.org`` wityh password ``plugh``. It is used with a
+``bugmail@my-project.org`` with password ``plugh``. It is used with a
 collection of Mercurial repositories in ``/var/local/hg/repos/``,
 with a web interface at ``http://my-project.org/hg``. Bug comments
 are sent to the Bugzilla email address
--- a/hgext/convert/hg.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/hgext/convert/hg.py	Tue Jun 14 20:43:04 2011 -0500
@@ -112,7 +112,7 @@
             self.after()
             for pbranch, heads in missings.iteritems():
                 pbranchpath = os.path.join(self.path, pbranch)
-                prepo = hg.repository(self.ui, pbranchpath)
+                prepo = hg.peer(self.ui, {}, pbranchpath)
                 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
                 self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
             self.before()
--- a/hgext/fetch.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/hgext/fetch.py	Tue Jun 14 20:43:04 2011 -0500
@@ -63,8 +63,7 @@
             raise util.Abort(_('multiple heads in this branch '
                                '(use "hg heads ." and "hg merge" to merge)'))
 
-        other = hg.repository(hg.remoteui(repo, opts),
-                              ui.expandpath(source))
+        other = hg.peer(repo, opts, ui.expandpath(source))
         ui.status(_('pulling from %s\n') %
                   util.hidepassword(ui.expandpath(source)))
         revs = None
--- a/hgext/keyword.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/hgext/keyword.py	Tue Jun 14 20:43:04 2011 -0500
@@ -595,12 +595,10 @@
                 wlock.release()
 
     # monkeypatches
-    def kwpatchfile_init(orig, self, ui, fname, backend, store, mode, create,
-                         remove, eolmode=None, copysource=None):
+    def kwpatchfile_init(orig, self, ui, gp, backend, store, eolmode=None):
         '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
         rejects or conflicts due to expanded keywords in working dir.'''
-        orig(self, ui, fname, backend, store, mode, create, remove,
-             eolmode, copysource)
+        orig(self, ui, gp, backend, store, eolmode)
         # shrink keywords read from working dir
         self.lines = kwt.shrinklines(self.fname, self.lines)
 
--- a/hgext/mq.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/hgext/mq.py	Tue Jun 14 20:43:04 2011 -0500
@@ -267,14 +267,14 @@
         self.path = patchdir or curpath
         self.opener = scmutil.opener(self.path)
         self.ui = ui
-        self.applied_dirty = 0
-        self.series_dirty = 0
+        self.applieddirty = 0
+        self.seriesdirty = 0
         self.added = []
-        self.series_path = "series"
-        self.status_path = "status"
-        self.guards_path = "guards"
-        self.active_guards = None
-        self.guards_dirty = False
+        self.seriespath = "series"
+        self.statuspath = "status"
+        self.guardspath = "guards"
+        self.activeguards = None
+        self.guardsdirty = False
         # Handle mq.git as a bool with extended values
         try:
             gitmode = ui.configbool('mq', 'git', None)
@@ -287,7 +287,7 @@
 
     @util.propertycache
     def applied(self):
-        if os.path.exists(self.join(self.status_path)):
+        if os.path.exists(self.join(self.statuspath)):
             def parselines(lines):
                 for l in lines:
                     entry = l.split(':', 1)
@@ -297,34 +297,34 @@
                     elif l.strip():
                         self.ui.warn(_('malformated mq status line: %s\n') % entry)
                     # else we ignore empty lines
-            lines = self.opener.read(self.status_path).splitlines()
+            lines = self.opener.read(self.statuspath).splitlines()
             return list(parselines(lines))
         return []
 
     @util.propertycache
-    def full_series(self):
-        if os.path.exists(self.join(self.series_path)):
-            return self.opener.read(self.series_path).splitlines()
+    def fullseries(self):
+        if os.path.exists(self.join(self.seriespath)):
+            return self.opener.read(self.seriespath).splitlines()
         return []
 
     @util.propertycache
     def series(self):
-        self.parse_series()
+        self.parseseries()
         return self.series
 
     @util.propertycache
-    def series_guards(self):
-        self.parse_series()
-        return self.series_guards
+    def seriesguards(self):
+        self.parseseries()
+        return self.seriesguards
 
     def invalidate(self):
-        for a in 'applied full_series series series_guards'.split():
+        for a in 'applied fullseries series seriesguards'.split():
             if a in self.__dict__:
                 delattr(self, a)
-        self.applied_dirty = 0
-        self.series_dirty = 0
-        self.guards_dirty = False
-        self.active_guards = None
+        self.applieddirty = 0
+        self.seriesdirty = 0
+        self.guardsdirty = False
+        self.activeguards = None
 
     def diffopts(self, opts={}, patchfn=None):
         diffopts = patchmod.diffopts(self.ui, opts)
@@ -360,21 +360,21 @@
     def join(self, *p):
         return os.path.join(self.path, *p)
 
-    def find_series(self, patch):
+    def findseries(self, patch):
         def matchpatch(l):
             l = l.split('#', 1)[0]
             return l.strip() == patch
-        for index, l in enumerate(self.full_series):
+        for index, l in enumerate(self.fullseries):
             if matchpatch(l):
                 return index
         return None
 
     guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
 
-    def parse_series(self):
+    def parseseries(self):
         self.series = []
-        self.series_guards = []
-        for l in self.full_series:
+        self.seriesguards = []
+        for l in self.fullseries:
             h = l.find('#')
             if h == -1:
                 patch = l
@@ -388,11 +388,11 @@
             if patch:
                 if patch in self.series:
                     raise util.Abort(_('%s appears more than once in %s') %
-                                     (patch, self.join(self.series_path)))
+                                     (patch, self.join(self.seriespath)))
                 self.series.append(patch)
-                self.series_guards.append(self.guard_re.findall(comment))
-
-    def check_guard(self, guard):
+                self.seriesguards.append(self.guard_re.findall(comment))
+
+    def checkguard(self, guard):
         if not guard:
             return _('guard cannot be an empty string')
         bad_chars = '# \t\r\n\f'
@@ -404,52 +404,52 @@
             if c in guard:
                 return _('invalid character in guard %r: %r') % (guard, c)
 
-    def set_active(self, guards):
+    def setactive(self, guards):
         for guard in guards:
-            bad = self.check_guard(guard)
+            bad = self.checkguard(guard)
             if bad:
                 raise util.Abort(bad)
         guards = sorted(set(guards))
         self.ui.debug('active guards: %s\n' % ' '.join(guards))
-        self.active_guards = guards
-        self.guards_dirty = True
+        self.activeguards = guards
+        self.guardsdirty = True
 
     def active(self):
-        if self.active_guards is None:
-            self.active_guards = []
+        if self.activeguards is None:
+            self.activeguards = []
             try:
-                guards = self.opener.read(self.guards_path).split()
+                guards = self.opener.read(self.guardspath).split()
             except IOError, err:
                 if err.errno != errno.ENOENT:
                     raise
                 guards = []
             for i, guard in enumerate(guards):
-                bad = self.check_guard(guard)
+                bad = self.checkguard(guard)
                 if bad:
                     self.ui.warn('%s:%d: %s\n' %
-                                 (self.join(self.guards_path), i + 1, bad))
+                                 (self.join(self.guardspath), i + 1, bad))
                 else:
-                    self.active_guards.append(guard)
-        return self.active_guards
-
-    def set_guards(self, idx, guards):
+                    self.activeguards.append(guard)
+        return self.activeguards
+
+    def setguards(self, idx, guards):
         for g in guards:
             if len(g) < 2:
                 raise util.Abort(_('guard %r too short') % g)
             if g[0] not in '-+':
                 raise util.Abort(_('guard %r starts with invalid char') % g)
-            bad = self.check_guard(g[1:])
+            bad = self.checkguard(g[1:])
             if bad:
                 raise util.Abort(bad)
-        drop = self.guard_re.sub('', self.full_series[idx])
-        self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
-        self.parse_series()
-        self.series_dirty = True
+        drop = self.guard_re.sub('', self.fullseries[idx])
+        self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
+        self.parseseries()
+        self.seriesdirty = True
 
     def pushable(self, idx):
         if isinstance(idx, str):
             idx = self.series.index(idx)
-        patchguards = self.series_guards[idx]
+        patchguards = self.seriesguards[idx]
         if not patchguards:
             return True, None
         guards = self.active()
@@ -464,7 +464,7 @@
             return False, ' '.join(map(repr, pos))
         return True, ''
 
-    def explain_pushable(self, idx, all_patches=False):
+    def explainpushable(self, idx, all_patches=False):
         write = all_patches and self.ui.write or self.ui.warn
         if all_patches or self.ui.verbose:
             if isinstance(idx, str):
@@ -489,18 +489,18 @@
                     write(_('skipping %s - no matching guards\n') %
                           self.series[idx])
 
-    def save_dirty(self):
-        def write_list(items, path):
+    def savedirty(self):
+        def writelist(items, path):
             fp = self.opener(path, 'w')
             for i in items:
                 fp.write("%s\n" % i)
             fp.close()
-        if self.applied_dirty:
-            write_list(map(str, self.applied), self.status_path)
-        if self.series_dirty:
-            write_list(self.full_series, self.series_path)
-        if self.guards_dirty:
-            write_list(self.active_guards, self.guards_path)
+        if self.applieddirty:
+            writelist(map(str, self.applied), self.statuspath)
+        if self.seriesdirty:
+            writelist(self.fullseries, self.seriespath)
+        if self.guardsdirty:
+            writelist(self.activeguards, self.guardspath)
         if self.added:
             qrepo = self.qrepo()
             if qrepo:
@@ -587,7 +587,7 @@
             n = repo.commit('[mq]: merge marker', force=True)
             self.removeundo(repo)
             self.applied.append(statusentry(n, pname))
-            self.applied_dirty = 1
+            self.applieddirty = 1
 
         head = self.qparents(repo)
 
@@ -598,7 +598,7 @@
                 return (1, None)
             pushable, reason = self.pushable(patch)
             if not pushable:
-                self.explain_pushable(patch, all_patches=True)
+                self.explainpushable(patch, all_patches=True)
                 continue
             info = mergeq.isapplied(patch)
             if not info:
@@ -608,16 +608,16 @@
             err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
             if head:
                 self.applied.append(statusentry(head, patch))
-                self.applied_dirty = 1
+                self.applieddirty = 1
             if err:
                 return (err, head)
-        self.save_dirty()
+        self.savedirty()
         return (0, head)
 
     def patch(self, repo, patchfile):
         '''Apply patchfile  to the working directory.
         patchfile: name of patch file'''
-        files = {}
+        files = set()
         try:
             fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
                                   files=files, eolmode=None)
@@ -639,7 +639,7 @@
                 ret = self._apply(repo, series, list, update_status,
                                   strict, patchdir, merge, all_files=all_files)
                 tr.close()
-                self.save_dirty()
+                self.savedirty()
                 return ret
             except:
                 try:
@@ -664,7 +664,7 @@
         for patchname in series:
             pushable, reason = self.pushable(patchname)
             if not pushable:
-                self.explain_pushable(patchname, all_patches=True)
+                self.explainpushable(patchname, all_patches=True)
                 continue
             self.ui.status(_("applying %s\n") % patchname)
             pf = os.path.join(patchdir, patchname)
@@ -742,14 +742,14 @@
         if numrevs:
             qfinished = self.applied[:numrevs]
             del self.applied[:numrevs]
-            self.applied_dirty = 1
+            self.applieddirty = 1
 
         unknown = []
 
-        for (i, p) in sorted([(self.find_series(p), p) for p in patches],
+        for (i, p) in sorted([(self.findseries(p), p) for p in patches],
                              reverse=True):
             if i is not None:
-                del self.full_series[i]
+                del self.fullseries[i]
             else:
                 unknown.append(p)
 
@@ -763,8 +763,8 @@
                 msg = _('unknown patches: %s\n')
                 raise util.Abort(''.join(msg % p for p in unknown))
 
-        self.parse_series()
-        self.series_dirty = 1
+        self.parseseries()
+        self.seriesdirty = 1
 
     def _revpatches(self, repo, revs):
         firstrev = repo[self.applied[0].node].rev()
@@ -823,7 +823,7 @@
 
         self._cleanup(realpatches, numrevs, opts.get('keep'))
 
-    def check_toppatch(self, repo):
+    def checktoppatch(self, repo):
         if self.applied:
             top = self.applied[-1].node
             patch = self.applied[-1].name
@@ -833,7 +833,7 @@
             return top, patch
         return None, None
 
-    def check_substate(self, repo):
+    def checksubstate(self, repo):
         '''return list of subrepos at a different revision than substate.
         Abort if any subrepos have uncommitted changes.'''
         inclsubs = []
@@ -852,14 +852,14 @@
         else:
             raise util.Abort(_("local changes found"))
 
-    def check_localchanges(self, repo, force=False, refresh=True):
+    def checklocalchanges(self, repo, force=False, refresh=True):
         m, a, r, d = repo.status()[:4]
         if (m or a or r or d) and not force:
             self.localchangesfound(refresh)
         return m, a, r, d
 
     _reserved = ('series', 'status', 'guards', '.', '..')
-    def check_reserved_name(self, name):
+    def checkreservedname(self, name):
         if name in self._reserved:
             raise util.Abort(_('"%s" cannot be used as the name of a patch')
                              % name)
@@ -873,7 +873,7 @@
                                  % c)
 
     def checkpatchname(self, name, force=False):
-        self.check_reserved_name(name)
+        self.checkreservedname(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')
@@ -893,7 +893,7 @@
         diffopts = self.diffopts({'git': opts.get('git')})
         if opts.get('checkname', True):
             self.checkpatchname(patchfn)
-        inclsubs = self.check_substate(repo)
+        inclsubs = self.checksubstate(repo)
         if inclsubs:
             inclsubs.append('.hgsubstate')
         if opts.get('include') or opts.get('exclude') or pats:
@@ -907,13 +907,13 @@
             match.bad = badfn
             m, a, r, d = repo.status(match=match)[:4]
         else:
-            m, a, r, d = self.check_localchanges(repo, force=True)
+            m, a, r, d = self.checklocalchanges(repo, force=True)
             match = scmutil.matchfiles(repo, m + a + r + inclsubs)
         if len(repo[None].parents()) > 1:
             raise util.Abort(_('cannot manage merge changesets'))
         commitfiles = m + a + r
-        self.check_toppatch(repo)
-        insert = self.full_series_end()
+        self.checktoppatch(repo)
+        insert = self.fullseriesend()
         wlock = repo.wlock()
         try:
             try:
@@ -945,11 +945,11 @@
                 if n is None:
                     raise util.Abort(_("repo commit failed"))
                 try:
-                    self.full_series[insert:insert] = [patchfn]
+                    self.fullseries[insert:insert] = [patchfn]
                     self.applied.append(statusentry(n, patchfn))
-                    self.parse_series()
-                    self.series_dirty = 1
-                    self.applied_dirty = 1
+                    self.parseseries()
+                    self.seriesdirty = 1
+                    self.applieddirty = 1
                     if msg:
                         msg = msg + "\n\n"
                         p.write(msg)
@@ -986,7 +986,7 @@
             lock = repo.lock()
 
             if update:
-                self.check_localchanges(repo, force=force, refresh=False)
+                self.checklocalchanges(repo, force=force, refresh=False)
                 urev = self.qparents(repo, revs[0])
                 hg.clean(repo, urev)
                 repo.dirstate.write()
@@ -1016,7 +1016,7 @@
     def lookup(self, patch, strict=False):
         patch = patch and str(patch)
 
-        def partial_name(s):
+        def partialname(s):
             if s in self.series:
                 return s
             matches = [x for x in self.series if s in x]
@@ -1029,7 +1029,7 @@
                 return matches[0]
             if self.series and self.applied:
                 if s == 'qtip':
-                    return self.series[self.series_end(True)-1]
+                    return self.series[self.seriesend(True)-1]
                 if s == 'qbase':
                     return self.series[0]
             return None
@@ -1049,12 +1049,12 @@
                     return self.series[sno]
 
             if not strict:
-                res = partial_name(patch)
+                res = partialname(patch)
                 if res:
                     return res
                 minus = patch.rfind('-')
                 if minus >= 0:
-                    res = partial_name(patch[:minus])
+                    res = partialname(patch[:minus])
                     if res:
                         i = self.series.index(res)
                         try:
@@ -1066,7 +1066,7 @@
                                 return self.series[i - off]
                 plus = patch.rfind('+')
                 if plus >= 0:
-                    res = partial_name(patch[:plus])
+                    res = partialname(patch[:plus])
                     if res:
                         i = self.series.index(res)
                         try:
@@ -1109,7 +1109,7 @@
 
                 pushable, reason = self.pushable(patch)
                 if pushable:
-                    if self.series.index(patch) < self.series_end():
+                    if self.series.index(patch) < self.seriesend():
                         raise util.Abort(
                             _("cannot push to a previous patch: %s") % patch)
                 else:
@@ -1130,7 +1130,7 @@
             # qpush without an argument is an error (nothing to
             # apply). This allows a loop of "...while hg qpush..." to
             # work as it detects an error when done
-            start = self.series_end()
+            start = self.seriesend()
             if start == len(self.series):
                 self.ui.warn(_('patch series already fully applied\n'))
                 return 1
@@ -1150,21 +1150,21 @@
             if move:
                 if not patch:
                     raise util.Abort(_("please specify the patch to move"))
-                for i, rpn in enumerate(self.full_series[start:]):
+                for i, rpn in enumerate(self.fullseries[start:]):
                     # strip markers for patch guards
                     if self.guard_re.split(rpn, 1)[0] == patch:
                         break
                 index = start + i
-                assert index < len(self.full_series)
-                fullpatch = self.full_series[index]
-                del self.full_series[index]
-                self.full_series.insert(start, fullpatch)
-                self.parse_series()
-                self.series_dirty = 1
-
-            self.applied_dirty = 1
+                assert index < len(self.fullseries)
+                fullpatch = self.fullseries[index]
+                del self.fullseries[index]
+                self.fullseries.insert(start, fullpatch)
+                self.parseseries()
+                self.seriesdirty = 1
+
+            self.applieddirty = 1
             if start > 0:
-                self.check_toppatch(repo)
+                self.checktoppatch(repo)
             if not patch:
                 patch = self.series[start]
                 end = start + 1
@@ -1183,7 +1183,7 @@
                         if wcfiles.intersection(patchfiles):
                             self.localchangesfound(self.applied)
             elif mergeq:
-                self.check_localchanges(refresh=self.applied)
+                self.checklocalchanges(refresh=self.applied)
 
             all_files = set()
             try:
@@ -1265,11 +1265,11 @@
                         break
                 update = needupdate
 
-            self.applied_dirty = 1
+            self.applieddirty = 1
             end = len(self.applied)
             rev = self.applied[start].node
             if update:
-                top = self.check_toppatch(repo)[0]
+                top = self.checktoppatch(repo)[0]
 
             try:
                 heads = repo.changelog.heads(rev)
@@ -1319,7 +1319,7 @@
             wlock.release()
 
     def diff(self, repo, pats, opts):
-        top, patch = self.check_toppatch(repo)
+        top, patch = self.checktoppatch(repo)
         if not top:
             self.ui.write(_("no patches applied\n"))
             return
@@ -1343,12 +1343,12 @@
         wlock = repo.wlock()
 
         try:
-            self.check_toppatch(repo)
+            self.checktoppatch(repo)
             (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
             if repo.changelog.heads(top) != [top]:
                 raise util.Abort(_("cannot refresh a revision with children"))
 
-            inclsubs = self.check_substate(repo)
+            inclsubs = self.checksubstate(repo)
 
             cparents = repo.changelog.parents(top)
             patchparent = self.qparents(repo, top)
@@ -1494,7 +1494,7 @@
                 # assumes strip can roll itself back if interrupted
                 repo.dirstate.setparents(*cparents)
                 self.applied.pop()
-                self.applied_dirty = 1
+                self.applieddirty = 1
                 self.strip(repo, [top], update=False,
                            backup='strip')
             except:
@@ -1511,7 +1511,7 @@
             except:
                 ctx = repo[cparents[0]]
                 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
-                self.save_dirty()
+                self.savedirty()
                 self.ui.warn(_('refresh interrupted while patch was popped! '
                                '(revert --all, qpush to recover)\n'))
                 raise
@@ -1534,7 +1534,7 @@
         if patch and patch not in self.series:
             raise util.Abort(_("patch %s is not in series file") % patch)
         if not patch:
-            start = self.series_end()
+            start = self.seriesend()
         else:
             start = self.series.index(patch) + 1
         unapplied = []
@@ -1542,7 +1542,7 @@
             pushable, reason = self.pushable(i)
             if pushable:
                 unapplied.append((i, self.series[i]))
-            self.explain_pushable(i)
+            self.explainpushable(i)
         return unapplied
 
     def qseries(self, repo, missing=None, start=0, length=None, status=None,
@@ -1593,8 +1593,8 @@
                 for f in files:
                     fl = os.path.join(d, f)
                     if (fl not in self.series and
-                        fl not in (self.status_path, self.series_path,
-                                   self.guards_path)
+                        fl not in (self.statuspath, self.seriespath,
+                                   self.guardspath)
                         and not fl.startswith('.')):
                         msng_list.append(fl)
             for x in sorted(msng_list):
@@ -1638,11 +1638,11 @@
             self.ui.warn(_("No saved patch data found\n"))
             return 1
         self.ui.warn(_("restoring status: %s\n") % lines[0])
-        self.full_series = series
+        self.fullseries = series
         self.applied = applied
-        self.parse_series()
-        self.series_dirty = 1
-        self.applied_dirty = 1
+        self.parseseries()
+        self.seriesdirty = 1
+        self.applieddirty = 1
         heads = repo.changelog.heads()
         if delete:
             if rev not in heads:
@@ -1684,25 +1684,25 @@
             msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
         msg += "\n\nPatch Data:\n"
         msg += ''.join('%s\n' % x for x in self.applied)
-        msg += ''.join(':%s\n' % x for x in self.full_series)
+        msg += ''.join(':%s\n' % x for x in self.fullseries)
         n = repo.commit(msg, force=True)
         if not n:
             self.ui.warn(_("repo commit failed\n"))
             return 1
         self.applied.append(statusentry(n, '.hg.patches.save.line'))
-        self.applied_dirty = 1
+        self.applieddirty = 1
         self.removeundo(repo)
 
-    def full_series_end(self):
+    def fullseriesend(self):
         if self.applied:
             p = self.applied[-1].name
-            end = self.find_series(p)
+            end = self.findseries(p)
             if end is None:
-                return len(self.full_series)
+                return len(self.fullseries)
             return end + 1
         return 0
 
-    def series_end(self, all_patches=False):
+    def seriesend(self, all_patches=False):
         """If all_patches is False, return the index of the next pushable patch
         in the series, or the series length. If all_patches is True, return the
         index of the first patch past the last applied one.
@@ -1715,7 +1715,7 @@
                 p, reason = self.pushable(i)
                 if p:
                     break
-                self.explain_pushable(i)
+                self.explainpushable(i)
             return i
         if self.applied:
             p = self.applied[-1].name
@@ -1789,7 +1789,7 @@
                     patchname = normname('%d.diff' % r)
                 checkseries(patchname)
                 self.checkpatchname(patchname, force)
-                self.full_series.insert(0, patchname)
+                self.fullseries.insert(0, patchname)
 
                 patchf = self.opener(patchname, "w")
                 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
@@ -1800,16 +1800,16 @@
 
                 self.added.append(patchname)
                 patchname = None
-            self.parse_series()
-            self.applied_dirty = 1
-            self.series_dirty = True
+            self.parseseries()
+            self.applieddirty = 1
+            self.seriesdirty = True
 
         for i, filename in enumerate(files):
             if existing:
                 if filename == '-':
                     raise util.Abort(_('-e is incompatible with import from -'))
                 filename = normname(filename)
-                self.check_reserved_name(filename)
+                self.checkreservedname(filename)
                 originpath = self.join(filename)
                 if not os.path.isfile(originpath):
                     raise util.Abort(_("patch %s does not exist") % filename)
@@ -1844,10 +1844,10 @@
             if not force:
                 checkseries(patchname)
             if patchname not in self.series:
-                index = self.full_series_end() + i
-                self.full_series[index:index] = [patchname]
-            self.parse_series()
-            self.series_dirty = True
+                index = self.fullseriesend() + i
+                self.fullseries[index:index] = [patchname]
+            self.parseseries()
+            self.seriesdirty = True
             self.ui.warn(_("adding %s to series file\n") % patchname)
             self.added.append(patchname)
             patchname = None
@@ -1869,7 +1869,7 @@
     use the :hg:`qfinish` command."""
     q = repo.mq
     q.delete(repo, patches, opts)
-    q.save_dirty()
+    q.savedirty()
     return 0
 
 @command("qapplied",
@@ -1888,7 +1888,7 @@
             raise util.Abort(_("patch %s is not in series file") % patch)
         end = q.series.index(patch) + 1
     else:
-        end = q.series_end(True)
+        end = q.seriesend(True)
 
     if opts.get('last') and not end:
         ui.write(_("no patches applied\n"))
@@ -1920,7 +1920,7 @@
             raise util.Abort(_("patch %s is not in series file") % patch)
         start = q.series.index(patch) + 1
     else:
-        start = q.series_end(True)
+        start = q.seriesend(True)
 
     if start == len(q.series) and opts.get('first'):
         ui.write(_("all patches applied\n"))
@@ -1979,7 +1979,7 @@
               existing=opts.get('existing'), force=opts.get('force'),
               rev=opts.get('rev'), git=opts.get('git'))
     finally:
-        q.save_dirty()
+        q.savedirty()
 
     if opts.get('push') and not opts.get('rev'):
         return q.push(repo, None)
@@ -1995,7 +1995,7 @@
     Returns 0 if initialization succeeded."""
     q = repo.mq
     r = q.init(repo, create)
-    q.save_dirty()
+    q.savedirty()
     if r:
         if not os.path.exists(r.wjoin('.hgignore')):
             fp = r.wopener('.hgignore', 'w')
@@ -2084,13 +2084,13 @@
         except error.RepoError:
             pass
     ui.note(_('cloning main repository\n'))
-    sr, dr = hg.clone(ui, sr.url(), dest,
+    sr, dr = hg.clone(ui, opts, sr.url(), dest,
                       pull=opts.get('pull'),
                       rev=destrev,
                       update=False,
                       stream=opts.get('uncompressed'))
     ui.note(_('cloning patch repository\n'))
-    hg.clone(ui, opts.get('patches') or patchdir(sr), patchdir(dr),
+    hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
              pull=opts.get('pull'), update=not opts.get('noupdate'),
              stream=opts.get('uncompressed'))
     if dr.local():
@@ -2132,7 +2132,7 @@
 
     Returns 0 on success."""
     q = repo.mq
-    t = q.applied and q.series_end(True) or 0
+    t = q.applied and q.seriesend(True) or 0
     if t:
         q.qseries(repo, start=t - 1, length=1, status='A',
                   summary=opts.get('summary'))
@@ -2146,7 +2146,7 @@
 
     Returns 0 on success."""
     q = repo.mq
-    end = q.series_end()
+    end = q.seriesend()
     if end == len(q.series):
         ui.write(_("all patches applied\n"))
         return 1
@@ -2222,7 +2222,7 @@
         opts['msg'] = msg
     setupheaderopts(ui, opts)
     q.new(repo, patch, *args, **opts)
-    q.save_dirty()
+    q.savedirty()
     return 0
 
 @command("^qrefresh",
@@ -2275,9 +2275,13 @@
         # We don't want to lose the patch message if qrefresh fails (issue2062)
         repo.savecommitmessage(message)
     setupheaderopts(ui, opts)
-    ret = q.refresh(repo, pats, msg=message, **opts)
-    q.save_dirty()
-    return ret
+    wlock = repo.wlock()
+    try:
+        ret = q.refresh(repo, pats, msg=message, **opts)
+        q.savedirty()
+        return ret
+    finally:
+        wlock.release()
 
 @command("^qdiff",
          commands.diffopts + commands.diffopts2 + commands.walkopts,
@@ -2324,9 +2328,9 @@
 
     if not files:
         raise util.Abort(_('qfold requires at least one patch name'))
-    if not q.check_toppatch(repo)[0]:
+    if not q.checktoppatch(repo)[0]:
         raise util.Abort(_('no patches applied'))
-    q.check_localchanges(repo)
+    q.checklocalchanges(repo)
 
     message = cmdutil.logmessage(opts)
     if opts.get('edit'):
@@ -2366,9 +2370,13 @@
         message = ui.edit(message, user or ui.username())
 
     diffopts = q.patchopts(q.diffopts(), *patches)
-    q.refresh(repo, msg=message, git=diffopts.git)
-    q.delete(repo, patches, opts)
-    q.save_dirty()
+    wlock = repo.wlock()
+    try:
+        q.refresh(repo, msg=message, git=diffopts.git)
+        q.delete(repo, patches, opts)
+        q.savedirty()
+    finally:
+        wlock.release()
 
 @command("qgoto",
          [('f', 'force', None, _('overwrite any local changes'))],
@@ -2383,7 +2391,7 @@
         ret = q.pop(repo, patch, force=opts.get('force'))
     else:
         ret = q.push(repo, patch, force=opts.get('force'))
-    q.save_dirty()
+    q.savedirty()
     return ret
 
 @command("qguard",
@@ -2412,7 +2420,7 @@
     Returns 0 on success.
     '''
     def status(idx):
-        guards = q.series_guards[idx] or ['unguarded']
+        guards = q.seriesguards[idx] or ['unguarded']
         if q.series[idx] in applied:
             state = 'applied'
         elif q.pushable(idx)[0]:
@@ -2451,11 +2459,11 @@
     if patch is None:
         raise util.Abort(_('no patch to work with'))
     if args or opts.get('none'):
-        idx = q.find_series(patch)
+        idx = q.findseries(patch)
         if idx is None:
             raise util.Abort(_('no patch named %s') % patch)
-        q.set_guards(idx, args)
-        q.save_dirty()
+        q.setguards(idx, args)
+        q.savedirty()
     else:
         status(q.series.index(q.lookup(patch)))
 
@@ -2561,7 +2569,7 @@
         q = repo.mq
     ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
                 all=opts.get('all'))
-    q.save_dirty()
+    q.savedirty()
     return ret
 
 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
@@ -2593,16 +2601,16 @@
     q.checkpatchname(name)
 
     ui.note(_('renaming %s to %s\n') % (patch, name))
-    i = q.find_series(patch)
-    guards = q.guard_re.findall(q.full_series[i])
-    q.full_series[i] = name + ''.join([' #' + g for g in guards])
-    q.parse_series()
-    q.series_dirty = 1
+    i = q.findseries(patch)
+    guards = q.guard_re.findall(q.fullseries[i])
+    q.fullseries[i] = name + ''.join([' #' + g for g in guards])
+    q.parseseries()
+    q.seriesdirty = 1
 
     info = q.isapplied(patch)
     if info:
         q.applied[info[0]] = statusentry(info[1], name)
-    q.applied_dirty = 1
+    q.applieddirty = 1
 
     destdir = os.path.dirname(absdest)
     if not os.path.isdir(destdir):
@@ -2624,7 +2632,7 @@
         finally:
             wlock.release()
 
-    q.save_dirty()
+    q.savedirty()
 
 @command("qrestore",
          [('d', 'delete', None, _('delete save entry')),
@@ -2638,7 +2646,7 @@
     q = repo.mq
     q.restore(repo, rev, delete=opts.get('delete'),
               qupdate=opts.get('update'))
-    q.save_dirty()
+    q.savedirty()
     return 0
 
 @command("qsave",
@@ -2657,7 +2665,7 @@
     ret = q.save(repo, msg=message)
     if ret:
         return ret
-    q.save_dirty()
+    q.savedirty()
     if opts.get('copy'):
         path = q.path
         if opts.get('name'):
@@ -2675,7 +2683,7 @@
         util.copyfiles(path, newpath)
     if opts.get('empty'):
         try:
-            os.unlink(q.join(q.status_path))
+            os.unlink(q.join(q.statuspath))
         except:
             pass
     return 0
@@ -2745,7 +2753,7 @@
         # refresh queue state if we're about to strip
         # applied patches
         if cl.rev(repo.lookup('qtip')) in strippedrevs:
-            q.applied_dirty = True
+            q.applieddirty = True
             start = 0
             end = len(q.applied)
             for i, statusentry in enumerate(q.applied):
@@ -2755,7 +2763,7 @@
                     start = i
                     break
             del q.applied[start:end]
-            q.save_dirty()
+            q.savedirty()
 
     revs = list(rootnodes)
     if update and opts.get('keep'):
@@ -2819,8 +2827,8 @@
         old_unapplied = q.unapplied(repo)
         old_guarded = [i for i in xrange(len(q.applied)) if
                        not q.pushable(i)[0]]
-        q.set_active(args)
-        q.save_dirty()
+        q.setactive(args)
+        q.savedirty()
         if not args:
             ui.status(_('guards deactivated\n'))
         if not opts.get('pop') and not opts.get('reapply'):
@@ -2838,7 +2846,7 @@
     elif opts.get('series'):
         guards = {}
         noguards = 0
-        for gs in q.series_guards:
+        for gs in q.seriesguards:
             if not gs:
                 noguards += 1
             for g in gs:
@@ -2881,7 +2889,7 @@
                 ui.status(_('reapplying unguarded patches\n'))
                 q.push(repo, reapply)
         finally:
-            q.save_dirty()
+            q.savedirty()
 
 @command("qfinish",
          [('a', 'applied', None, _('finish all applied changesets'))],
@@ -2916,7 +2924,7 @@
 
     revs = scmutil.revrange(repo, revrange)
     q.finish(repo, revs)
-    q.save_dirty()
+    q.savedirty()
     return 0
 
 @command("qqueue",
@@ -3095,7 +3103,7 @@
         def mq(self):
             return queue(self.ui, self.join(""))
 
-        def abort_if_wdir_patched(self, errmsg, force=False):
+        def abortifwdirpatched(self, errmsg, force=False):
             if self.mq.applied and not force:
                 parents = self.dirstate.parents()
                 patches = [s.node for s in self.mq.applied]
@@ -3104,7 +3112,7 @@
 
         def commit(self, text="", user=None, date=None, match=None,
                    force=False, editor=False, extra={}):
-            self.abort_if_wdir_patched(
+            self.abortifwdirpatched(
                 _('cannot commit over an applied mq patch'),
                 force)
 
@@ -3136,7 +3144,7 @@
 
             try:
                 self.changelog.rev(mqtags[-1][0])
-            except error.RepoLookupError:
+            except error.LookupError:
                 self.ui.warn(_('mq status file refers to unknown node %s\n')
                              % short(mqtags[-1][0]))
                 return result
@@ -3189,9 +3197,9 @@
         repo.__class__ = mqrepo
 
 def mqimport(orig, ui, repo, *args, **kwargs):
-    if (hasattr(repo, 'abort_if_wdir_patched')
+    if (hasattr(repo, 'abortifwdirpatched')
         and not kwargs.get('no_commit', False)):
-        repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
+        repo.abortifwdirpatched(_('cannot import over an applied patch'),
                                    kwargs.get('force'))
     return orig(ui, repo, *args, **kwargs)
 
--- a/hgext/notify.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/hgext/notify.py	Tue Jun 14 20:43:04 2011 -0500
@@ -21,6 +21,8 @@
   incoming.notify = python:hgext.notify.hook
   # batch emails when many changesets incoming at one time
   changegroup.notify = python:hgext.notify.hook
+  # batch emails when many changesets outgoing at one time (client side)
+  outgoing.notify = python:hgext.notify.hook
 
   [notify]
   # config items go here
@@ -37,7 +39,8 @@
   style = ...            # style file to use when formatting email
   template = ...         # template to use when formatting email
   incoming = ...         # template to use when run as incoming hook
-  changegroup = ...      # template when run as changegroup hook
+  outgoing = ...         # template to use when run as outgoing hook
+  changegroup = ...      # template to use when run as changegroup hook
   maxdiff = 300          # max lines of diffs to include (0=none, -1=all)
   maxsubject = 67        # truncate subject line longer than this
   diffstat = True        # add a diffstat before the diff content
@@ -290,7 +293,7 @@
     ui.pushbuffer()
     data = ''
     count = 0
-    if hooktype == 'changegroup':
+    if hooktype == 'changegroup' or hooktype == 'outgoing':
         start, end = ctx.rev(), len(repo)
         for rev in xrange(start, end):
             if n.node(repo[rev]):
--- a/hgext/patchbomb.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/hgext/patchbomb.py	Tue Jun 14 20:43:04 2011 -0500
@@ -276,7 +276,7 @@
         dest = ui.expandpath(dest or 'default-push', dest or 'default')
         dest, branches = hg.parseurl(dest)
         revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
-        other = hg.repository(hg.remoteui(repo, opts), dest)
+        other = hg.peer(repo, opts, dest)
         ui.status(_('comparing with %s\n') % util.hidepassword(dest))
         common, _anyinc, _heads = discovery.findcommonincoming(repo, other)
         nodes = revs and map(repo.lookup, revs) or revs
--- a/hgext/rebase.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/hgext/rebase.py	Tue Jun 14 20:43:04 2011 -0500
@@ -376,7 +376,7 @@
     'Update rebased mq patches - finalize and then import them'
     mqrebase = {}
     mq = repo.mq
-    original_series = mq.full_series[:]
+    original_series = mq.fullseries[:]
 
     for p in mq.applied:
         rev = repo[p.node].rev()
@@ -396,15 +396,10 @@
                 mq.qimport(repo, (), patchname=name, git=isgit,
                                 rev=[str(state[rev])])
 
-        # Restore missing guards
-        for s in original_series:
-            pname = mq.guard_re.split(s, 1)[0]
-            if pname in mq.full_series:
-                repo.ui.debug('restoring guard for patch %s' % (pname))
-                mq.full_series.remove(pname)
-                mq.full_series.append(s)
-                mq.series_dirty = True
-        mq.save_dirty()
+        # restore old series to preserve guards
+        mq.fullseries = original_series
+        mq.series_dirty = True
+        mq.savedirty()
 
 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
                                                                 external):
--- a/hgext/record.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/hgext/record.py	Tue Jun 14 20:43:04 2011 -0500
@@ -17,6 +17,15 @@
 
 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
 
+diffopts = [
+    ('w', 'ignore-all-space', False,
+     _('ignore white space when comparing lines')),
+    ('b', 'ignore-space-change', None,
+     _('ignore changes in the amount of white space')),
+    ('B', 'ignore-blank-lines', None,
+     _('ignore changes whose lines are all blank')),
+]
+
 def scanpatch(fp):
     """like patch.iterhunks, but yield different events
 
@@ -345,7 +354,8 @@
                if h[0].special() or len(h) > 1], [])
 
 @command("record",
-         commands.table['^commit|ci'][1], # same options as commit
+         # same options as commit + white space diff options
+         commands.table['^commit|ci'][1][:] + diffopts,
           _('hg record [OPTION]... [FILE]...'))
 def record(ui, repo, *pats, **opts):
     '''interactively select changes to commit
@@ -435,7 +445,10 @@
                                '(use "hg commit" instead)'))
 
         changes = repo.status(match=match)[:3]
-        diffopts = mdiff.diffopts(git=True, nodates=True)
+        diffopts = mdiff.diffopts(git=True, nodates=True,
+                                  ignorews=opts.get('ignore_all_space'),
+                                  ignorewsamount=opts.get('ignore_space_change'),
+                                  ignoreblanklines=opts.get('ignore_blank_lines'))
         chunks = patch.diff(repo, changes=changes, opts=diffopts)
         fp = cStringIO.StringIO()
         fp.write(''.join(chunks))
@@ -567,8 +580,8 @@
     cmdtable["qrecord"] = \
         (qrecord,
          # same options as qnew, but copy them so we don't get
-         # -i/--interactive for qrecord
-         mq.cmdtable['^qnew'][1][:],
+         # -i/--interactive for qrecord and add white space diff options
+         mq.cmdtable['^qnew'][1][:] + diffopts,
          _('hg qrecord [OPTION]... PATCH [FILE]...'))
 
     _wrapcmd('qnew', mq.cmdtable, qrecord, _("interactively record a new patch"))
--- a/hgext/relink.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/hgext/relink.py	Tue Jun 14 20:43:04 2011 -0500
@@ -38,9 +38,8 @@
     """
     if not hasattr(util, 'samefile') or not hasattr(util, 'samedevice'):
         raise util.Abort(_('hardlinks are not supported on this system'))
-    src = hg.repository(hg.remoteui(repo, opts),
-                        ui.expandpath(origin or 'default-relink',
-                                      origin or 'default'))
+    src = hg.repository(ui, ui.expandpath(origin or 'default-relink',
+                                          origin or 'default'))
     if not src.local():
         raise util.Abort(_('must specify local origin repository'))
     ui.status(_('relinking %s to %s\n') % (src.store.path, repo.store.path))
--- a/hgext/schemes.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/hgext/schemes.py	Tue Jun 14 20:43:04 2011 -0500
@@ -69,7 +69,7 @@
             tail = ''
         context = dict((str(i + 1), v) for i, v in enumerate(parts))
         url = ''.join(self.templater.process(self.url, context)) + tail
-        return hg._lookup(url).instance(ui, url, create)
+        return hg._peerlookup(url).instance(ui, url, create)
 
 def hasdriveletter(orig, path):
     for scheme in schemes:
--- a/hgext/transplant.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/hgext/transplant.py	Tue Jun 14 20:43:04 2011 -0500
@@ -227,7 +227,7 @@
             raise util.Abort(_('can only omit patchfile if merging'))
         if patchfile:
             try:
-                files = {}
+                files = set()
                 patch.patch(self.ui, repo, patchfile, files=files, eolmode=None)
                 files = list(files)
                 if not files:
@@ -561,7 +561,7 @@
 
     sourcerepo = opts.get('source')
     if sourcerepo:
-        source = hg.repository(ui, ui.expandpath(sourcerepo))
+        source = hg.peer(ui, opts, ui.expandpath(sourcerepo))
         branches = map(source.lookup, opts.get('branch', ()))
         source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, source,
                                     onlyheads=branches, force=True)
--- a/mercurial/bookmarks.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/bookmarks.py	Tue Jun 14 20:43:04 2011 -0500
@@ -114,7 +114,7 @@
     wlock = repo.wlock()
     try:
         file = repo.opener('bookmarks.current', 'w', atomictemp=True)
-        file.write(mark)
+        file.write(encoding.fromlocal(mark))
         file.rename()
     finally:
         wlock.release()
--- a/mercurial/commands.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/commands.py	Tue Jun 14 20:43:04 2011 -0500
@@ -891,7 +891,7 @@
     else:
         dest = ui.expandpath(dest or 'default-push', dest or 'default')
         dest, branches = hg.parseurl(dest, opts.get('branch'))
-        other = hg.repository(hg.remoteui(repo, opts), dest)
+        other = hg.peer(repo, opts, dest)
         revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
         heads = revs and map(repo.lookup, revs) or revs
         common, outheads = discovery.findcommonoutgoing(repo, other,
@@ -1026,7 +1026,7 @@
     if opts.get('noupdate') and opts.get('updaterev'):
         raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
 
-    r = hg.clone(hg.remoteui(ui, opts), source, dest,
+    r = hg.clone(ui, opts, source, dest,
                  pull=opts.get('pull'),
                  stream=opts.get('uncompressed'),
                  rev=opts.get('rev'),
@@ -1542,7 +1542,7 @@
 def debugdiscovery(ui, repo, remoteurl="default", **opts):
     """runs the changeset discovery protocol in isolation"""
     remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
-    remote = hg.repository(hg.remoteui(repo, opts), remoteurl)
+    remote = hg.peer(repo, opts, remoteurl)
     ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
 
     # make sure tests are repeatable
@@ -1603,6 +1603,10 @@
     if ui.verbose:
         tree = fileset.parse(expr)[0]
         ui.note(tree, "\n")
+    matcher = lambda x: scmutil.match(repo, x, default='glob')
+
+    for f in fileset.getfileset(repo[None], matcher, expr):
+        ui.write("%s\n" % f)
 
 @command('debugfsinfo', [], _('[PATH]'))
 def debugfsinfo(ui, path = "."):
@@ -1625,7 +1629,7 @@
     Every ID must be a full-length hex node id string. Saves the bundle to the
     given file.
     """
-    repo = hg.repository(ui, repopath)
+    repo = hg.peer(ui, opts, repopath)
     if not repo.capable('getbundle'):
         raise util.Abort("getbundle() not supported by target repository")
     args = {}
@@ -1800,14 +1804,14 @@
     Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
     indicating unknown/known.
     """
-    repo = hg.repository(ui, repopath)
+    repo = hg.peer(ui, opts, repopath)
     if not repo.capable('known'):
         raise util.Abort("known() not supported by target repository")
     flags = repo.known([bin(s) for s in ids])
     ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
 
 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
-def debugpushkey(ui, repopath, namespace, *keyinfo):
+def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
     '''access the pushkey key/value protocol
 
     With two args, list the keys in the given namespace.
@@ -1816,7 +1820,7 @@
     Reports success or failure.
     '''
 
-    target = hg.repository(ui, repopath)
+    target = hg.peer(ui, {}, repopath)
     if keyinfo:
         key, old, new = keyinfo
         r = target.pushkey(namespace, key, old, new)
@@ -2113,7 +2117,7 @@
     ] + remoteopts,
     _('REPO [OPTIONS]... [ONE [TWO]]'))
 def debugwireargs(ui, repopath, *vals, **opts):
-    repo = hg.repository(hg.remoteui(ui, opts), repopath)
+    repo = hg.peer(ui, opts, repopath)
     for opt in remoteopts:
         del opts[opt[1]]
     args = {}
@@ -2268,8 +2272,9 @@
 
     for f in m.files():
         if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
-            ui.warn(_('not removing %s: file is already untracked\n')
-                    % m.rel(f))
+            if os.path.exists(m.rel(f)):
+                ui.warn(_('not removing %s: file is already untracked\n')
+                        % m.rel(f))
             errs = 1
 
     for f in forget:
@@ -2910,7 +2915,7 @@
 
     if source:
         source, branches = hg.parseurl(ui.expandpath(source))
-        repo = hg.repository(ui, source)
+        repo = hg.peer(ui, {}, source)
         revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
 
     if not repo.local():
@@ -2997,6 +3002,8 @@
     ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
     ('', 'no-commit', None,
      _("don't commit, just update the working directory")),
+    ('', 'bypass', None,
+     _("apply patch without touching the working directory")),
     ('', 'exact', None,
      _('apply patch to the nodes from which it was generated')),
     ('', 'import-branch', None,
@@ -3030,6 +3037,11 @@
     the patch. This may happen due to character set problems or other
     deficiencies in the text patch format.
 
+    Use --bypass to apply and commit patches directly to the
+    repository, not touching the working directory. Without --exact,
+    patches will be applied on top of the working directory parent
+    revision.
+
     With -s/--similarity, hg will attempt to discover renames and
     copies in the patch in the same way as 'addremove'.
 
@@ -3045,14 +3057,19 @@
     if date:
         opts['date'] = util.parsedate(date)
 
+    update = not opts.get('bypass')
+    if not update and opts.get('no_commit'):
+        raise util.Abort(_('cannot use --no-commit with --bypass'))
     try:
         sim = float(opts.get('similarity') or 0)
     except ValueError:
         raise util.Abort(_('similarity must be a number'))
     if sim < 0 or sim > 100:
         raise util.Abort(_('similarity must be between 0 and 100'))
-
-    if opts.get('exact') or not opts.get('force'):
+    if sim and not update:
+        raise util.Abort(_('cannot use --similarity with --bypass'))
+
+    if (opts.get('exact') or not opts.get('force')) and update:
         cmdutil.bailifchanged(repo)
 
     d = opts["base"]
@@ -3060,7 +3077,12 @@
     wlock = lock = None
     msgs = []
 
-    def tryone(ui, hunk):
+    def checkexact(repo, n, nodeid):
+        if opts.get('exact') and hex(n) != nodeid:
+            repo.rollback()
+            raise util.Abort(_('patch is damaged or loses information'))
+
+    def tryone(ui, hunk, parents):
         tmpname, message, user, date, branch, nodeid, p1, p2 = \
             patch.extract(ui, hunk)
 
@@ -3081,53 +3103,77 @@
                 message = None
             ui.debug('message:\n%s\n' % message)
 
-            wp = repo.parents()
+            if len(parents) == 1:
+                parents.append(repo[nullid])
             if opts.get('exact'):
                 if not nodeid or not p1:
                     raise util.Abort(_('not a Mercurial patch'))
-                p1 = repo.lookup(p1)
-                p2 = repo.lookup(p2 or hex(nullid))
-
-                if p1 != wp[0].node():
-                    hg.clean(repo, p1)
-                repo.dirstate.setparents(p1, p2)
+                p1 = repo[p1]
+                p2 = repo[p2 or nullid]
             elif p2:
                 try:
-                    p1 = repo.lookup(p1)
-                    p2 = repo.lookup(p2)
-                    if p1 == wp[0].node():
-                        repo.dirstate.setparents(p1, p2)
+                    p1 = repo[p1]
+                    p2 = repo[p2]
                 except error.RepoError:
-                    pass
-            if opts.get('exact') or opts.get('import_branch'):
-                repo.dirstate.setbranch(branch or 'default')
-
-            files = {}
-            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:
-                    msgs.append(message)
+                    p1, p2 = parents
             else:
-                if opts.get('exact'):
-                    m = None
+                p1, p2 = parents
+
+            n = None
+            if update:
+                if opts.get('exact') and p1 != parents[0]:
+                    hg.clean(repo, p1.node())
+                if p1 != parents[0] and p2 != parents[1]:
+                    repo.dirstate.setparents(p1.node(), p2.node())
+
+                if opts.get('exact') or opts.get('import_branch'):
+                    repo.dirstate.setbranch(branch or 'default')
+
+                files = set()
+                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:
+                        msgs.append(message)
                 else:
-                    m = scmutil.matchfiles(repo, files or [])
-                n = repo.commit(message, opts.get('user') or user,
-                                opts.get('date') or date, match=m,
-                                editor=cmdutil.commiteditor)
-                if opts.get('exact'):
-                    if hex(n) != nodeid:
-                        repo.rollback()
-                        raise util.Abort(_('patch is damaged'
-                                           ' or loses information'))
-                # Force a dirstate write so that the next transaction
-                # backups an up-do-date file.
-                repo.dirstate.write()
-                if n:
-                    commitid = short(n)
-
+                    if opts.get('exact'):
+                        m = None
+                    else:
+                        m = scmutil.matchfiles(repo, files or [])
+                    n = repo.commit(message, opts.get('user') or user,
+                                    opts.get('date') or date, match=m,
+                                    editor=cmdutil.commiteditor)
+                    checkexact(repo, n, nodeid)
+                    # Force a dirstate write so that the next transaction
+                    # backups an up-to-date file.
+                    repo.dirstate.write()
+            else:
+                if opts.get('exact') or opts.get('import_branch'):
+                    branch = branch or 'default'
+                else:
+                    branch = p1.branch()
+                store = patch.filestore()
+                try:
+                    files = set()
+                    try:
+                        patch.patchrepo(ui, repo, p1, store, tmpname, strip,
+                                        files, eolmode=None)
+                    except patch.PatchError, e:
+                        raise util.Abort(str(e))
+                    memctx = patch.makememctx(repo, (p1.node(), p2.node()),
+                                              message,
+                                              opts.get('user') or user,
+                                              opts.get('date') or date,
+                                              branch, files, store,
+                                              editor=cmdutil.commiteditor)
+                    repo.savecommitmessage(memctx.description())
+                    n = memctx.commit()
+                    checkexact(repo, n, nodeid)
+                finally:
+                    store.close()
+            if n:
+                commitid = short(n)
             return commitid
         finally:
             os.unlink(tmpname)
@@ -3135,6 +3181,7 @@
     try:
         wlock = repo.wlock()
         lock = repo.lock()
+        parents = repo.parents()
         lastcommit = None
         for p in patches:
             pf = os.path.join(d, p)
@@ -3148,12 +3195,16 @@
 
             haspatch = False
             for hunk in patch.split(pf):
-                commitid = tryone(ui, hunk)
+                commitid = tryone(ui, hunk, parents)
                 if commitid:
                     haspatch = True
                     if lastcommit:
                         ui.status(_('applied %s\n') % lastcommit)
                     lastcommit = commitid
+                if update or opts.get('exact'):
+                    parents = repo.parents()
+                else:
+                    parents = [repo[commitid]]
 
             if not haspatch:
                 raise util.Abort(_('no diffs found'))
@@ -3195,7 +3246,7 @@
     if opts.get('bookmarks'):
         source, branches = hg.parseurl(ui.expandpath(source),
                                        opts.get('branch'))
-        other = hg.repository(hg.remoteui(repo, opts), source)
+        other = hg.peer(repo, opts, source)
         if 'bookmarks' not in other.listkeys('namespaces'):
             ui.warn(_("remote doesn't support bookmarks\n"))
             return 0
@@ -3223,7 +3274,7 @@
 
     Returns 0 on success.
     """
-    hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=True)
+    hg.peer(ui, opts, ui.expandpath(dest), create=True)
 
 @command('locate',
     [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
@@ -3557,7 +3608,7 @@
     if opts.get('bookmarks'):
         dest = ui.expandpath(dest or 'default-push', dest or 'default')
         dest, branches = hg.parseurl(dest, opts.get('branch'))
-        other = hg.repository(hg.remoteui(repo, opts), dest)
+        other = hg.peer(repo, opts, dest)
         if 'bookmarks' not in other.listkeys('namespaces'):
             ui.warn(_("remote doesn't support bookmarks\n"))
             return 0
@@ -3709,7 +3760,7 @@
     Returns 0 on success, 1 if an update had unresolved files.
     """
     source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
-    other = hg.repository(hg.remoteui(repo, opts), source)
+    other = hg.peer(repo, opts, source)
     ui.status(_('pulling from %s\n') % util.hidepassword(source))
     revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
 
@@ -3806,7 +3857,7 @@
     dest, branches = hg.parseurl(dest, opts.get('branch'))
     ui.status(_('pushing to %s\n') % util.hidepassword(dest))
     revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
-    other = hg.repository(hg.remoteui(repo, opts), dest)
+    other = hg.peer(repo, opts, dest)
     if revs:
         revs = [repo.lookup(rev) for rev in revs]
 
@@ -3913,7 +3964,8 @@
 
     for f in m.files():
         if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
-            ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
+            if os.path.exists(m.rel(f)):
+                ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
             ret = 1
 
     if force:
@@ -4086,44 +4138,29 @@
     ] + walkopts + dryrunopts,
     _('[OPTION]... [-r REV] [NAME]...'))
 def revert(ui, repo, *pats, **opts):
-    """restore individual files or directories to an earlier state
+    """restore files to their checkout state
 
     .. note::
-       This command is most likely not what you are looking for.
-       Revert will partially overwrite content in the working
-       directory without changing the working directory parents. Use
-       :hg:`update -r rev` to check out earlier revisions, or
-       :hg:`update --clean .` to undo a merge which has added another
-       parent.
-
-    With no revision specified, revert the named files or directories
-    to the contents they had in the parent of the working directory.
-    This restores the contents of the affected files to an unmodified
-    state and unschedules adds, removes, copies, and renames. If the
-    working directory has two parents, you must explicitly specify a
-    revision.
-
-    Using the -r/--rev option, revert the given files or directories
-    to their contents as of a specific revision. This can be helpful
-    to "roll back" some or all of an earlier change. See :hg:`help
-    dates` for a list of formats valid for -d/--date.
-
-    Revert modifies the working directory. It does not commit any
-    changes, or change the parent of the working directory. If you
-    revert to a revision other than the parent of the working
-    directory, the reverted files will thus appear modified
-    afterwards.
-
-    If a file has been deleted, it is restored. Files scheduled for
-    addition are just unscheduled and left as they are. If the
-    executable mode of a file was changed, it is reset.
-
-    If names are given, all files matching the names are reverted.
-    If no arguments are given, no files are reverted.
+       To check out earlier revisions, you should use :hg:`update REV`.
+       To cancel a merge (and lose your changes), use :hg:`update --clean .`.
+
+    With no revision specified, revert the specified files or directories
+    to the state they had in the first parent of the working directory.
+    This restores the contents of files to an unmodified
+    state and unschedules adds, removes, copies, and renames.
+
+    Using the -r/--rev or -d/--date options, revert the given files or
+    directories to their states as of a specific revision. Because
+    revert does not change the working directory parents, this will
+    cause these files to appear modified. This can be helpful to "back
+    out" some or all of an earlier change. See :hg:`backout` for a
+    related method.
 
     Modified files are saved with a .orig suffix before reverting.
     To disable these backups, use --no-backup.
 
+    See :hg:`help dates` for a list of formats valid for -d/--date.
+
     Returns 0 on success.
     """
 
@@ -4133,13 +4170,10 @@
         opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
 
     parent, p2 = repo.dirstate.parents()
-    if not opts.get('rev') and p2 != nullid:
-        raise util.Abort(_('uncommitted merge - '
-                           'use "hg update", see "hg help revert"'))
 
     if not pats and not opts.get('all'):
-        raise util.Abort(_('no files or directories specified; '
-                           'use --all to revert the whole repo'))
+        raise util.Abort(_('no files or directories specified'),
+                         hint=_('use --all to revert all files'))
 
     ctx = scmutil.revsingle(repo, opts.get('rev'))
     node = ctx.node()
@@ -4756,7 +4790,7 @@
     if opts.get('remote'):
         t = []
         source, branches = hg.parseurl(ui.expandpath('default'))
-        other = hg.repository(hg.remoteui(repo, {}), source)
+        other = hg.peer(repo, {}, source)
         revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
         ui.debug('comparing with %s\n' % util.hidepassword(source))
         repo.ui.pushbuffer()
@@ -4769,7 +4803,7 @@
         dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
         revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
         if source != dest:
-            other = hg.repository(hg.remoteui(repo, {}), dest)
+            other = hg.peer(repo, {}, dest)
             commoninc = None
             ui.debug('comparing with %s\n' % util.hidepassword(dest))
         repo.ui.pushbuffer()
--- a/mercurial/dispatch.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/dispatch.py	Tue Jun 14 20:43:04 2011 -0500
@@ -12,34 +12,55 @@
 import ui as uimod
 
 class request(object):
-    def __init__(self, args, ui=None, repo=None):
+    def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None):
         self.args = args
         self.ui = ui
         self.repo = repo
 
+        # input/output/error streams
+        self.fin = fin
+        self.fout = fout
+        self.ferr = ferr
+
 def run():
     "run the command in sys.argv"
     sys.exit(dispatch(request(sys.argv[1:])))
 
 def dispatch(req):
     "run the command specified in req.args"
+    if req.ferr:
+        ferr = req.ferr
+    elif req.ui:
+        ferr = req.ui.ferr
+    else:
+        ferr = sys.stderr
+
     try:
         if not req.ui:
             req.ui = uimod.ui()
         if '--traceback' in req.args:
             req.ui.setconfig('ui', 'traceback', 'on')
+
+        # set ui streams from the request
+        if req.fin:
+            req.ui.fin = req.fin
+        if req.fout:
+            req.ui.fout = req.fout
+        if req.ferr:
+            req.ui.ferr = req.ferr
     except util.Abort, inst:
-        sys.stderr.write(_("abort: %s\n") % inst)
+        ferr.write(_("abort: %s\n") % inst)
         if inst.hint:
-            sys.stderr.write(_("(%s)\n") % inst.hint)
+            ferr.write(_("(%s)\n") % inst.hint)
         return -1
     except error.ParseError, inst:
         if len(inst.args) > 1:
-            sys.stderr.write(_("hg: parse error at %s: %s\n") %
+            ferr.write(_("hg: parse error at %s: %s\n") %
                              (inst.args[1], inst.args[0]))
         else:
-            sys.stderr.write(_("hg: parse error: %s\n") % inst.args[0])
+            ferr.write(_("hg: parse error: %s\n") % inst.args[0])
         return -1
+
     return _runcatch(req)
 
 def _runcatch(req):
@@ -572,16 +593,20 @@
         atexit.register(print_time)
 
     if options['verbose'] or options['debug'] or options['quiet']:
-        ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
-        ui.setconfig('ui', 'debug', str(bool(options['debug'])))
-        ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
+        for ui_ in (ui, lui):
+            ui_.setconfig('ui', 'verbose', str(bool(options['verbose'])))
+            ui_.setconfig('ui', 'debug', str(bool(options['debug'])))
+            ui_.setconfig('ui', 'quiet', str(bool(options['quiet'])))
     if options['traceback']:
-        ui.setconfig('ui', 'traceback', 'on')
+        for ui_ in (ui, lui):
+            ui_.setconfig('ui', 'traceback', 'on')
     if options['noninteractive']:
-        ui.setconfig('ui', 'interactive', 'off')
+        for ui_ in (ui, lui):
+            ui_.setconfig('ui', 'interactive', 'off')
 
     if cmdoptions.get('insecure', False):
-        ui.setconfig('web', 'cacerts', '')
+        for ui_ in (ui, lui):
+            ui_.setconfig('web', 'cacerts', '')
 
     if options['help']:
         return commands.help_(ui, cmd, options['version'])
--- a/mercurial/extensions.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/extensions.py	Tue Jun 14 20:43:04 2011 -0500
@@ -262,6 +262,14 @@
 
 def disabled():
     '''find disabled extensions from hgext. returns a dict of {name: desc}'''
+    try:
+        from hgext import __index__
+        return dict((name, gettext(desc))
+                    for name, desc in __index__.docs.iteritems()
+                    if name not in _order)
+    except ImportError:
+        pass
+
     paths = _disabledpaths()
     if not paths:
         return None
@@ -276,6 +284,15 @@
 
 def disabledext(name):
     '''find a specific disabled extension from hgext. returns desc'''
+    try:
+        from hgext import __index__
+        if name in _order:  # enabled
+            return
+        else:
+            return gettext(__index__.docs.get(name))
+    except ImportError:
+        pass
+
     paths = _disabledpaths()
     if name in paths:
         return _disabledhelp(paths[name])
--- a/mercurial/fileset.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/fileset.py	Tue Jun 14 20:43:04 2011 -0500
@@ -27,6 +27,8 @@
 
 keywords = set(['and', 'or', 'not'])
 
+globchars = ".*{}[]?/\\"
+
 def tokenize(program):
     pos, l = 0, len(program)
     while pos < l:
@@ -56,13 +58,13 @@
                 pos += 1
             else:
                 raise error.ParseError(_("unterminated string"), s)
-        elif c.isalnum() or c in '.*{}[]?' or ord(c) > 127:
+        elif c.isalnum() or c in globchars or ord(c) > 127:
             # gather up a symbol/keyword
             s = pos
             pos += 1
             while pos < l: # find end of symbol
                 d = program[pos]
-                if not (d.isalnum() or d in ".*{}[]?," or ord(d) > 127):
+                if not (d.isalnum() or d in globchars or ord(d) > 127):
                     break
                 pos += 1
             sym = program[s:pos]
@@ -78,3 +80,63 @@
 
 parse = parser.parser(tokenize, elements).parse
 
+def getstring(x, err):
+    if x and (x[0] == 'string' or x[0] == 'symbol'):
+        return x[1]
+    raise error.ParseError(err)
+
+def getset(mctx, x):
+    if not x:
+        raise error.ParseError(_("missing argument"))
+    return methods[x[0]](mctx, *x[1:])
+
+def stringset(mctx, x):
+    m = mctx.matcher([x])
+    return [f for f in mctx.subset if m(f)]
+
+def andset(mctx, x, y):
+    return getset(mctx.narrow(getset(mctx, x)), y)
+
+def orset(mctx, x, y):
+    # needs optimizing
+    xl = getset(mctx, x)
+    yl = getset(mctx, y)
+    return xl + [f for f in yl if f not in xl]
+
+def notset(mctx, x):
+    s = set(getset(mctx, x))
+    return [r for r in mctx.subset if r not in s]
+
+def listset(mctx, a, b):
+    raise error.ParseError(_("can't use a list in this context"))
+
+methods = {
+    'string': stringset,
+    'symbol': stringset,
+    'and': andset,
+    'or': orset,
+    'list': listset,
+    'group': getset,
+    'not': notset
+}
+
+class matchctx(object):
+    def __init__(self, ctx, matchfn, subset=None):
+        self.ctx = ctx
+        self.matchfn = matchfn
+        self.subset = subset
+        if subset is None:
+            self.subset = ctx.walk(matchfn([])) # optimize this later
+    def matcher(self, pattern):
+        return self.matchfn(pattern)
+    def filter(self, files):
+        return [f for f in files if f in self.subset]
+    def narrow(self, files):
+        return matchctx(self.ctx, self.matchfn,
+                        self.filter(files))
+
+def getfileset(ctx, matchfn, expr):
+    tree, pos = parse(expr)
+    if (pos != len(expr)):
+        raise error.ParseError("invalid token", pos)
+    return getset(matchctx(ctx, matchfn), tree)
--- a/mercurial/hg.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/hg.py	Tue Jun 14 20:43:04 2011 -0500
@@ -70,7 +70,7 @@
     'static-http': statichttprepo,
 }
 
-def _lookup(path):
+def _peerlookup(path):
     u = util.url(path)
     scheme = u.scheme or 'file'
     thing = schemes.get(scheme) or schemes['file']
@@ -83,14 +83,14 @@
     '''return true if repo or path is local'''
     if isinstance(repo, str):
         try:
-            return _lookup(repo).islocal(repo)
+            return _peerlookup(repo).islocal(repo)
         except AttributeError:
             return False
     return repo.local()
 
 def repository(ui, path='', create=False):
     """return a repository object for the specified path"""
-    repo = _lookup(path).instance(ui, path, create)
+    repo = _peerlookup(path).instance(ui, path, create)
     ui = getattr(repo, "ui", ui)
     for name, module in extensions.extensions():
         hook = getattr(module, 'reposetup', None)
@@ -98,6 +98,11 @@
             hook(ui, repo)
     return repo
 
+def peer(ui, opts, path, create=False):
+    '''return a repository peer for the specified path'''
+    rui = remoteui(ui, opts)
+    return _peerlookup(path).instance(rui, path, create)
+
 def defaultdest(source):
     '''return default destination of clone if none is given'''
     return os.path.basename(os.path.normpath(source))
@@ -169,8 +174,8 @@
                 continue
         _update(r, uprev)
 
-def clone(ui, source, dest=None, pull=False, rev=None, update=True,
-          stream=False, branch=None):
+def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
+          update=True, stream=False, branch=None):
     """Make a copy of an existing repository.
 
     Create a copy of an existing repository in a new directory.  The
@@ -209,7 +214,7 @@
     if isinstance(source, str):
         origsource = ui.expandpath(source)
         source, branch = parseurl(origsource, branch)
-        srcrepo = repository(ui, source)
+        srcrepo = repository(remoteui(ui, peeropts), source)
     else:
         srcrepo = source
         branch = (None, branch or [])
@@ -303,12 +308,13 @@
 
             # we need to re-init the repo after manually copying the data
             # into it
-            destrepo = repository(ui, dest)
+            destrepo = repository(remoteui(ui, peeropts), dest)
             srcrepo.hook('outgoing', source='clone',
                           node=node.hex(node.nullid))
         else:
             try:
-                destrepo = repository(ui, dest, create=True)
+                destrepo = repository(remoteui(ui, peeropts), dest,
+                                      create=True)
             except OSError, inst:
                 if inst.errno == errno.EEXIST:
                     dircleanup.close()
@@ -318,7 +324,7 @@
 
             revs = None
             if rev:
-                if 'lookup' not in srcrepo.capabilities:
+                if not srcrepo.capable('lookup'):
                     raise util.Abort(_("src repository does not support "
                                        "revision lookup and so doesn't "
                                        "support clone by revision"))
@@ -423,7 +429,7 @@
     and is supposed to contain only code that can't be unified.
     """
     source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
-    other = repository(remoteui(repo, opts), source)
+    other = peer(repo, opts, source)
     ui.status(_('comparing with %s\n') % util.hidepassword(source))
     revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
 
@@ -481,7 +487,7 @@
     if revs:
         revs = [repo.lookup(rev) for rev in revs]
 
-    other = repository(remoteui(repo, opts), dest)
+    other = peer(repo, opts, dest)
     common, outheads = discovery.findcommonoutgoing(repo, other, revs,
                                                     force=opts.get('force'))
     o = repo.changelog.findmissing(common, outheads)
--- a/mercurial/hgweb/protocol.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/hgweb/protocol.py	Tue Jun 14 20:43:04 2011 -0500
@@ -5,16 +5,17 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import cgi, cStringIO, zlib, sys, urllib
+import cgi, cStringIO, zlib, urllib
 from mercurial import util, wireproto
 from common import HTTP_OK
 
 HGTYPE = 'application/mercurial-0.1'
 
 class webproto(object):
-    def __init__(self, req):
+    def __init__(self, req, ui):
         self.req = req
         self.response = ''
+        self.ui = ui
     def getargs(self, args):
         knownargs = self._args()
         data = {}
@@ -46,8 +47,12 @@
         for s in util.filechunkiter(self.req, limit=length):
             fp.write(s)
     def redirect(self):
-        self.oldio = sys.stdout, sys.stderr
-        sys.stderr = sys.stdout = cStringIO.StringIO()
+        self.oldio = self.ui.fout, self.ui.ferr
+        self.ui.ferr = self.ui.fout = cStringIO.StringIO()
+    def restore(self):
+        val = self.ui.fout.getvalue()
+        self.ui.ferr, self.ui.fout = self.oldio
+        return val
     def groupchunks(self, cg):
         z = zlib.compressobj()
         while True:
@@ -66,7 +71,7 @@
     return cmd in wireproto.commands
 
 def call(repo, req, cmd):
-    p = webproto(req)
+    p = webproto(req, repo.ui)
     rsp = wireproto.dispatch(repo, p, cmd)
     if isinstance(rsp, str):
         req.respond(HTTP_OK, HGTYPE, length=len(rsp))
@@ -75,14 +80,13 @@
         req.respond(HTTP_OK, HGTYPE)
         return rsp.gen
     elif isinstance(rsp, wireproto.pushres):
-        val = sys.stdout.getvalue()
-        sys.stdout, sys.stderr = p.oldio
+        val = p.restore()
         req.respond(HTTP_OK, HGTYPE)
         return ['%d\n%s' % (rsp.res, val)]
     elif isinstance(rsp, wireproto.pusherr):
         # drain the incoming bundle
         req.drain()
-        sys.stdout, sys.stderr = p.oldio
+        p.restore()
         rsp = '0\n%s\n' % rsp.res
         req.respond(HTTP_OK, HGTYPE, length=len(rsp))
         return [rsp]
--- a/mercurial/hgweb/webcommands.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/hgweb/webcommands.py	Tue Jun 14 20:43:04 2011 -0500
@@ -271,7 +271,8 @@
     diffs = webutil.diffs(web.repo, tmpl, ctx, None, parity, style)
 
     parity = paritygen(web.stripecount)
-    diffstat = webutil.diffstat(tmpl, ctx, parity)
+    diffstatgen = webutil.diffstatgen(ctx)
+    diffstat = webutil.diffstat(tmpl, ctx, diffstatgen, parity)
 
     return tmpl('changeset',
                 diff=diffs,
@@ -286,6 +287,7 @@
                 desc=ctx.description(),
                 date=ctx.date(),
                 files=files,
+                diffsummary=lambda **x: webutil.diffsummary(diffstatgen),
                 diffstat=diffstat,
                 archives=web.archivelist(ctx.hex()),
                 tags=webutil.nodetagsdict(web.repo, ctx.node()),
--- a/mercurial/hgweb/webutil.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/hgweb/webutil.py	Tue Jun 14 20:43:04 2011 -0500
@@ -8,6 +8,7 @@
 
 import os, copy
 from mercurial import match, patch, scmutil, error, ui, util
+from mercurial.i18n import _
 from mercurial.node import hex, nullid
 
 def up(p):
@@ -211,25 +212,40 @@
     yield tmpl('diffblock', parity=parity.next(),
                lines=prettyprintlines(''.join(block)))
 
-def diffstat(tmpl, ctx, parity):
-    '''Return a diffstat template for each file in the cset.'''
+def diffstatgen(ctx):
+    '''Generator function that provides the diffstat data.'''
 
     stats = patch.diffstatdata(util.iterlines(ctx.diff()))
     maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
+    while True:
+        yield stats, maxname, maxtotal, addtotal, removetotal, binary
 
-    statsdict = {}
-    if maxtotal > 0:
-        for filename, adds, removes, isbinary in stats:
-            total = adds + removes
-            addpct = (float(adds) / maxtotal) * 100
-            removepct = (float(removes) / maxtotal) * 100
-            statsdict[filename] = (total, addpct, removepct)
+def diffsummary(statgen):
+    '''Return a short summary of the diff.'''
+
+    stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
+    return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
+             len(stats), addtotal, removetotal)
+
+def diffstat(tmpl, ctx, statgen, parity):
+    '''Return a diffstat template for each file in the diff.'''
+
+    stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next()
+    files = ctx.files()
 
-    for f in ctx.files():
-        template = f in ctx and 'diffstatlink' or 'diffstatnolink'
-        total, addpct, removepct = statsdict.get(f, ('', 0, 0))
-        yield tmpl(template, node=ctx.hex(), file=f, total=total,
-            addpct=addpct, removepct=removepct, parity=parity.next())
+    def pct(i):
+        if maxtotal == 0:
+            return 0
+        return (float(i) / maxtotal) * 100
+
+    fileno = 0
+    for filename, adds, removes, isbinary in stats:
+        template = filename in files and 'diffstatlink' or 'diffstatnolink'
+        total = adds + removes
+        fileno += 1
+        yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno,
+                   total=total, addpct=pct(adds), removepct=pct(removes),
+                   parity=parity.next())
 
 class sessionvars(object):
     def __init__(self, vars, start='?'):
--- a/mercurial/localrepo.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/localrepo.py	Tue Jun 14 20:43:04 2011 -0500
@@ -563,7 +563,7 @@
         return [(n in nm) for n in nodes]
 
     def local(self):
-        return True
+        return self
 
     def join(self, f):
         return os.path.join(self.path, f)
@@ -1456,7 +1456,7 @@
 
     def changegroupsubset(self, bases, heads, source):
         """Compute a changegroup consisting of all the nodes that are
-        descendents of any of the bases and ancestors of any of the heads.
+        descendants of any of the bases and ancestors of any of the heads.
         Return a chunkbuffer object whose read() method will return
         successive changegroup chunks.
 
--- a/mercurial/patch.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/patch.py	Tue Jun 14 20:43:04 2011 -0500
@@ -11,7 +11,8 @@
 
 from i18n import _
 from node import hex, nullid, short
-import base85, mdiff, scmutil, util, diffhelpers, copies, encoding
+import base85, mdiff, scmutil, util, diffhelpers, copies, encoding, error
+import context
 
 gitre = re.compile('diff --git a/(.*) b/(.*)')
 
@@ -281,6 +282,14 @@
         isexec = mode & 0100
         self.mode = (islink, isexec)
 
+    def copy(self):
+        other = patchmeta(self.path)
+        other.oldpath = self.oldpath
+        other.mode = self.mode
+        other.op = self.op
+        other.binary = self.binary
+        return other
+
     def __repr__(self):
         return "<patchmeta %s %r>" % (self.op, self.path)
 
@@ -483,7 +492,7 @@
         self.files = {}
         self.created = 0
 
-    def setfile(self, fname, data, mode):
+    def setfile(self, fname, data, mode, copied=None):
         if self.opener is None:
             root = tempfile.mkdtemp(prefix='hg-patch-')
             self.opener = scmutil.opener(root)
@@ -491,27 +500,68 @@
         fn = str(self.created)
         self.opener.write(fn, data)
         self.created += 1
-        self.files[fname] = (fn, mode)
+        self.files[fname] = (fn, mode, copied)
 
     def getfile(self, fname):
         if fname not in self.files:
             raise IOError()
-        fn, mode = self.files[fname]
-        return self.opener.read(fn), mode
+        fn, mode, copied = self.files[fname]
+        return self.opener.read(fn), mode, copied
 
     def close(self):
         if self.opener:
             shutil.rmtree(self.opener.base)
 
+class repobackend(abstractbackend):
+    def __init__(self, ui, repo, ctx, store):
+        super(repobackend, self).__init__(ui)
+        self.repo = repo
+        self.ctx = ctx
+        self.store = store
+        self.changed = set()
+        self.removed = set()
+        self.copied = {}
+
+    def _checkknown(self, fname):
+        if fname not in self.ctx:
+            raise PatchError(_('cannot patch %s: file is not tracked') % fname)
+
+    def getfile(self, fname):
+        try:
+            fctx = self.ctx[fname]
+        except error.LookupError:
+            raise IOError()
+        flags = fctx.flags()
+        return fctx.data(), ('l' in flags, 'x' in flags)
+
+    def setfile(self, fname, data, mode, copysource):
+        if copysource:
+            self._checkknown(copysource)
+        if data is None:
+            data = self.ctx[fname].data()
+        self.store.setfile(fname, data, mode, copysource)
+        self.changed.add(fname)
+        if copysource:
+            self.copied[fname] = copysource
+
+    def unlink(self, fname):
+        self._checkknown(fname)
+        self.removed.add(fname)
+
+    def exists(self, fname):
+        return fname in self.ctx
+
+    def close(self):
+        return self.changed | self.removed
+
 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
 eolmodes = ['strict', 'crlf', 'lf', 'auto']
 
 class patchfile(object):
-    def __init__(self, ui, fname, backend, store, mode, create, remove,
-                 eolmode='strict', copysource=None):
-        self.fname = fname
+    def __init__(self, ui, gp, backend, store, eolmode='strict'):
+        self.fname = gp.path
         self.eolmode = eolmode
         self.eol = None
         self.backend = backend
@@ -519,17 +569,17 @@
         self.lines = []
         self.exists = False
         self.missing = True
-        self.mode = mode
-        self.copysource = copysource
-        self.create = create
-        self.remove = remove
+        self.mode = gp.mode
+        self.copysource = gp.oldpath
+        self.create = gp.op in ('ADD', 'COPY', 'RENAME')
+        self.remove = gp.op == 'DELETE'
         try:
-            if copysource is None:
-                data, mode = backend.getfile(fname)
+            if self.copysource is None:
+                data, mode = backend.getfile(self.fname)
                 self.exists = True
             else:
-                data, mode = store.getfile(copysource)
-                self.exists = backend.exists(fname)
+                data, mode = store.getfile(self.copysource)[:2]
+                self.exists = backend.exists(self.fname)
             self.missing = False
             if data:
                 self.lines = data.splitlines(True)
@@ -549,7 +599,7 @@
                         nlines.append(l)
                     self.lines = nlines
         except IOError:
-            if create:
+            if self.create:
                 self.missing = False
             if self.mode is None:
                 self.mode = (False, False)
@@ -1016,14 +1066,7 @@
         count -= 1
     return path[:i].lstrip(), path[i:].rstrip()
 
-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]
-        create = gp.op in ('ADD', 'COPY', 'RENAME')
-        remove = gp.op == 'DELETE'
-        return fname, create, remove
+def makepatchmeta(backend, afile_orig, bfile_orig, hunk, strip):
     nulla = afile_orig == "/dev/null"
     nullb = bfile_orig == "/dev/null"
     create = nulla and hunk.starta == 0 and hunk.lena == 0
@@ -1065,7 +1108,12 @@
         else:
             raise PatchError(_("undefined source and destination files"))
 
-    return fname, create, remove
+    gp = patchmeta(fname)
+    if create:
+        gp.op = 'ADD'
+    elif remove:
+        gp.op = 'DELETE'
+    return gp
 
 def scangitpatch(lr, firstline):
     """
@@ -1134,7 +1182,7 @@
             hunknum += 1
             if emitfile:
                 emitfile = False
-                yield 'file', (afile, bfile, h, gp)
+                yield 'file', (afile, bfile, h, gp and gp.copy() or None)
             yield 'hunk', h
         elif x.startswith('diff --git'):
             m = gitre.match(x)
@@ -1144,14 +1192,14 @@
                 # scan whole input for git metadata
                 gitpatches = [('a/' + gp.path, 'b/' + gp.path, gp) for gp
                               in scangitpatch(lr, x)]
-                yield 'git', [g[2] for g in gitpatches
+                yield 'git', [g[2].copy() for g in gitpatches
                               if g[2].op in ('COPY', 'RENAME')]
                 gitpatches.reverse()
             afile = 'a/' + m.group(1)
             bfile = 'b/' + m.group(2)
             while afile != gitpatches[-1][0] and bfile != gitpatches[-1][1]:
                 gp = gitpatches.pop()[2]
-                yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp)
+                yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
             gp = gitpatches[-1][2]
             # copy/rename + modify should modify target, not source
             if gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD') or gp.mode:
@@ -1191,23 +1239,22 @@
 
     while gitpatches:
         gp = gitpatches.pop()[2]
-        yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp)
+        yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
 
-def applydiff(ui, fp, changed, backend, store, strip=1, eolmode='strict'):
+def applydiff(ui, fp, backend, store, strip=1, eolmode='strict'):
     """Reads a patch from fp and tries to apply it.
 
-    The dict 'changed' is filled in with all of the filenames changed
-    by the patch. Returns 0 for a clean patch, -1 if any rejects were
-    found and 1 if there was any fuzz.
+    Returns 0 for a clean patch, -1 if any rejects were found and 1 if
+    there was any fuzz.
 
     If 'eolmode' is 'strict', the patch content and patched file are
     read in binary mode. Otherwise, line endings are ignored when
     patching then normalized according to 'eolmode'.
     """
-    return _applydiff(ui, fp, patchfile, backend, store, changed, strip=strip,
+    return _applydiff(ui, fp, patchfile, backend, store, strip=strip,
                       eolmode=eolmode)
 
-def _applydiff(ui, fp, patcher, backend, store, changed, strip=1,
+def _applydiff(ui, fp, patcher, backend, store, strip=1,
                eolmode='strict'):
 
     def pstrip(p):
@@ -1222,51 +1269,45 @@
             if not current_file:
                 continue
             ret = current_file.apply(values)
-            if ret >= 0:
-                changed.setdefault(current_file.fname, None)
-                if ret > 0:
-                    err = 1
+            if ret > 0:
+                err = 1
         elif state == 'file':
             if current_file:
                 rejects += current_file.close()
                 current_file = None
             afile, bfile, first_hunk, gp = values
-            copysource = None
             if gp:
                 path = pstrip(gp.path)
+                gp.path = pstrip(gp.path)
                 if gp.oldpath:
-                    copysource = pstrip(gp.oldpath)
-                changed[path] = gp
-                if gp.op == 'RENAME':
-                    backend.unlink(copysource)
-                if not first_hunk:
-                    if gp.op == 'DELETE':
-                        backend.unlink(path)
-                        continue
-                    data, mode = None, None
-                    if gp.op in ('RENAME', 'COPY'):
-                        data, mode = store.getfile(copysource)
-                    if gp.mode:
-                        mode = gp.mode
-                        if gp.op == 'ADD':
-                            # Added files without content have no hunk and
-                            # must be created
-                            data = ''
-                    if data or mode:
-                        if (gp.op in ('ADD', 'RENAME', 'COPY')
-                            and backend.exists(path)):
-                            raise PatchError(_("cannot create %s: destination "
-                                               "already exists") % path)
-                        backend.setfile(path, data, mode, copysource)
+                    gp.oldpath = pstrip(gp.oldpath)
+            else:
+                gp = makepatchmeta(backend, afile, bfile, first_hunk, strip)
+            if gp.op == 'RENAME':
+                backend.unlink(gp.oldpath)
             if not first_hunk:
+                if gp.op == 'DELETE':
+                    backend.unlink(gp.path)
+                    continue
+                data, mode = None, None
+                if gp.op in ('RENAME', 'COPY'):
+                    data, mode = store.getfile(gp.oldpath)[:2]
+                if gp.mode:
+                    mode = gp.mode
+                    if gp.op == 'ADD':
+                        # Added files without content have no hunk and
+                        # must be created
+                        data = ''
+                if data or mode:
+                    if (gp.op in ('ADD', 'RENAME', 'COPY')
+                        and backend.exists(gp.path)):
+                        raise PatchError(_("cannot create %s: destination "
+                                           "already exists") % gp.path)
+                    backend.setfile(gp.path, data, mode, gp.oldpath)
                 continue
             try:
-                mode = gp and gp.mode or None
-                current_file, create, remove = selectfile(
-                    backend, afile, bfile, first_hunk, strip, gp)
-                current_file = patcher(ui, current_file, backend, store, mode,
-                                       create, remove, eolmode=eolmode,
-                                       copysource=copysource)
+                current_file = patcher(ui, gp, backend, store,
+                                       eolmode=eolmode)
             except PatchError, inst:
                 ui.warn(str(inst) + '\n')
                 current_file = None
@@ -1306,7 +1347,7 @@
             if line.startswith('patching file '):
                 pf = util.parsepatchoutput(line)
                 printed_file = False
-                files.setdefault(pf, None)
+                files.add(pf)
             elif line.find('with fuzz') >= 0:
                 fuzz = True
                 if not printed_file:
@@ -1334,13 +1375,9 @@
                          util.explainexit(code)[0])
     return fuzz
 
-def internalpatch(ui, repo, patchobj, strip, files=None, eolmode='strict',
-                  similarity=0):
-    """use builtin patch to apply <patchobj> to the working directory.
-    returns whether patch was applied with fuzz factor."""
-
+def patchbackend(ui, backend, patchobj, strip, files=None, eolmode='strict'):
     if files is None:
-        files = {}
+        files = set()
     if eolmode is None:
         eolmode = ui.config('patch', 'eol', 'strict')
     if eolmode.lower() not in eolmodes:
@@ -1348,23 +1385,49 @@
     eolmode = eolmode.lower()
 
     store = filestore()
-    backend = workingbackend(ui, repo, similarity)
     try:
         fp = open(patchobj, 'rb')
     except TypeError:
         fp = patchobj
     try:
-        ret = applydiff(ui, fp, files, backend, store, strip=strip,
+        ret = applydiff(ui, fp, backend, store, strip=strip,
                         eolmode=eolmode)
     finally:
         if fp != patchobj:
             fp.close()
-        files.update(dict.fromkeys(backend.close()))
+        files.update(backend.close())
         store.close()
     if ret < 0:
         raise PatchError(_('patch failed to apply'))
     return ret > 0
 
+def internalpatch(ui, repo, patchobj, strip, files=None, eolmode='strict',
+                  similarity=0):
+    """use builtin patch to apply <patchobj> to the working directory.
+    returns whether patch was applied with fuzz factor."""
+    backend = workingbackend(ui, repo, similarity)
+    return patchbackend(ui, backend, patchobj, strip, files, eolmode)
+
+def patchrepo(ui, repo, ctx, store, patchobj, strip, files=None,
+              eolmode='strict'):
+    backend = repobackend(ui, repo, ctx, store)
+    return patchbackend(ui, backend, patchobj, strip, files, eolmode)
+
+def makememctx(repo, parents, text, user, date, branch, files, store,
+               editor=None):
+    def getfilectx(repo, memctx, path):
+        data, (islink, isexec), copied = store.getfile(path)
+        return context.memfilectx(path, data, islink=islink, isexec=isexec,
+                                  copied=copied)
+    extra = {}
+    if branch:
+        extra['branch'] = encoding.fromlocal(branch)
+    ctx =  context.memctx(repo, parents, text, files, getfilectx, user,
+                          date, extra)
+    if editor:
+        ctx._text = editor(repo, ctx, [])
+    return ctx
+
 def patch(ui, repo, patchname, strip=1, files=None, eolmode='strict',
           similarity=0):
     """Apply <patchname> to the working directory.
@@ -1380,7 +1443,7 @@
     """
     patcher = ui.config('ui', 'patch')
     if files is None:
-        files = {}
+        files = set()
     try:
         if patcher:
             return _externalpatch(ui, repo, patcher, patchname, strip,
@@ -1399,14 +1462,14 @@
             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, create, remove = selectfile(
-                    backend, afile, bfile, first_hunk, strip, gp)
-                changed.add(current_file)
+                    gp.path = pathstrip(gp.path, strip - 1)[1]
+                    if gp.oldpath:
+                        gp.oldpath = pathstrip(gp.oldpath, strip - 1)[1]
+                else:
+                    gp = makepatchmeta(backend, afile, bfile, first_hunk, strip)
+                changed.add(gp.path)
+                if gp.op == 'RENAME':
+                    changed.add(gp.oldpath)
             elif state not in ('hunk', 'git'):
                 raise util.Abort(_('unsupported parser state: %s') % state)
         return changed
--- a/mercurial/revlog.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/revlog.py	Tue Jun 14 20:43:04 2011 -0500
@@ -491,7 +491,7 @@
                 return nonodes
             lowestrev = min([self.rev(n) for n in roots])
         else:
-            roots = [nullid] # Everybody's a descendent of nullid
+            roots = [nullid] # Everybody's a descendant of nullid
             lowestrev = nullrev
         if (lowestrev == nullrev) and (heads is None):
             # We want _all_ the nodes!
@@ -528,7 +528,7 @@
                 r = self.rev(n)
                 if r >= lowestrev:
                     if n not in ancestors:
-                        # If we are possibly a descendent of one of the roots
+                        # If we are possibly a descendant of one of the roots
                         # and we haven't already been marked as an ancestor
                         ancestors.add(n) # Mark as ancestor
                         # Add non-nullid parents to list of nodes to tag.
@@ -562,41 +562,41 @@
                 lowestrev = nullrev
                 roots = [nullid]
         # Transform our roots list into a set.
-        descendents = set(roots)
+        descendants = set(roots)
         # Also, keep the original roots so we can filter out roots that aren't
         # 'real' roots (i.e. are descended from other roots).
-        roots = descendents.copy()
+        roots = descendants.copy()
         # Our topologically sorted list of output nodes.
         orderedout = []
         # Don't start at nullid since we don't want nullid in our output list,
         # and if nullid shows up in descedents, empty parents will look like
-        # they're descendents.
+        # they're descendants.
         for r in xrange(max(lowestrev, 0), highestrev + 1):
             n = self.node(r)
-            isdescendent = False
-            if lowestrev == nullrev:  # Everybody is a descendent of nullid
-                isdescendent = True
-            elif n in descendents:
-                # n is already a descendent
-                isdescendent = True
+            isdescendant = False
+            if lowestrev == nullrev:  # Everybody is a descendant of nullid
+                isdescendant = True
+            elif n in descendants:
+                # n is already a descendant
+                isdescendant = True
                 # This check only needs to be done here because all the roots
-                # will start being marked is descendents before the loop.
+                # will start being marked is descendants before the loop.
                 if n in roots:
                     # If n was a root, check if it's a 'real' root.
                     p = tuple(self.parents(n))
-                    # If any of its parents are descendents, it's not a root.
-                    if (p[0] in descendents) or (p[1] in descendents):
+                    # If any of its parents are descendants, it's not a root.
+                    if (p[0] in descendants) or (p[1] in descendants):
                         roots.remove(n)
             else:
                 p = tuple(self.parents(n))
-                # A node is a descendent if either of its parents are
-                # descendents.  (We seeded the dependents list with the roots
+                # A node is a descendant if either of its parents are
+                # descendants.  (We seeded the dependents list with the roots
                 # up there, remember?)
-                if (p[0] in descendents) or (p[1] in descendents):
-                    descendents.add(n)
-                    isdescendent = True
-            if isdescendent and ((ancestors is None) or (n in ancestors)):
-                # Only include nodes that are both descendents and ancestors.
+                if (p[0] in descendants) or (p[1] in descendants):
+                    descendants.add(n)
+                    isdescendant = True
+            if isdescendant and ((ancestors is None) or (n in ancestors)):
+                # Only include nodes that are both descendants and ancestors.
                 orderedout.append(n)
                 if (ancestors is not None) and (n in heads):
                     # We're trying to figure out which heads are reachable
--- a/mercurial/revset.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/revset.py	Tue Jun 14 20:43:04 2011 -0500
@@ -599,7 +599,7 @@
     revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
     if revs:
         revs = [repo.lookup(rev) for rev in revs]
-    other = hg.repository(hg.remoteui(repo, {}), dest)
+    other = hg.peer(repo, {}, dest)
     repo.ui.pushbuffer()
     common, outheads = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
     repo.ui.popbuffer()
--- a/mercurial/setdiscovery.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/setdiscovery.py	Tue Jun 14 20:43:04 2011 -0500
@@ -91,15 +91,27 @@
     roundtrips = 0
     cl = local.changelog
     dag = dagutil.revlogdag(cl)
-    nodes = dag.nodeset()
 
-    # early exit if we know all the specified server heads already
+    # early exit if we know all the specified remote heads already
     ui.debug("query 1; heads\n")
     roundtrips += 1
-    srvheadhashes = remote.heads()
-
-    ## TODO We might want to request an additional random sample of the server's
-    ## nodes batched with the heads query here.
+    ownheads = dag.heads()
+    sample = ownheads
+    if remote.local():
+        # stopgap until we have a proper localpeer that supports batch()
+        srvheadhashes = remote.heads()
+        yesno = remote.known(dag.externalizeall(sample))
+    elif remote.capable('batch'):
+        batch = remote.batch()
+        srvheadhashesref = batch.heads()
+        yesnoref = batch.known(dag.externalizeall(sample))
+        batch.submit()
+        srvheadhashes = srvheadhashesref.value
+        yesno = yesnoref.value
+    else:
+        # compatibitity with pre-batch, but post-known remotes during 1.9 devel
+        srvheadhashes = remote.heads()
+        sample = []
 
     if cl.tip() == nullid:
         if srvheadhashes != [nullid]:
@@ -115,46 +127,48 @@
         ui.note("all remote heads known locally\n")
         return (srvheadhashes, False, srvheadhashes,)
 
+    if sample and util.all(yesno):
+        ui.note("all local heads known remotely\n")
+        ownheadhashes = dag.externalizeall(ownheads)
+        return (ownheadhashes, True, srvheadhashes,)
+
     # full blown discovery
-    undecided = nodes # own nodes where I don't know if the server knows them
+    undecided = dag.nodeset() # own nodes where I don't know if remote knows them
     common = set() # own nodes I know we both know
-    missing = set() # own nodes I know the server lacks
+    missing = set() # own nodes I know remote lacks
 
-    # treat remote heads as a first implicit sample response
+    # treat remote heads (and maybe own heads) as a first implicit sample response
     common.update(dag.ancestorset(srvheads))
     undecided.difference_update(common)
-    # use cheapish initial sample
-    if common:
-        ui.debug("taking initial sample\n")
-        sample = _takefullsample(dag, undecided, size=fullsamplesize)
-    else:
-        ui.debug("taking quick initial sample\n")
-        sample = _takequicksample(dag, nodes, size=initialsamplesize,
-                                  initial=True)
+
+    full = False
+    while undecided:
 
-    roundtrips += 1
-    ui.progress(_('searching'), roundtrips, unit=_('queries'))
-    ui.debug("query %i; still undecided: %i, sample size is: %i\n"
-             % (roundtrips, len(undecided), len(sample)))
-    # indices between sample and externalized version must match
-    sample = list(sample)
-    yesno = remote.known(dag.externalizeall(sample))
+        if sample:
+            commoninsample = set(n for i, n in enumerate(sample) if yesno[i])
+            common.update(dag.ancestorset(commoninsample, common))
 
-    while undecided:
-        commoninsample = set(n for i, n in enumerate(sample) if yesno[i])
-        common.update(dag.ancestorset(commoninsample, common))
+            missinginsample = [n for i, n in enumerate(sample) if not yesno[i]]
+            missing.update(dag.descendantset(missinginsample, missing))
 
-        missinginsample = [n for i, n in enumerate(sample) if not yesno[i]]
-        missing.update(dag.descendantset(missinginsample, missing))
-
-        undecided.difference_update(missing)
-        undecided.difference_update(common)
+            undecided.difference_update(missing)
+            undecided.difference_update(common)
 
         if not undecided:
             break
 
-        ui.note("sampling from both directions\n")
-        sample = _takefullsample(dag, undecided, size=fullsamplesize)
+        if full:
+            ui.note("sampling from both directions\n")
+            sample = _takefullsample(dag, undecided, size=fullsamplesize)
+        elif common:
+            # use cheapish initial sample
+            ui.debug("taking initial sample\n")
+            sample = _takefullsample(dag, undecided, size=fullsamplesize)
+        else:
+            # use even cheaper initial sample
+            ui.debug("taking quick initial sample\n")
+            sample = _takequicksample(dag, undecided, size=initialsamplesize,
+                                      initial=True)
 
         roundtrips += 1
         ui.progress(_('searching'), roundtrips, unit=_('queries'))
@@ -163,6 +177,7 @@
         # indices between sample and externalized version must match
         sample = list(sample)
         yesno = remote.known(dag.externalizeall(sample))
+        full = True
 
     result = dag.headsetofconnecteds(common)
     ui.progress(_('searching'), None)
--- a/mercurial/simplemerge.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/simplemerge.py	Tue Jun 14 20:43:04 2011 -0500
@@ -211,7 +211,7 @@
         Method is as follows:
 
         The two sequences align only on regions which match the base
-        and both descendents.  These are found by doing a two-way diff
+        and both descendants.  These are found by doing a two-way diff
         of each one against the base, and then finding the
         intersections between those regions.  These "sync regions"
         are by definition unchanged in both and easily dealt with.
@@ -315,7 +315,7 @@
     mismatch_region = staticmethod(mismatch_region)
 
     def find_sync_regions(self):
-        """Return a list of sync regions, where both descendents match the base.
+        """Return a list of sync regions, where both descendants match the base.
 
         Generates a list of (base1, base2, a1, a2, b1, b2).  There is
         always a zero-length sync region at the end of all the files.
--- a/mercurial/sshserver.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/sshserver.py	Tue Jun 14 20:43:04 2011 -0500
@@ -14,11 +14,11 @@
         self.ui = ui
         self.repo = repo
         self.lock = None
-        self.fin = sys.stdin
-        self.fout = sys.stdout
+        self.fin = ui.fin
+        self.fout = ui.fout
 
         hook.redirect(True)
-        sys.stdout = sys.stderr
+        ui.fout = repo.ui.fout = ui.ferr
 
         # Prevent insertion/deletion of CRs
         util.setbinary(self.fin)
--- a/mercurial/sslutil.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/sslutil.py	Tue Jun 14 20:43:04 2011 -0500
@@ -18,6 +18,8 @@
 except ImportError:
     CERT_REQUIRED = 2
 
+    import socket, httplib
+
     def ssl_wrap_socket(sock, key_file, cert_file,
                         cert_reqs=CERT_REQUIRED, ca_certs=None):
         if ca_certs:
--- a/mercurial/subrepo.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/subrepo.py	Tue Jun 14 20:43:04 2011 -0500
@@ -437,14 +437,14 @@
         if revision not in self._repo:
             self._repo._subsource = source
             srcurl = _abssource(self._repo)
-            other = hg.repository(self._repo.ui, srcurl)
+            other = hg.peer(self._repo.ui, {}, srcurl)
             if len(self._repo) == 0:
                 self._repo.ui.status(_('cloning subrepo %s from %s\n')
                                      % (subrelpath(self), srcurl))
                 parentrepo = self._repo._subparent
                 shutil.rmtree(self._repo.root)
-                other, self._repo = hg.clone(self._repo._subparent.ui, other,
-                                             self._repo.root, update=False)
+                other, self._repo = hg.clone(self._repo._subparent.ui, {}, other,
+                                         self._repo.root, update=False)
                 self._initrepo(parentrepo, source, create=True)
             else:
                 self._repo.ui.status(_('pulling subrepo %s from %s\n')
@@ -495,7 +495,7 @@
         dsturl = _abssource(self._repo, True)
         self._repo.ui.status(_('pushing subrepo %s to %s\n') %
             (subrelpath(self), dsturl))
-        other = hg.repository(self._repo.ui, dsturl)
+        other = hg.peer(self._repo.ui, {}, dsturl)
         return self._repo.push(other, force)
 
     def outgoing(self, ui, dest, opts):
--- a/mercurial/templates/coal/map	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/templates/coal/map	Tue Jun 14 20:43:04 2011 -0500
@@ -23,6 +23,8 @@
 filenodelink = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
 filenolink = '{file|escape} '
 fileellipses = '...'
+diffstatlink = ../paper/diffstat.tmpl
+diffstatnolink = ../paper/diffstat.tmpl
 changelogentry = ../paper/shortlogentry.tmpl
 searchentry = ../paper/shortlogentry.tmpl
 changeset = ../paper/changeset.tmpl
--- a/mercurial/templates/paper/changeset.tmpl	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/templates/paper/changeset.tmpl	Tue Jun 14 20:43:04 2011 -0500
@@ -62,6 +62,18 @@
  <th class="files">files</th>
  <td class="files">{files}</td>
 </tr>
+<tr>
+  <th class="diffstat">diffstat</th>
+  <td class="diffstat">
+    {diffsummary}
+    <a id="diffstatexpand" href="javascript:showDiffstat()"/>[<tt>+</tt>]</a>
+    <div id="diffstatdetails" style="display:none;">
+      <a href="javascript:hideDiffstat()"/>[<tt>-</tt>]</a>
+      <p>
+      <table>{diffstat}</table>
+    </div>
+  </td>
+</tr>
 </table>
 
 <div class="overflow">
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/templates/paper/diffstat.tmpl	Tue Jun 14 20:43:04 2011 -0500
@@ -0,0 +1,8 @@
+  <tr class="parity{parity}">
+    <td class="diffstat-file"><a href="#l{fileno}.1">{file|escape}</a></td>
+    <td class="diffstat-total" align="right">{total}</td>
+    <td class="diffstat-graph">
+      <span class="diffstat-add" style="width:{addpct}%;">&nbsp;</span>
+      <span class="diffstat-remove" style="width:{removepct}%;">&nbsp;</span>
+    </td>
+  </tr>
--- a/mercurial/templates/paper/map	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/templates/paper/map	Tue Jun 14 20:43:04 2011 -0500
@@ -22,6 +22,8 @@
 filenodelink = '<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{file|escape}</a> '
 filenolink = '{file|escape} '
 fileellipses = '...'
+diffstatlink = diffstat.tmpl
+diffstatnolink = diffstat.tmpl
 changelogentry = shortlogentry.tmpl
 searchentry = shortlogentry.tmpl
 changeset = changeset.tmpl
--- a/mercurial/templates/static/mercurial.js	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/templates/static/mercurial.js	Tue Jun 14 20:43:04 2011 -0500
@@ -2,6 +2,7 @@
 //
 // Rendering of branch DAGs on the client side
 // Display of elapsed time
+// Show or hide diffstat
 //
 // Copyright 2008 Dirkjan Ochtman <dirkjan AT ochtman DOT nl>
 // Copyright 2006 Alexander Schremmer <alex AT alexanderweb DOT de>
@@ -218,3 +219,13 @@
 		}
 	}
 })(document, RegExp, Math, isNaN, Date, false, true)
+
+function showDiffstat() {
+	document.getElementById('diffstatdetails').style.display = 'inline';
+	document.getElementById('diffstatexpand').style.display = 'none';
+}
+
+function hideDiffstat() {
+	document.getElementById('diffstatdetails').style.display = 'none';
+	document.getElementById('diffstatexpand').style.display = 'inline';
+}
--- a/mercurial/templates/static/style-coal.css	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/templates/static/style-coal.css	Tue Jun 14 20:43:04 2011 -0500
@@ -103,6 +103,26 @@
 .minusline { color: #dc143c; } /* crimson */
 .atline { color: purple; }
 
+.diffstat-file {
+  white-space: nowrap;
+  font-size: 90%;
+}
+.diffstat-total {
+  white-space: nowrap;
+  font-size: 90%;
+}
+.diffstat-graph {
+  width: 100%;
+}
+.diffstat-add {
+  background-color: green;
+  float: left;
+}
+.diffstat-remove {
+  background-color: red;
+  float: left;
+}
+
 .navigate {
   text-align: right;
   font-size: 60%;
--- a/mercurial/templates/static/style-paper.css	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/templates/static/style-paper.css	Tue Jun 14 20:43:04 2011 -0500
@@ -94,6 +94,26 @@
 .minusline { color: #dc143c; } /* crimson */
 .atline { color: purple; }
 
+.diffstat-file {
+  white-space: nowrap;
+  font-size: 90%;
+}
+.diffstat-total {
+  white-space: nowrap;
+  font-size: 90%;
+}
+.diffstat-graph {
+  width: 100%;
+}
+.diffstat-add {
+  background-color: green;
+  float: left;
+}
+.diffstat-remove {
+  background-color: red;
+  float: left;
+}
+
 .navigate {
   text-align: right;
   font-size: 60%;
--- a/mercurial/ui.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/ui.py	Tue Jun 14 20:43:04 2011 -0500
@@ -21,6 +21,10 @@
         self._trustgroups = set()
 
         if src:
+            self.fout = src.fout
+            self.ferr = src.ferr
+            self.fin = src.fin
+
             self._tcfg = src._tcfg.copy()
             self._ucfg = src._ucfg.copy()
             self._ocfg = src._ocfg.copy()
@@ -29,6 +33,10 @@
             self.environ = src.environ
             self.fixconfig()
         else:
+            self.fout = sys.stdout
+            self.ferr = sys.stderr
+            self.fin = sys.stdin
+
             # shared read-only environment
             self.environ = os.environ
             # we always trust global config files
@@ -435,26 +443,26 @@
             self._buffers[-1].extend([str(a) for a in args])
         else:
             for a in args:
-                sys.stdout.write(str(a))
+                self.fout.write(str(a))
 
     def write_err(self, *args, **opts):
         try:
-            if not getattr(sys.stdout, 'closed', False):
-                sys.stdout.flush()
+            if not getattr(self.fout, 'closed', False):
+                self.fout.flush()
             for a in args:
-                sys.stderr.write(str(a))
+                self.ferr.write(str(a))
             # stderr may be buffered under win32 when redirected to files,
             # including stdout.
-            if not getattr(sys.stderr, 'closed', False):
-                sys.stderr.flush()
+            if not getattr(self.ferr, 'closed', False):
+                self.ferr.flush()
         except IOError, inst:
             if inst.errno not in (errno.EPIPE, errno.EIO):
                 raise
 
     def flush(self):
-        try: sys.stdout.flush()
+        try: self.fout.flush()
         except: pass
-        try: sys.stderr.flush()
+        try: self.ferr.flush()
         except: pass
 
     def interactive(self):
@@ -475,7 +483,7 @@
         if i is None:
             # some environments replace stdin without implementing isatty
             # usually those are non-interactive
-            return util.isatty(sys.stdin)
+            return util.isatty(self.fin)
 
         return i
 
@@ -513,12 +521,12 @@
         if i is None:
             # some environments replace stdout without implementing isatty
             # usually those are non-interactive
-            return util.isatty(sys.stdout)
+            return util.isatty(self.fout)
 
         return i
 
     def _readline(self, prompt=''):
-        if util.isatty(sys.stdin):
+        if util.isatty(self.fin):
             try:
                 # magically add command line editing support, where
                 # available
@@ -528,7 +536,14 @@
                 # windows sometimes raises something other than ImportError
             except Exception:
                 pass
+
+        # instead of trying to emulate raw_input, swap our in/out
+        # with sys.stdin/out
+        old = sys.stdout, sys.stdin
+        sys.stdout, sys.stdin = self.fout, self.fin
         line = raw_input(prompt)
+        sys.stdout, sys.stdin = old
+
         # When stdin is in binary mode on Windows, it can cause
         # raw_input() to emit an extra trailing carriage return
         if os.linesep == '\r\n' and line and line[-1] == '\r':
--- a/mercurial/wireproto.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/mercurial/wireproto.py	Tue Jun 14 20:43:04 2011 -0500
@@ -12,6 +12,110 @@
 import repo, error, encoding, util, store
 import pushkey as pushkeymod
 
+# abstract batching support
+
+class future(object):
+    '''placeholder for a value to be set later'''
+    def set(self, value):
+        if hasattr(self, 'value'):
+            raise error.RepoError("future is already set")
+        self.value = value
+
+class batcher(object):
+    '''base class for batches of commands submittable in a single request
+
+    All methods invoked on instances of this class are simply queued and return a
+    a future for the result. Once you call submit(), all the queued calls are
+    performed and the results set in their respective futures.
+    '''
+    def __init__(self):
+        self.calls = []
+    def __getattr__(self, name):
+        def call(*args, **opts):
+            resref = future()
+            self.calls.append((name, args, opts, resref,))
+            return resref
+        return call
+    def submit(self):
+        pass
+
+class localbatch(batcher):
+    '''performs the queued calls directly'''
+    def __init__(self, local):
+        batcher.__init__(self)
+        self.local = local
+    def submit(self):
+        for name, args, opts, resref in self.calls:
+            resref.set(getattr(self.local, name)(*args, **opts))
+
+class remotebatch(batcher):
+    '''batches the queued calls; uses as few roundtrips as possible'''
+    def __init__(self, remote):
+        '''remote must support _submitbatch(encbatch) and _submitone(op, encargs)'''
+        batcher.__init__(self)
+        self.remote = remote
+    def submit(self):
+        req, rsp = [], []
+        for name, args, opts, resref in self.calls:
+            mtd = getattr(self.remote, name)
+            if hasattr(mtd, 'batchable'):
+                batchable = getattr(mtd, 'batchable')(mtd.im_self, *args, **opts)
+                encargsorres, encresref = batchable.next()
+                if encresref:
+                    req.append((name, encargsorres,))
+                    rsp.append((batchable, encresref, resref,))
+                else:
+                    resref.set(encargsorres)
+            else:
+                if req:
+                    self._submitreq(req, rsp)
+                    req, rsp = [], []
+                resref.set(mtd(*args, **opts))
+        if req:
+            self._submitreq(req, rsp)
+    def _submitreq(self, req, rsp):
+        encresults = self.remote._submitbatch(req)
+        for encres, r in zip(encresults, rsp):
+            batchable, encresref, resref = r
+            encresref.set(encres)
+            resref.set(batchable.next())
+
+def batchable(f):
+    '''annotation for batchable methods
+
+    Such methods must implement a coroutine as follows:
+
+    @batchable
+    def sample(self, one, two=None):
+        # Handle locally computable results first:
+        if not one:
+            yield "a local result", None
+        # Build list of encoded arguments suitable for your wire protocol:
+        encargs = [('one', encode(one),), ('two', encode(two),)]
+        # Create future for injection of encoded result:
+        encresref = future()
+        # Return encoded arguments and future:
+        yield encargs, encresref
+        # Assuming the future to be filled with the result from the batched request
+        # now. Decode it:
+        yield decode(encresref.value)
+
+    The decorator returns a function which wraps this coroutine as a plain method,
+    but adds the original method as an attribute called "batchable", which is
+    used by remotebatch to split the call into separate encoding and decoding
+    phases.
+    '''
+    def plain(*args, **opts):
+        batchable = f(*args, **opts)
+        encargsorres, encresref = batchable.next()
+        if not encresref:
+            return encargsorres # a local result in this case
+        self = args[0]
+        encresref.set(self._submitone(f.func_name, encargsorres))
+        return batchable.next()
+    setattr(plain, 'batchable', f)
+    return plain
+
 # list of nodes encoding / decoding
 
 def decodelist(l, sep=' '):
@@ -22,34 +126,77 @@
 def encodelist(l, sep=' '):
     return sep.join(map(hex, l))
 
+# batched call argument encoding
+
+def escapearg(plain):
+    return (plain
+            .replace(':', '::')
+            .replace(',', ':,')
+            .replace(';', ':;')
+            .replace('=', ':='))
+
+def unescapearg(escaped):
+    return (escaped
+            .replace(':=', '=')
+            .replace(':;', ';')
+            .replace(':,', ',')
+            .replace('::', ':'))
+
 # client side
 
+def todict(**args):
+    return args
+
 class wirerepository(repo.repository):
+
+    def batch(self):
+        return remotebatch(self)
+    def _submitbatch(self, req):
+        cmds = []
+        for op, argsdict in req:
+            args = ','.join('%s=%s' % p for p in argsdict.iteritems())
+            cmds.append('%s %s' % (op, args))
+        rsp = self._call("batch", cmds=';'.join(cmds))
+        return rsp.split(';')
+    def _submitone(self, op, args):
+        return self._call(op, **args)
+
+    @batchable
     def lookup(self, key):
         self.requirecap('lookup', _('look up remote revision'))
-        d = self._call("lookup", key=encoding.fromlocal(key))
+        f = future()
+        yield todict(key=encoding.fromlocal(key)), f
+        d = f.value
         success, data = d[:-1].split(" ", 1)
         if int(success):
-            return bin(data)
+            yield bin(data)
         self._abort(error.RepoError(data))
 
+    @batchable
     def heads(self):
-        d = self._call("heads")
+        f = future()
+        yield {}, f
+        d = f.value
         try:
-            return decodelist(d[:-1])
+            yield decodelist(d[:-1])
         except ValueError:
             self._abort(error.ResponseError(_("unexpected response:"), d))
 
+    @batchable
     def known(self, nodes):
-        n = encodelist(nodes)
-        d = self._call("known", nodes=n)
+        f = future()
+        yield todict(nodes=encodelist(nodes)), f
+        d = f.value
         try:
-            return [bool(int(f)) for f in d]
+            yield [bool(int(f)) for f in d]
         except ValueError:
             self._abort(error.ResponseError(_("unexpected response:"), d))
 
+    @batchable
     def branchmap(self):
-        d = self._call("branchmap")
+        f = future()
+        yield {}, f
+        d = f.value
         try:
             branchmap = {}
             for branchpart in d.splitlines():
@@ -57,7 +204,7 @@
                 branchname = encoding.tolocal(urllib.unquote(branchname))
                 branchheads = decodelist(branchheads)
                 branchmap[branchname] = branchheads
-            return branchmap
+            yield branchmap
         except TypeError:
             self._abort(error.ResponseError(_("unexpected response:"), d))
 
@@ -82,30 +229,35 @@
                 self._abort(error.ResponseError(_("unexpected response:"), d))
         return r
 
+    @batchable
     def pushkey(self, namespace, key, old, new):
         if not self.capable('pushkey'):
-            return False
-        d = self._call("pushkey",
-                       namespace=encoding.fromlocal(namespace),
-                       key=encoding.fromlocal(key),
-                       old=encoding.fromlocal(old),
-                       new=encoding.fromlocal(new))
+            yield False, None
+        f = future()
+        yield todict(namespace=encoding.fromlocal(namespace),
+                     key=encoding.fromlocal(key),
+                     old=encoding.fromlocal(old),
+                     new=encoding.fromlocal(new)), f
+        d = f.value
         try:
             d = bool(int(d))
         except ValueError:
             raise error.ResponseError(
                 _('push failed (unexpected response):'), d)
-        return d
+        yield d
 
+    @batchable
     def listkeys(self, namespace):
         if not self.capable('pushkey'):
-            return {}
-        d = self._call("listkeys", namespace=encoding.fromlocal(namespace))
+            yield {}, None
+        f = future()
+        yield todict(namespace=encoding.fromlocal(namespace)), f
+        d = f.value
         r = {}
         for l in d.splitlines():
             k, v = l.split('\t')
             r[encoding.tolocal(k)] = encoding.tolocal(v)
-        return r
+        yield r
 
     def stream_out(self):
         return self._callstream('stream_out')
@@ -198,6 +350,34 @@
                          % (cmd, ",".join(others)))
     return opts
 
+def batch(repo, proto, cmds, others):
+    res = []
+    for pair in cmds.split(';'):
+        op, args = pair.split(' ', 1)
+        vals = {}
+        for a in args.split(','):
+            if a:
+                n, v = a.split('=')
+                vals[n] = unescapearg(v)
+        func, spec = commands[op]
+        if spec:
+            keys = spec.split()
+            data = {}
+            for k in keys:
+                if k == '*':
+                    star = {}
+                    for key in vals.keys():
+                        if key not in keys:
+                            star[key] = vals[key]
+                    data['*'] = star
+                else:
+                    data[k] = vals[k]
+            result = func(repo, proto, *[data[k] for k in keys])
+        else:
+            result = func(repo, proto)
+        res.append(escapearg(result))
+    return ';'.join(res)
+
 def between(repo, proto, pairs):
     pairs = [decodelist(p, '-') for p in pairs.split(" ")]
     r = []
@@ -223,7 +403,7 @@
 
 def capabilities(repo, proto):
     caps = ('lookup changegroupsubset branchmap pushkey known getbundle '
-            'unbundlehash').split()
+            'unbundlehash batch').split()
     if _allowstream(repo.ui):
         requiredformats = repo.requirements & repo.supportedformats
         # if our local revlogs are just revlogv1, add 'stream' cap
@@ -402,6 +582,7 @@
         os.unlink(tempname)
 
 commands = {
+    'batch': (batch, 'cmds *'),
     'between': (between, 'pairs'),
     'branchmap': (branchmap, ''),
     'branches': (branches, 'nodes'),
--- a/setup.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/setup.py	Tue Jun 14 20:43:04 2011 -0500
@@ -56,7 +56,7 @@
 import shutil
 import tempfile
 from distutils import log
-from distutils.core import setup, Extension
+from distutils.core import setup, Command, Extension
 from distutils.dist import Distribution
 from distutils.command.build import build
 from distutils.command.build_ext import build_ext
@@ -64,7 +64,7 @@
 from distutils.command.install_scripts import install_scripts
 from distutils.spawn import spawn, find_executable
 from distutils.ccompiler import new_compiler
-from distutils.errors import CCompilerError
+from distutils.errors import CCompilerError, DistutilsExecError
 from distutils.sysconfig import get_python_inc
 from distutils.version import StrictVersion
 
@@ -260,6 +260,34 @@
             else:
                 yield module
 
+class buildhgextindex(Command):
+    description = 'generate prebuilt index of hgext (for frozen package)'
+    user_options = []
+    _indexfilename = 'hgext/__index__.py'
+
+    def initialize_options(self):
+        pass
+
+    def finalize_options(self):
+        pass
+
+    def run(self):
+        if os.path.exists(self._indexfilename):
+            os.unlink(self._indexfilename)
+
+        # here no extension enabled, disabled() lists up everything
+        code = ('import pprint; from mercurial import extensions; '
+                'pprint.pprint(extensions.disabled())')
+        out, err = runcmd([sys.executable, '-c', code], env)
+        if err:
+            raise DistutilsExecError(err)
+
+        f = open(self._indexfilename, 'w')
+        f.write('# this file is autogenerated by setup.py\n')
+        f.write('docs = ')
+        f.write(out)
+        f.close()
+
 class hginstallscripts(install_scripts):
     '''
     This is a specialization of install_scripts that replaces the @LIBDIR@ with
@@ -309,6 +337,7 @@
 cmdclass = {'build_mo': hgbuildmo,
             'build_ext': hgbuildext,
             'build_py': hgbuildpy,
+            'build_hgextindex': buildhgextindex,
             'install_scripts': hginstallscripts}
 
 packages = ['mercurial', 'mercurial.hgweb',
@@ -373,6 +402,8 @@
         {'script':'hg',
          'copyright':'Copyright (C) 2005-2010 Matt Mackall and others',
          'product_version':version}]
+    # sub command of 'build' because 'py2exe' does not handle sub_commands
+    build.sub_commands.insert(0, ('build_hgextindex', None))
 
 if os.name == 'nt':
     # Windows binary file versions for exe/dll files must have the
--- a/tests/hghave	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/hghave	Tue Jun 14 20:43:04 2011 -0500
@@ -52,7 +52,7 @@
 
 def has_mtn():
     return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
-        'mtn --version', r'monotone 0\.(\d|[12]\d|3[01])[^\d]', True)
+        'mtn --version', r'monotone 0\.', True)
 
 def has_eol_in_paths():
     try:
--- a/tests/run-tests.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/run-tests.py	Tue Jun 14 20:43:04 2011 -0500
@@ -741,6 +741,7 @@
             skip("doesn't exist")
             return None
     else:
+        vlog('# Test file', test, 'not supported, ignoring')
         return None # not a supported test, don't record
 
     if not (options.whitelisted and test in options.whitelisted):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-batching.py	Tue Jun 14 20:43:04 2011 -0500
@@ -0,0 +1,175 @@
+# test-batching.py - tests for transparent command batching
+#
+# Copyright 2011 Peter Arrenbrecht <peter@arrenbrecht.ch>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from mercurial.wireproto import localbatch, remotebatch, batchable, future
+
+# equivalent of repo.repository
+class thing(object):
+    def hello(self):
+        return "Ready."
+
+# equivalent of localrepo.localrepository
+class localthing(thing):
+    def foo(self, one, two=None):
+        if one:
+            return "%s and %s" % (one, two,)
+        return "Nope"
+    def bar(self, b, a):
+        return "%s und %s" % (b, a,)
+    def greet(self, name=None):
+        return "Hello, %s" % name
+    def batch(self):
+        '''Support for local batching.'''
+        return localbatch(self)
+
+# usage of "thing" interface
+def use(it):
+
+    # Direct call to base method shared between client and server.
+    print it.hello()
+
+    # Direct calls to proxied methods. They cause individual roundtrips.
+    print it.foo("Un", two="Deux")
+    print it.bar("Eins", "Zwei")
+
+    # Batched call to a couple of (possibly proxied) methods.
+    batch = it.batch()
+    # The calls return futures to eventually hold results.
+    foo = batch.foo(one="One", two="Two")
+    foo2 = batch.foo(None)
+    bar = batch.bar("Eins", "Zwei")
+    # We can call non-batchable proxy methods, but the break the current batch
+    # request and cause additional roundtrips.
+    greet = batch.greet(name="John Smith")
+    # We can also add local methods into the mix, but they break the batch too.
+    hello = batch.hello()
+    bar2 = batch.bar(b="Uno", a="Due")
+    # Only now are all the calls executed in sequence, with as few roundtrips
+    # as possible.
+    batch.submit()
+    # After the call to submit, the futures actually contain values.
+    print foo.value
+    print foo2.value
+    print bar.value
+    print greet.value
+    print hello.value
+    print bar2.value
+
+# local usage
+mylocal = localthing()
+print
+print "== Local"
+use(mylocal)
+
+# demo remoting; mimicks what wireproto and HTTP/SSH do
+
+# shared
+
+def escapearg(plain):
+    return (plain
+            .replace(':', '::')
+            .replace(',', ':,')
+            .replace(';', ':;')
+            .replace('=', ':='))
+def unescapearg(escaped):
+    return (escaped
+            .replace(':=', '=')
+            .replace(':;', ';')
+            .replace(':,', ',')
+            .replace('::', ':'))
+
+# server side
+
+# equivalent of wireproto's global functions
+class server:
+    def __init__(self, local):
+        self.local = local
+    def _call(self, name, args):
+        args = dict(arg.split('=', 1) for arg in args)
+        return getattr(self, name)(**args)
+    def perform(self, req):
+        print "REQ:", req
+        name, args = req.split('?', 1)
+        args = args.split('&')
+        vals = dict(arg.split('=', 1) for arg in args)
+        res = getattr(self, name)(**vals)
+        print "  ->", res
+        return res
+    def batch(self, cmds):
+        res = []
+        for pair in cmds.split(';'):
+            name, args = pair.split(':', 1)
+            vals = {}
+            for a in args.split(','):
+                if a:
+                    n, v = a.split('=')
+                    vals[n] = unescapearg(v)
+            res.append(escapearg(getattr(self, name)(**vals)))
+        return ';'.join(res)
+    def foo(self, one, two):
+        return mangle(self.local.foo(unmangle(one), unmangle(two)))
+    def bar(self, b, a):
+        return mangle(self.local.bar(unmangle(b), unmangle(a)))
+    def greet(self, name):
+        return mangle(self.local.greet(unmangle(name)))
+myserver = server(mylocal)
+
+# local side
+
+# equivalent of wireproto.encode/decodelist, that is, type-specific marshalling
+# here we just transform the strings a bit to check we're properly en-/decoding
+def mangle(s):
+    return ''.join(chr(ord(c) + 1) for c in s)
+def unmangle(s):
+    return ''.join(chr(ord(c) - 1) for c in s)
+
+# equivalent of wireproto.wirerepository and something like http's wire format
+class remotething(thing):
+    def __init__(self, server):
+        self.server = server
+    def _submitone(self, name, args):
+        req = name + '?' + '&'.join(['%s=%s' % (n, v) for n, v in args])
+        return self.server.perform(req)
+    def _submitbatch(self, cmds):
+        req = []
+        for name, args in cmds:
+            args = ','.join(n + '=' + escapearg(v) for n, v in args)
+            req.append(name + ':' + args)
+        req = ';'.join(req)
+        res = self._submitone('batch', [('cmds', req,)])
+        return res.split(';')
+
+    def batch(self):
+        return remotebatch(self)
+
+    @batchable
+    def foo(self, one, two=None):
+        if not one:
+            yield "Nope", None
+        encargs = [('one', mangle(one),), ('two', mangle(two),)]
+        encresref = future()
+        yield encargs, encresref
+        yield unmangle(encresref.value)
+
+    @batchable
+    def bar(self, b, a):
+        encresref = future()
+        yield [('b', mangle(b),), ('a', mangle(a),)], encresref
+        yield unmangle(encresref.value)
+
+    # greet is coded directly. It therefore does not support batching. If it
+    # does appear in a batch, the batch is split around greet, and the call to
+    # greet is done in its own roundtrip.
+    def greet(self, name=None):
+        return unmangle(self._submitone('greet', [('name', mangle(name),)]))
+
+# demo remote usage
+
+myproxy = remotething(myserver)
+print
+print "== Remote"
+use(myproxy)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-batching.py.out	Tue Jun 14 20:43:04 2011 -0500
@@ -0,0 +1,32 @@
+
+== Local
+Ready.
+Un and Deux
+Eins und Zwei
+One and Two
+Nope
+Eins und Zwei
+Hello, John Smith
+Ready.
+Uno und Due
+
+== Remote
+Ready.
+REQ: foo?one=Vo&two=Efvy
+  -> Vo!boe!Efvy
+Un and Deux
+REQ: bar?b=Fjot&a=[xfj
+  -> Fjot!voe![xfj
+Eins und Zwei
+REQ: batch?cmds=foo:one=Pof,two=Uxp;bar:b=Fjot,a=[xfj
+  -> Pof!boe!Uxp;Fjot!voe![xfj
+REQ: greet?name=Kpio!Tnjui
+  -> Ifmmp-!Kpio!Tnjui
+REQ: batch?cmds=bar:b=Vop,a=Evf
+  -> Vop!voe!Evf
+One and Two
+Nope
+Eins und Zwei
+Hello, John Smith
+Ready.
+Uno und Due
--- a/tests/test-clone.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-clone.t	Tue Jun 14 20:43:04 2011 -0500
@@ -433,7 +433,7 @@
   > from mercurial import ui, hg
   > myui = ui.ui()
   > repo = hg.repository(myui, 'a')
-  > hg.clone(myui, repo, dest="ua")
+  > hg.clone(myui, {}, repo, dest="ua")
   > EOF
 
   $ python simpleclone.py
@@ -446,7 +446,7 @@
   > from mercurial import ui, hg
   > myui = ui.ui()
   > repo = hg.repository(myui, 'a')
-  > hg.clone(myui, repo, dest="ua", branch=["stable",])
+  > hg.clone(myui, {}, repo, dest="ua", branch=["stable",])
   > EOF
 
   $ python branchclone.py
--- a/tests/test-confused-revert.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-confused-revert.t	Tue Jun 14 20:43:04 2011 -0500
@@ -59,7 +59,8 @@
 Revert should fail:
 
   $ hg revert
-  abort: uncommitted merge - use "hg update", see "hg help revert"
+  abort: no files or directories specified
+  (use --all to revert all files)
   [255]
 
 Revert should be ok now:
--- a/tests/test-convert-cvs-detectmerge.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-convert-cvs-detectmerge.t	Tue Jun 14 20:43:04 2011 -0500
@@ -16,6 +16,7 @@
 
   $ cvsci()
   > {
+  >     sleep 1
   >     cvs -f ci "$@" > /dev/null
   > }
 
@@ -53,7 +54,6 @@
 modify file1 on branch v1_0
 
   $ cvscall -Q update -rv1_0
-  $ sleep 1
   $ echo "change" >> file1
   $ cvsci -m"add text"
   cvs commit: Examining .
@@ -96,7 +96,6 @@
 this will create rev 1.3
 change on trunk to backport
 
-  $ sleep 1
   $ echo "backport me" >> file1
   $ cvsci -m"add other text" file1
   $ cvscall log file1
@@ -150,7 +149,6 @@
 fix bug on v1_1, merge to trunk with error
 
   $ cvscall -Q update -rv1_1
-  $ sleep 1
   $ echo "merge forward" >> file1
   $ cvscall -Q tag unmerged
   $ cvsci -m"fix file1"
--- a/tests/test-convert-cvs-synthetic.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-convert-cvs-synthetic.t	Tue Jun 14 20:43:04 2011 -0500
@@ -23,6 +23,7 @@
 
   $ cvsci()
   > {
+  >     sleep 1
   >     cvs -f ci "$@" >/dev/null
   > }
   $ cvscall -d "$CVSROOT" init
--- a/tests/test-debugcomplete.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-debugcomplete.t	Tue Jun 14 20:43:04 2011 -0500
@@ -245,7 +245,7 @@
   heads: rev, topo, active, closed, style, template
   help: extension, command
   identify: rev, num, id, branch, tags, bookmarks
-  import: strip, base, force, no-commit, exact, import-branch, message, logfile, date, user, similarity
+  import: strip, base, force, no-commit, bypass, 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, all
--- a/tests/test-git-import.t	Tue Jun 14 00:31:56 2011 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,468 +0,0 @@
-
-  $ hg init
-
-New file:
-
-  $ hg import -d "1000000 0" -mnew - <<EOF
-  > diff --git a/new b/new
-  > new file mode 100644
-  > index 0000000..7898192
-  > --- /dev/null
-  > +++ b/new
-  > @@ -0,0 +1 @@
-  > +a
-  > EOF
-  applying patch from stdin
-
-  $ hg tip -q
-  0:ae3ee40d2079
-
-New empty file:
-
-  $ hg import -d "1000000 0" -mempty - <<EOF
-  > diff --git a/empty b/empty
-  > new file mode 100644
-  > EOF
-  applying patch from stdin
-
-  $ hg tip -q
-  1:ab199dc869b5
-
-  $ hg locate empty
-  empty
-
-chmod +x:
-
-  $ hg import -d "1000000 0" -msetx - <<EOF
-  > diff --git a/new b/new
-  > old mode 100644
-  > new mode 100755
-  > EOF
-  applying patch from stdin
-
-  $ hg tip -q
-  2:3a34410f282e
-
-  $ test -x new
-
-Copy:
-
-  $ hg import -d "1000000 0" -mcopy - <<EOF
-  > diff --git a/new b/copy
-  > old mode 100755
-  > new mode 100644
-  > similarity index 100%
-  > copy from new
-  > copy to copy
-  > diff --git a/new b/copyx
-  > similarity index 100%
-  > copy from new
-  > copy to copyx
-  > EOF
-  applying patch from stdin
-
-  $ hg tip -q
-  3:37bacb7ca14d
-
-  $ if "$TESTDIR/hghave" -q execbit; then
-  >     test -f copy -a ! -x copy || echo bad
-  >     test -x copyx || echo bad
-  > else
-  >     test -f copy || echo bad
-  > fi
-
-  $ cat copy
-  a
-
-  $ hg cat copy
-  a
-
-Rename:
-
-  $ hg import -d "1000000 0" -mrename - <<EOF
-  > diff --git a/copy b/rename
-  > similarity index 100%
-  > rename from copy
-  > rename to rename
-  > EOF
-  applying patch from stdin
-
-  $ hg tip -q
-  4:47b81a94361d
-
-  $ hg locate
-  copyx
-  empty
-  new
-  rename
-
-Delete:
-
-  $ hg import -d "1000000 0" -mdelete - <<EOF
-  > diff --git a/copyx b/copyx
-  > deleted file mode 100755
-  > index 7898192..0000000
-  > --- a/copyx
-  > +++ /dev/null
-  > @@ -1 +0,0 @@
-  > -a
-  > EOF
-  applying patch from stdin
-
-  $ hg tip -q
-  5:d9b001d98336
-
-  $ hg locate
-  empty
-  new
-  rename
-
-  $ test -f copyx
-  [1]
-
-Regular diff:
-
-  $ hg import -d "1000000 0" -mregular - <<EOF
-  > diff --git a/rename b/rename
-  > index 7898192..72e1fe3 100644
-  > --- a/rename
-  > +++ b/rename
-  > @@ -1 +1,5 @@
-  >  a
-  > +a
-  > +a
-  > +a
-  > +a
-  > EOF
-  applying patch from stdin
-
-  $ hg tip -q
-  6:ebe901e7576b
-
-Copy and modify:
-
-  $ hg import -d "1000000 0" -mcopymod - <<EOF
-  > diff --git a/rename b/copy2
-  > similarity index 80%
-  > copy from rename
-  > copy to copy2
-  > index 72e1fe3..b53c148 100644
-  > --- a/rename
-  > +++ b/copy2
-  > @@ -1,5 +1,5 @@
-  >  a
-  >  a
-  > -a
-  > +b
-  >  a
-  >  a
-  > EOF
-  applying patch from stdin
-
-  $ hg tip -q
-  7:18f368958ecd
-
-  $ hg cat copy2
-  a
-  a
-  b
-  a
-  a
-
-Rename and modify:
-
-  $ hg import -d "1000000 0" -mrenamemod - <<EOF
-  > diff --git a/copy2 b/rename2
-  > similarity index 80%
-  > rename from copy2
-  > rename to rename2
-  > index b53c148..8f81e29 100644
-  > --- a/copy2
-  > +++ b/rename2
-  > @@ -1,5 +1,5 @@
-  >  a
-  >  a
-  >  b
-  > -a
-  > +c
-  >  a
-  > EOF
-  applying patch from stdin
-
-  $ hg tip -q
-  8:c32b0d7e6f44
-
-  $ hg locate copy2
-  [1]
-  $ hg cat rename2
-  a
-  a
-  b
-  c
-  a
-
-One file renamed multiple times:
-
-  $ hg import -d "1000000 0" -mmultirenames - <<EOF
-  > diff --git a/rename2 b/rename3
-  > rename from rename2
-  > rename to rename3
-  > diff --git a/rename2 b/rename3-2
-  > rename from rename2
-  > rename to rename3-2
-  > EOF
-  applying patch from stdin
-
-  $ hg tip -q
-  9:034a6bf95330
-
-  $ hg log -vr. --template '{rev} {files} / {file_copies}\n'
-  9 rename2 rename3 rename3-2 / rename3 (rename2)rename3-2 (rename2)
-
-  $ hg locate rename2 rename3 rename3-2
-  rename3
-  rename3-2
-
-  $ hg cat rename3
-  a
-  a
-  b
-  c
-  a
-
-  $ hg cat rename3-2
-  a
-  a
-  b
-  c
-  a
-
-  $ echo foo > foo
-  $ hg add foo
-  $ hg ci -m 'add foo'
-
-Binary files and regular patch hunks:
-
-  $ hg import -d "1000000 0" -m binaryregular - <<EOF
-  > diff --git a/binary b/binary
-  > new file mode 100644
-  > index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4
-  > GIT binary patch
-  > literal 4
-  > Lc\${NkU|;|M00aO5
-  > 
-  > diff --git a/foo b/foo2
-  > rename from foo
-  > rename to foo2
-  > EOF
-  applying patch from stdin
-
-  $ hg tip -q
-  11:c39bce63e786
-
-  $ cat foo2
-  foo
-
-  $ hg manifest --debug | grep binary
-  045c85ba38952325e126c70962cc0f9d9077bc67 644   binary
-
-Multiple binary files:
-
-  $ hg import -d "1000000 0" -m multibinary - <<EOF
-  > diff --git a/mbinary1 b/mbinary1
-  > new file mode 100644
-  > index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4
-  > GIT binary patch
-  > literal 4
-  > Lc\${NkU|;|M00aO5
-  > 
-  > diff --git a/mbinary2 b/mbinary2
-  > new file mode 100644
-  > index 0000000000000000000000000000000000000000..112363ac1917b417ffbd7f376ca786a1e5fa7490
-  > GIT binary patch
-  > literal 5
-  > Mc\${NkU|\`?^000jF3jhEB
-  > 
-  > EOF
-  applying patch from stdin
-
-  $ hg tip -q
-  12:30b530085242
-
-  $ hg manifest --debug | grep mbinary
-  045c85ba38952325e126c70962cc0f9d9077bc67 644   mbinary1
-  a874b471193996e7cb034bb301cac7bdaf3e3f46 644   mbinary2
-
-Filenames with spaces:
-
-  $ hg import -d "1000000 0" -m spaces - <<EOF
-  > diff --git a/foo bar b/foo bar
-  > new file mode 100644
-  > index 0000000..257cc56
-  > --- /dev/null
-  > +++ b/foo bar	
-  > @@ -0,0 +1 @@
-  > +foo
-  > EOF
-  applying patch from stdin
-
-  $ hg tip -q
-  13:04750ef42fb3
-
-  $ cat "foo bar"
-  foo
-
-Copy then modify the original file:
-
-  $ hg import -d "1000000 0" -m copy-mod-orig - <<EOF
-  > diff --git a/foo2 b/foo2
-  > index 257cc56..fe08ec6 100644
-  > --- a/foo2
-  > +++ b/foo2
-  > @@ -1 +1,2 @@
-  >  foo
-  > +new line
-  > diff --git a/foo2 b/foo3
-  > similarity index 100%
-  > copy from foo2
-  > copy to foo3
-  > EOF
-  applying patch from stdin
-
-  $ hg tip -q
-  14:c4cd9cdeaa74
-
-  $ cat foo3
-  foo
-
-Move text file and patch as binary
-
-  $ echo a > text2
-  $ hg ci -Am0
-  adding text2
-  $ hg import -d "1000000 0" -m rename-as-binary - <<"EOF"
-  > diff --git a/text2 b/binary2
-  > rename from text2
-  > rename to binary2
-  > index 78981922613b2afb6025042ff6bd878ac1994e85..10efcb362e9f3b3420fcfbfc0e37f3dc16e29757
-  > GIT binary patch
-  > literal 5
-  > Mc$`b*O5$Pw00T?_*Z=?k
-  > 
-  > EOF
-  applying patch from stdin
-
-  $ cat binary2
-  a
-  b
-  \x00 (no-eol) (esc)
-
-  $ hg st --copies --change . 
-  A binary2
-    text2
-  R text2
-  $ cd ..
-
-Consecutive import with renames (issue2459)
-
-  $ hg init issue2459
-  $ cd issue2459
-  $ hg import --no-commit --force - <<EOF
-  > diff --git a/a b/a
-  > new file mode 100644
-  > EOF
-  applying patch from stdin
-  $ hg import --no-commit --force - <<EOF
-  > diff --git a/a b/b
-  > rename from a
-  > rename to b
-  > EOF
-  applying patch from stdin
-  a has not been committed yet, so no copy data will be stored for b.
-  $ hg debugstate
-  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
-  cannot create b: destination already exists
-  1 out of 1 hunks FAILED -- saving rejects to file b.rej
-  abort: patch failed to apply
-  [255]
-  $ cat b
-  b
-
-  $ ln -s b linkb
-  $ hg add linkb
-  $ hg ci -m addlinkb
-  $ hg import --no-commit - <<EOF
-  > diff --git a/linkb b/linkb
-  > deleted file mode 120000
-  > --- a/linkb
-  > +++ /dev/null
-  > @@ -1,1 +0,0 @@
-  > -badhunk
-  > \ No newline at end of file
-  > EOF
-  applying patch from stdin
-  patching file linkb
-  Hunk #1 FAILED at 0
-  1 out of 1 hunks FAILED -- saving rejects to file linkb.rej
-  abort: patch failed to apply
-  [255]
-  $ hg st
-  ? b.rej
-  ? linkb.rej
-
-  $ cd ..
--- a/tests/test-globalopts.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-globalopts.t	Tue Jun 14 20:43:04 2011 -0500
@@ -316,7 +316,7 @@
    remove       remove the specified files on the next commit
    rename       rename files; equivalent of copy + remove
    resolve      redo merges or set/view the merge status of files
-   revert       restore individual files or directories to an earlier state
+   revert       restore files to their checkout state
    rollback     roll back the last transaction (dangerous)
    root         print the root (top) of the current working directory
    serve        start stand-alone webserver
@@ -396,7 +396,7 @@
    remove       remove the specified files on the next commit
    rename       rename files; equivalent of copy + remove
    resolve      redo merges or set/view the merge status of files
-   revert       restore individual files or directories to an earlier state
+   revert       restore files to their checkout state
    rollback     roll back the last transaction (dangerous)
    root         print the root (top) of the current working directory
    serve        start stand-alone webserver
--- a/tests/test-help.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-help.t	Tue Jun 14 20:43:04 2011 -0500
@@ -86,7 +86,7 @@
    remove       remove the specified files on the next commit
    rename       rename files; equivalent of copy + remove
    resolve      redo merges or set/view the merge status of files
-   revert       restore individual files or directories to an earlier state
+   revert       restore files to their checkout state
    rollback     roll back the last transaction (dangerous)
    root         print the root (top) of the current working directory
    serve        start stand-alone webserver
@@ -160,7 +160,7 @@
    remove       remove the specified files on the next commit
    rename       rename files; equivalent of copy + remove
    resolve      redo merges or set/view the merge status of files
-   revert       restore individual files or directories to an earlier state
+   revert       restore files to their checkout state
    rollback     roll back the last transaction (dangerous)
    root         print the root (top) of the current working directory
    serve        start stand-alone webserver
@@ -673,7 +673,7 @@
    remove       remove the specified files on the next commit
    rename       rename files; equivalent of copy + remove
    resolve      redo merges or set/view the merge status of files
-   revert       restore individual files or directories to an earlier state
+   revert       restore files to their checkout state
    rollback     roll back the last transaction (dangerous)
    root         print the root (top) of the current working directory
    serve        start stand-alone webserver
--- a/tests/test-hgweb-commands.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-hgweb-commands.t	Tue Jun 14 20:43:04 2011 -0500
@@ -352,6 +352,35 @@
    <th class="files">files</th>
    <td class="files"><a href="/file/2ef0ac749a14/da/foo">da/foo</a> <a href="/file/2ef0ac749a14/foo">foo</a> </td>
   </tr>
+  <tr>
+    <th class="diffstat">diffstat</th>
+    <td class="diffstat">
+       2 files changed, 2 insertions(+), 0 deletions(-)
+  
+      <a id="diffstatexpand" href="javascript:showDiffstat()"/>[<tt>+</tt>]</a>
+      <div id="diffstatdetails" style="display:none;">
+        <a href="javascript:hideDiffstat()"/>[<tt>-</tt>]</a>
+        <p>
+        <table>  <tr class="parity0">
+      <td class="diffstat-file"><a href="#l1.1">da/foo</a></td>
+      <td class="diffstat-total" align="right">1</td>
+      <td class="diffstat-graph">
+        <span class="diffstat-add" style="width:100.0%;">&nbsp;</span>
+        <span class="diffstat-remove" style="width:0.0%;">&nbsp;</span>
+      </td>
+    </tr>
+    <tr class="parity1">
+      <td class="diffstat-file"><a href="#l2.1">foo</a></td>
+      <td class="diffstat-total" align="right">1</td>
+      <td class="diffstat-graph">
+        <span class="diffstat-add" style="width:100.0%;">&nbsp;</span>
+        <span class="diffstat-remove" style="width:0.0%;">&nbsp;</span>
+      </td>
+    </tr>
+  </table>
+      </div>
+    </td>
+  </tr>
   </table>
   
   <div class="overflow">
@@ -952,7 +981,7 @@
   $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=capabilities'; echo
   200 Script output follows
   
-  lookup changegroupsubset branchmap pushkey known getbundle unbundlehash unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
+  lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
 
 heads
 
--- a/tests/test-hgweb-diffs.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-hgweb-diffs.t	Tue Jun 14 20:43:04 2011 -0500
@@ -94,6 +94,35 @@
    <th class="files">files</th>
    <td class="files"><a href="/file/0cd96de13884/a">a</a> <a href="/file/0cd96de13884/b">b</a> </td>
   </tr>
+  <tr>
+    <th class="diffstat">diffstat</th>
+    <td class="diffstat">
+       2 files changed, 2 insertions(+), 0 deletions(-)
+  
+      <a id="diffstatexpand" href="javascript:showDiffstat()"/>[<tt>+</tt>]</a>
+      <div id="diffstatdetails" style="display:none;">
+        <a href="javascript:hideDiffstat()"/>[<tt>-</tt>]</a>
+        <p>
+        <table>  <tr class="parity0">
+      <td class="diffstat-file"><a href="#l1.1">a</a></td>
+      <td class="diffstat-total" align="right">1</td>
+      <td class="diffstat-graph">
+        <span class="diffstat-add" style="width:100.0%;">&nbsp;</span>
+        <span class="diffstat-remove" style="width:0.0%;">&nbsp;</span>
+      </td>
+    </tr>
+    <tr class="parity1">
+      <td class="diffstat-file"><a href="#l2.1">b</a></td>
+      <td class="diffstat-total" align="right">1</td>
+      <td class="diffstat-graph">
+        <span class="diffstat-add" style="width:100.0%;">&nbsp;</span>
+        <span class="diffstat-remove" style="width:0.0%;">&nbsp;</span>
+      </td>
+    </tr>
+  </table>
+      </div>
+    </td>
+  </tr>
   </table>
   
   <div class="overflow">
@@ -325,6 +354,35 @@
    <th class="files">files</th>
    <td class="files"><a href="/file/0cd96de13884/a">a</a> <a href="/file/0cd96de13884/b">b</a> </td>
   </tr>
+  <tr>
+    <th class="diffstat">diffstat</th>
+    <td class="diffstat">
+       2 files changed, 2 insertions(+), 0 deletions(-)
+  
+      <a id="diffstatexpand" href="javascript:showDiffstat()"/>[<tt>+</tt>]</a>
+      <div id="diffstatdetails" style="display:none;">
+        <a href="javascript:hideDiffstat()"/>[<tt>-</tt>]</a>
+        <p>
+        <table>  <tr class="parity0">
+      <td class="diffstat-file"><a href="#l1.1">a</a></td>
+      <td class="diffstat-total" align="right">1</td>
+      <td class="diffstat-graph">
+        <span class="diffstat-add" style="width:100.0%;">&nbsp;</span>
+        <span class="diffstat-remove" style="width:0.0%;">&nbsp;</span>
+      </td>
+    </tr>
+    <tr class="parity1">
+      <td class="diffstat-file"><a href="#l2.1">b</a></td>
+      <td class="diffstat-total" align="right">1</td>
+      <td class="diffstat-graph">
+        <span class="diffstat-add" style="width:100.0%;">&nbsp;</span>
+        <span class="diffstat-remove" style="width:0.0%;">&nbsp;</span>
+      </td>
+    </tr>
+  </table>
+      </div>
+    </td>
+  </tr>
   </table>
   
   <div class="overflow">
--- a/tests/test-hgweb-removed.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-hgweb-removed.t	Tue Jun 14 20:43:04 2011 -0500
@@ -89,6 +89,27 @@
    <th class="files">files</th>
    <td class="files">a </td>
   </tr>
+  <tr>
+    <th class="diffstat">diffstat</th>
+    <td class="diffstat">
+       1 files changed, 0 insertions(+), 1 deletions(-)
+  
+      <a id="diffstatexpand" href="javascript:showDiffstat()"/>[<tt>+</tt>]</a>
+      <div id="diffstatdetails" style="display:none;">
+        <a href="javascript:hideDiffstat()"/>[<tt>-</tt>]</a>
+        <p>
+        <table>  <tr class="parity0">
+      <td class="diffstat-file"><a href="#l1.1">a</a></td>
+      <td class="diffstat-total" align="right">1</td>
+      <td class="diffstat-graph">
+        <span class="diffstat-add" style="width:0.0%;">&nbsp;</span>
+        <span class="diffstat-remove" style="width:100.0%;">&nbsp;</span>
+      </td>
+    </tr>
+  </table>
+      </div>
+    </td>
+  </tr>
   </table>
   
   <div class="overflow">
--- a/tests/test-hook.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-hook.t	Tue Jun 14 20:43:04 2011 -0500
@@ -346,6 +346,9 @@
   > def brokenhook(**args):
   >     return 1 + {}
   > 
+  > def verbosehook(ui, **args):
+  >     ui.note('verbose output from hook\n')
+  > 
   > class container:
   >     unreachable = 1
   > EOF
@@ -535,3 +538,14 @@
   cb9a9f314b8b
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
+make sure --verbose (and --quiet/--debug etc.) are propogated to the local ui
+that is passed to pre/post hooks
+
+  $ echo '[hooks]' > .hg/hgrc
+  $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc
+  $ hg id
+  cb9a9f314b8b
+  $ hg id --verbose
+  calling hook pre-identify: hooktests.verbosehook
+  verbose output from hook
+  cb9a9f314b8b
--- a/tests/test-http-proxy.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-http-proxy.t	Tue Jun 14 20:43:04 2011 -0500
@@ -102,19 +102,19 @@
   * - - [*] "GET http://localhost:$HGPORT/?cmd=stream_out HTTP/1.1" - - (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
-  * - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
-  * - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
-  * - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
-  * - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
+  * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob)
   * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-import-bypass.t	Tue Jun 14 20:43:04 2011 -0500
@@ -0,0 +1,261 @@
+  $ echo "[extensions]" >> $HGRCPATH
+  $ echo "purge=" >> $HGRCPATH
+  $ echo "graphlog=" >> $HGRCPATH
+
+  $ shortlog() {
+  >     hg glog --template '{rev}:{node|short} {author} {date|hgdate} - {branch} - {desc|firstline}\n'
+  > }
+
+Test --bypass with other options
+
+  $ hg init repo-options
+  $ cd repo-options
+  $ echo a > a
+  $ hg ci -Am adda
+  adding a
+  $ echo a >> a
+  $ hg branch foo
+  marked working directory as branch foo
+  $ hg ci -Am changea
+  $ hg export . > ../test.diff
+  $ hg up null
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+Test importing an existing revision
+
+  $ hg import --bypass --exact ../test.diff
+  applying ../test.diff
+  $ shortlog
+  o  1:4e322f7ce8e3 test 0 0 - foo - changea
+  |
+  o  0:07f494440405 test 0 0 - default - adda
+  
+
+Test failure without --exact
+
+  $ hg import --bypass ../test.diff
+  applying ../test.diff
+  unable to find 'a' for patching
+  abort: patch failed to apply
+  [255]
+  $ hg st
+  $ shortlog
+  o  1:4e322f7ce8e3 test 0 0 - foo - changea
+  |
+  o  0:07f494440405 test 0 0 - default - adda
+  
+
+Test --user, --date and --message
+
+  $ hg up 0
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg import --bypass --u test2 -d '1 0' -m patch2 ../test.diff
+  applying ../test.diff
+  $ cat .hg/last-message.txt
+  patch2 (no-eol)
+  $ shortlog
+  o  2:2e127d1da504 test2 1 0 - default - patch2
+  |
+  | o  1:4e322f7ce8e3 test 0 0 - foo - changea
+  |/
+  @  0:07f494440405 test 0 0 - default - adda
+  
+  $ hg rollback
+  repository tip rolled back to revision 1 (undo commit)
+  working directory now based on revision 0
+
+Test --import-branch
+
+  $ hg import --bypass --import-branch ../test.diff
+  applying ../test.diff
+  $ shortlog
+  o  1:4e322f7ce8e3 test 0 0 - foo - changea
+  |
+  @  0:07f494440405 test 0 0 - default - adda
+  
+  $ hg rollback
+  repository tip rolled back to revision 1 (undo commit)
+  working directory now based on revision 0
+
+Test --strip
+
+  $ hg import --bypass --strip 0 - <<EOF
+  > # HG changeset patch
+  > # User test
+  > # Date 0 0
+  > # Branch foo
+  > # Node ID 4e322f7ce8e3e4203950eac9ece27bf7e45ffa6c
+  > # Parent  07f4944404050f47db2e5c5071e0e84e7a27bba9
+  > changea
+  > 
+  > diff -r 07f494440405 -r 4e322f7ce8e3 a
+  > --- a	Thu Jan 01 00:00:00 1970 +0000
+  > +++ a	Thu Jan 01 00:00:00 1970 +0000
+  > @@ -1,1 +1,2 @@
+  >  a
+  > +a
+  > EOF
+  applying patch from stdin
+  $ hg rollback
+  repository tip rolled back to revision 1 (undo commit)
+  working directory now based on revision 0
+
+Test unsupported combinations
+
+  $ hg import --bypass --no-commit ../test.diff
+  abort: cannot use --no-commit with --bypass
+  [255]
+  $ hg import --bypass --similarity 50 ../test.diff
+  abort: cannot use --similarity with --bypass
+  [255]
+
+Test commit editor
+
+  $ hg diff -c 1 > ../test.diff
+  $ HGEDITOR=cat hg import --bypass ../test.diff
+  applying ../test.diff
+  
+  
+  HG: Enter commit message.  Lines beginning with 'HG:' are removed.
+  HG: Leave message empty to abort commit.
+  HG: --
+  HG: user: test
+  HG: branch 'default'
+  HG: changed a
+  abort: empty commit message
+  [255]
+
+Test patch.eol is handled
+
+  $ python -c 'file("a", "wb").write("a\r\n")'
+  $ hg ci -m makeacrlf
+  $ hg import -m 'should fail because of eol' --bypass ../test.diff
+  applying ../test.diff
+  patching file a
+  Hunk #1 FAILED at 0
+  abort: patch failed to apply
+  [255]
+  $ hg --config patch.eol=auto import -d '0 0' -m 'test patch.eol' --bypass ../test.diff
+  applying ../test.diff
+  $ shortlog
+  o  3:d7805b4d2cb3 test 0 0 - default - test patch.eol
+  |
+  @  2:872023de769d test 0 0 - default - makeacrlf
+  |
+  | o  1:4e322f7ce8e3 test 0 0 - foo - changea
+  |/
+  o  0:07f494440405 test 0 0 - default - adda
+  
+
+Test applying multiple patches
+
+  $ hg up -qC 0
+  $ echo e > e
+  $ hg ci -Am adde
+  adding e
+  created new head
+  $ hg export . > ../patch1.diff
+  $ hg up -qC 1
+  $ echo f > f
+  $ hg ci -Am addf
+  adding f
+  $ hg export . > ../patch2.diff
+  $ cd ..
+  $ hg clone -r1 repo-options repo-multi1
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 1 files
+  updating to branch foo
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd repo-multi1
+  $ hg up 0
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg import --bypass ../patch1.diff ../patch2.diff
+  applying ../patch1.diff
+  applying ../patch2.diff
+  applied 16581080145e
+  $ shortlog
+  o  3:bc8ca3f8a7c4 test 0 0 - default - addf
+  |
+  o  2:16581080145e test 0 0 - default - adde
+  |
+  | o  1:4e322f7ce8e3 test 0 0 - foo - changea
+  |/
+  @  0:07f494440405 test 0 0 - default - adda
+  
+
+Test applying multiple patches with --exact
+
+  $ cd ..
+  $ hg clone -r1 repo-options repo-multi2
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 1 files
+  updating to branch foo
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd repo-multi2
+  $ hg import --bypass --exact ../patch1.diff ../patch2.diff
+  applying ../patch1.diff
+  applying ../patch2.diff
+  applied 16581080145e
+  $ shortlog
+  o  3:d60cb8989666 test 0 0 - foo - addf
+  |
+  | o  2:16581080145e test 0 0 - default - adde
+  | |
+  @ |  1:4e322f7ce8e3 test 0 0 - foo - changea
+  |/
+  o  0:07f494440405 test 0 0 - default - adda
+  
+
+  $ cd ..
+
+Test complicated patch with --exact
+
+  $ hg init repo-exact
+  $ cd repo-exact
+  $ echo a > a
+  $ echo c > c
+  $ echo d > d
+  $ echo e > e
+  $ echo f > f
+  $ chmod +x f
+  $ ln -s c linkc
+  $ hg ci -Am t
+  adding a
+  adding c
+  adding d
+  adding e
+  adding f
+  adding linkc
+  $ hg cp a aa1
+  $ echo b >> a
+  $ echo b > b
+  $ hg add b
+  $ hg cp a aa2
+  $ echo aa >> aa2
+  $ chmod +x e
+  $ chmod -x f
+  $ ln -s a linka
+  $ hg rm d
+  $ hg rm linkc
+  $ hg mv c cc
+  $ hg ci -m patch
+  $ hg export --git . > ../test.diff
+  $ hg up -C null
+  0 files updated, 0 files merged, 7 files removed, 0 files unresolved
+  $ hg purge
+  $ hg st
+  $ hg import --bypass --exact ../test.diff
+  applying ../test.diff
+
+The patch should have matched the exported revision and generated no additional
+data. If not, diff both heads to debug it.
+
+  $ shortlog
+  o  1:2978fd5c8aa4 test 0 0 - default - patch
+  |
+  o  0:a0e19e636a43 test 0 0 - default - t
+  
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-import-git.t	Tue Jun 14 20:43:04 2011 -0500
@@ -0,0 +1,468 @@
+
+  $ hg init
+
+New file:
+
+  $ hg import -d "1000000 0" -mnew - <<EOF
+  > diff --git a/new b/new
+  > new file mode 100644
+  > index 0000000..7898192
+  > --- /dev/null
+  > +++ b/new
+  > @@ -0,0 +1 @@
+  > +a
+  > EOF
+  applying patch from stdin
+
+  $ hg tip -q
+  0:ae3ee40d2079
+
+New empty file:
+
+  $ hg import -d "1000000 0" -mempty - <<EOF
+  > diff --git a/empty b/empty
+  > new file mode 100644
+  > EOF
+  applying patch from stdin
+
+  $ hg tip -q
+  1:ab199dc869b5
+
+  $ hg locate empty
+  empty
+
+chmod +x:
+
+  $ hg import -d "1000000 0" -msetx - <<EOF
+  > diff --git a/new b/new
+  > old mode 100644
+  > new mode 100755
+  > EOF
+  applying patch from stdin
+
+  $ hg tip -q
+  2:3a34410f282e
+
+  $ test -x new
+
+Copy:
+
+  $ hg import -d "1000000 0" -mcopy - <<EOF
+  > diff --git a/new b/copy
+  > old mode 100755
+  > new mode 100644
+  > similarity index 100%
+  > copy from new
+  > copy to copy
+  > diff --git a/new b/copyx
+  > similarity index 100%
+  > copy from new
+  > copy to copyx
+  > EOF
+  applying patch from stdin
+
+  $ hg tip -q
+  3:37bacb7ca14d
+
+  $ if "$TESTDIR/hghave" -q execbit; then
+  >     test -f copy -a ! -x copy || echo bad
+  >     test -x copyx || echo bad
+  > else
+  >     test -f copy || echo bad
+  > fi
+
+  $ cat copy
+  a
+
+  $ hg cat copy
+  a
+
+Rename:
+
+  $ hg import -d "1000000 0" -mrename - <<EOF
+  > diff --git a/copy b/rename
+  > similarity index 100%
+  > rename from copy
+  > rename to rename
+  > EOF
+  applying patch from stdin
+
+  $ hg tip -q
+  4:47b81a94361d
+
+  $ hg locate
+  copyx
+  empty
+  new
+  rename
+
+Delete:
+
+  $ hg import -d "1000000 0" -mdelete - <<EOF
+  > diff --git a/copyx b/copyx
+  > deleted file mode 100755
+  > index 7898192..0000000
+  > --- a/copyx
+  > +++ /dev/null
+  > @@ -1 +0,0 @@
+  > -a
+  > EOF
+  applying patch from stdin
+
+  $ hg tip -q
+  5:d9b001d98336
+
+  $ hg locate
+  empty
+  new
+  rename
+
+  $ test -f copyx
+  [1]
+
+Regular diff:
+
+  $ hg import -d "1000000 0" -mregular - <<EOF
+  > diff --git a/rename b/rename
+  > index 7898192..72e1fe3 100644
+  > --- a/rename
+  > +++ b/rename
+  > @@ -1 +1,5 @@
+  >  a
+  > +a
+  > +a
+  > +a
+  > +a
+  > EOF
+  applying patch from stdin
+
+  $ hg tip -q
+  6:ebe901e7576b
+
+Copy and modify:
+
+  $ hg import -d "1000000 0" -mcopymod - <<EOF
+  > diff --git a/rename b/copy2
+  > similarity index 80%
+  > copy from rename
+  > copy to copy2
+  > index 72e1fe3..b53c148 100644
+  > --- a/rename
+  > +++ b/copy2
+  > @@ -1,5 +1,5 @@
+  >  a
+  >  a
+  > -a
+  > +b
+  >  a
+  >  a
+  > EOF
+  applying patch from stdin
+
+  $ hg tip -q
+  7:18f368958ecd
+
+  $ hg cat copy2
+  a
+  a
+  b
+  a
+  a
+
+Rename and modify:
+
+  $ hg import -d "1000000 0" -mrenamemod - <<EOF
+  > diff --git a/copy2 b/rename2
+  > similarity index 80%
+  > rename from copy2
+  > rename to rename2
+  > index b53c148..8f81e29 100644
+  > --- a/copy2
+  > +++ b/rename2
+  > @@ -1,5 +1,5 @@
+  >  a
+  >  a
+  >  b
+  > -a
+  > +c
+  >  a
+  > EOF
+  applying patch from stdin
+
+  $ hg tip -q
+  8:c32b0d7e6f44
+
+  $ hg locate copy2
+  [1]
+  $ hg cat rename2
+  a
+  a
+  b
+  c
+  a
+
+One file renamed multiple times:
+
+  $ hg import -d "1000000 0" -mmultirenames - <<EOF
+  > diff --git a/rename2 b/rename3
+  > rename from rename2
+  > rename to rename3
+  > diff --git a/rename2 b/rename3-2
+  > rename from rename2
+  > rename to rename3-2
+  > EOF
+  applying patch from stdin
+
+  $ hg tip -q
+  9:034a6bf95330
+
+  $ hg log -vr. --template '{rev} {files} / {file_copies}\n'
+  9 rename2 rename3 rename3-2 / rename3 (rename2)rename3-2 (rename2)
+
+  $ hg locate rename2 rename3 rename3-2
+  rename3
+  rename3-2
+
+  $ hg cat rename3
+  a
+  a
+  b
+  c
+  a
+
+  $ hg cat rename3-2
+  a
+  a
+  b
+  c
+  a
+
+  $ echo foo > foo
+  $ hg add foo
+  $ hg ci -m 'add foo'
+
+Binary files and regular patch hunks:
+
+  $ hg import -d "1000000 0" -m binaryregular - <<EOF
+  > diff --git a/binary b/binary
+  > new file mode 100644
+  > index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4
+  > GIT binary patch
+  > literal 4
+  > Lc\${NkU|;|M00aO5
+  > 
+  > diff --git a/foo b/foo2
+  > rename from foo
+  > rename to foo2
+  > EOF
+  applying patch from stdin
+
+  $ hg tip -q
+  11:c39bce63e786
+
+  $ cat foo2
+  foo
+
+  $ hg manifest --debug | grep binary
+  045c85ba38952325e126c70962cc0f9d9077bc67 644   binary
+
+Multiple binary files:
+
+  $ hg import -d "1000000 0" -m multibinary - <<EOF
+  > diff --git a/mbinary1 b/mbinary1
+  > new file mode 100644
+  > index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4
+  > GIT binary patch
+  > literal 4
+  > Lc\${NkU|;|M00aO5
+  > 
+  > diff --git a/mbinary2 b/mbinary2
+  > new file mode 100644
+  > index 0000000000000000000000000000000000000000..112363ac1917b417ffbd7f376ca786a1e5fa7490
+  > GIT binary patch
+  > literal 5
+  > Mc\${NkU|\`?^000jF3jhEB
+  > 
+  > EOF
+  applying patch from stdin
+
+  $ hg tip -q
+  12:30b530085242
+
+  $ hg manifest --debug | grep mbinary
+  045c85ba38952325e126c70962cc0f9d9077bc67 644   mbinary1
+  a874b471193996e7cb034bb301cac7bdaf3e3f46 644   mbinary2
+
+Filenames with spaces:
+
+  $ hg import -d "1000000 0" -m spaces - <<EOF
+  > diff --git a/foo bar b/foo bar
+  > new file mode 100644
+  > index 0000000..257cc56
+  > --- /dev/null
+  > +++ b/foo bar	
+  > @@ -0,0 +1 @@
+  > +foo
+  > EOF
+  applying patch from stdin
+
+  $ hg tip -q
+  13:04750ef42fb3
+
+  $ cat "foo bar"
+  foo
+
+Copy then modify the original file:
+
+  $ hg import -d "1000000 0" -m copy-mod-orig - <<EOF
+  > diff --git a/foo2 b/foo2
+  > index 257cc56..fe08ec6 100644
+  > --- a/foo2
+  > +++ b/foo2
+  > @@ -1 +1,2 @@
+  >  foo
+  > +new line
+  > diff --git a/foo2 b/foo3
+  > similarity index 100%
+  > copy from foo2
+  > copy to foo3
+  > EOF
+  applying patch from stdin
+
+  $ hg tip -q
+  14:c4cd9cdeaa74
+
+  $ cat foo3
+  foo
+
+Move text file and patch as binary
+
+  $ echo a > text2
+  $ hg ci -Am0
+  adding text2
+  $ hg import -d "1000000 0" -m rename-as-binary - <<"EOF"
+  > diff --git a/text2 b/binary2
+  > rename from text2
+  > rename to binary2
+  > index 78981922613b2afb6025042ff6bd878ac1994e85..10efcb362e9f3b3420fcfbfc0e37f3dc16e29757
+  > GIT binary patch
+  > literal 5
+  > Mc$`b*O5$Pw00T?_*Z=?k
+  > 
+  > EOF
+  applying patch from stdin
+
+  $ cat binary2
+  a
+  b
+  \x00 (no-eol) (esc)
+
+  $ hg st --copies --change . 
+  A binary2
+    text2
+  R text2
+  $ cd ..
+
+Consecutive import with renames (issue2459)
+
+  $ hg init issue2459
+  $ cd issue2459
+  $ hg import --no-commit --force - <<EOF
+  > diff --git a/a b/a
+  > new file mode 100644
+  > EOF
+  applying patch from stdin
+  $ hg import --no-commit --force - <<EOF
+  > diff --git a/a b/b
+  > rename from a
+  > rename to b
+  > EOF
+  applying patch from stdin
+  a has not been committed yet, so no copy data will be stored for b.
+  $ hg debugstate
+  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
+  cannot create b: destination already exists
+  1 out of 1 hunks FAILED -- saving rejects to file b.rej
+  abort: patch failed to apply
+  [255]
+  $ cat b
+  b
+
+  $ ln -s b linkb
+  $ hg add linkb
+  $ hg ci -m addlinkb
+  $ hg import --no-commit - <<EOF
+  > diff --git a/linkb b/linkb
+  > deleted file mode 120000
+  > --- a/linkb
+  > +++ /dev/null
+  > @@ -1,1 +0,0 @@
+  > -badhunk
+  > \ No newline at end of file
+  > EOF
+  applying patch from stdin
+  patching file linkb
+  Hunk #1 FAILED at 0
+  1 out of 1 hunks FAILED -- saving rejects to file linkb.rej
+  abort: patch failed to apply
+  [255]
+  $ hg st
+  ? b.rej
+  ? linkb.rej
+
+  $ cd ..
--- a/tests/test-mq-merge.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-mq-merge.t	Tue Jun 14 20:43:04 2011 -0500
@@ -130,7 +130,7 @@
   patch failed, unable to continue (try -v)
   patch failed, rejects left in working dir
   patch didn't work out, merging patcha
-  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   0 files updated, 2 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   applying patcha2
--- a/tests/test-notify.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-notify.t	Tue Jun 14 20:43:04 2011 -0500
@@ -32,6 +32,8 @@
     incoming.notify = python:hgext.notify.hook
     # batch emails when many changesets incoming at one time
     changegroup.notify = python:hgext.notify.hook
+    # batch emails when many changesets outgoing at one time (client side)
+    outgoing.notify = python:hgext.notify.hook
   
     [notify]
     # config items go here
@@ -48,7 +50,8 @@
     style = ...            # style file to use when formatting email
     template = ...         # template to use when formatting email
     incoming = ...         # template to use when run as incoming hook
-    changegroup = ...      # template when run as changegroup hook
+    outgoing = ...         # template to use when run as outgoing hook
+    changegroup = ...      # template to use when run as changegroup hook
     maxdiff = 300          # max lines of diffs to include (0=none, -1=all)
     maxsubject = 67        # truncate subject line longer than this
     diffstat = True        # add a diffstat before the diff content
--- a/tests/test-push-warn.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-push-warn.t	Tue Jun 14 20:43:04 2011 -0500
@@ -35,7 +35,7 @@
   searching for changes
   taking quick initial sample
   searching: 2 queries
-  query 2; still undecided: 2, sample size is: 2
+  query 2; still undecided: 1, sample size is: 1
   2 total queries
   new remote heads on branch 'default'
   new remote head 1e108cc5548c
--- a/tests/test-qrecord.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-qrecord.t	Tue Jun 14 20:43:04 2011 -0500
@@ -64,6 +64,9 @@
    -l --logfile FILE         read commit message from file
    -d --date DATE            record the specified date as commit date
    -u --user USER            record the specified user as committer
+   -w --ignore-all-space     ignore white space when comparing lines
+   -b --ignore-space-change  ignore changes in the amount of white space
+   -B --ignore-blank-lines   ignore changes whose lines are all blank
   
   [+] marked option can be specified multiple times
   
@@ -136,6 +139,9 @@
    -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
+   -w --ignore-all-space     ignore white space when comparing lines
+   -b --ignore-space-change  ignore changes in the amount of white space
+   -B --ignore-blank-lines   ignore changes whose lines are all blank
       --mq                   operate on patch repository
   
   [+] marked option can be specified multiple times
--- a/tests/test-rebase-mq.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-rebase-mq.t	Tue Jun 14 20:43:04 2011 -0500
@@ -250,6 +250,7 @@
   $ hg qnew foo
   $ hg qguard foo +baz
   $ echo foo > foo
+  $ hg add foo
   $ hg qref
   $ hg qpop
   popping foo
@@ -258,6 +259,7 @@
   $ hg qnew bar
   $ hg qguard bar +baz
   $ echo bar > bar
+  $ hg add bar
   $ hg qref
 
   $ hg qguard -l
@@ -272,13 +274,13 @@
 Create new head to rebase bar onto:
 
   $ hg up -C 0
-  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ echo b > b
   $ hg add b
   $ hg ci -m b
   created new head
   $ hg up -C 1
-  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ echo a >> a
   $ hg qref
 
@@ -290,13 +292,19 @@
   o  0:* 'a' tags: qparent (glob)
   
 
-Rebase bar:
+Rebase bar (make sure series order is preserved):
 
+  $ hg qseries
+  bar
+  foo
   $ hg -q rebase -d 1
+  $ hg qseries
+  bar
+  foo
 
   $ hg qguard -l
+  bar: +baz
   foo: +baz
-  bar: +baz
 
   $ hg tglog
   @  2:* '[mq]: bar' tags: bar qbase qtip tip (glob)
--- a/tests/test-revert.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-revert.t	Tue Jun 14 20:43:04 2011 -0500
@@ -185,7 +185,8 @@
 should fail - no arguments
 
   $ hg revert -rtip
-  abort: no files or directories specified; use --all to revert the whole repo
+  abort: no files or directories specified
+  (use --all to revert all files)
   [255]
 
 should succeed
--- a/tests/test-schemes.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-schemes.t	Tue Jun 14 20:43:04 2011 -0500
@@ -28,7 +28,7 @@
   sending capabilities command
   comparing with parts://localhost/
   query 1; heads
-  sending heads command
+  sending batch command
   searching for changes
   all remote heads known locally
   no changes found
--- a/tests/test-setdiscovery.t	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-setdiscovery.t	Tue Jun 14 20:43:04 2011 -0500
@@ -44,10 +44,7 @@
   comparing with b
   query 1; heads
   searching for changes
-  taking initial sample
-  searching: 2 queries
-  query 2; still undecided: 4, sample size is: 4
-  2 total queries
+  all local heads known remotely
   common heads: b5714e113bc0 01241442b3c2
   local is subset
   
@@ -83,9 +80,9 @@
   comparing with b
   query 1; heads
   searching for changes
-  taking quick initial sample
+  taking initial sample
   searching: 2 queries
-  query 2; still undecided: 35, sample size is: 35
+  query 2; still undecided: 29, sample size is: 29
   2 total queries
   common heads: bebd167eb94d
   
@@ -101,7 +98,7 @@
   searching for changes
   taking initial sample
   searching: 2 queries
-  query 2; still undecided: 3, sample size is: 3
+  query 2; still undecided: 2, sample size is: 2
   2 total queries
   common heads: bebd167eb94d
 
@@ -122,9 +119,9 @@
   comparing with b
   query 1; heads
   searching for changes
-  taking quick initial sample
+  taking initial sample
   searching: 2 queries
-  query 2; still undecided: 34, sample size is: 34
+  query 2; still undecided: 29, sample size is: 29
   2 total queries
   common heads: 2dc09a01254d
   
@@ -140,7 +137,7 @@
   searching for changes
   taking initial sample
   searching: 2 queries
-  query 2; still undecided: 30, sample size is: 30
+  query 2; still undecided: 29, sample size is: 29
   2 total queries
   common heads: 2dc09a01254d
 
@@ -163,7 +160,7 @@
   searching for changes
   taking quick initial sample
   searching: 2 queries
-  query 2; still undecided: 32, sample size is: 32
+  query 2; still undecided: 31, sample size is: 31
   2 total queries
   common heads: 66f7d451a68b
   
@@ -179,7 +176,7 @@
   searching for changes
   taking quick initial sample
   searching: 2 queries
-  query 2; still undecided: 32, sample size is: 32
+  query 2; still undecided: 31, sample size is: 31
   2 total queries
   common heads: 66f7d451a68b
 
@@ -202,7 +199,7 @@
   searching for changes
   taking quick initial sample
   searching: 2 queries
-  query 2; still undecided: 52, sample size is: 52
+  query 2; still undecided: 51, sample size is: 51
   2 total queries
   common heads: 66f7d451a68b
   
@@ -218,7 +215,7 @@
   searching for changes
   taking quick initial sample
   searching: 2 queries
-  query 2; still undecided: 32, sample size is: 32
+  query 2; still undecided: 31, sample size is: 31
   2 total queries
   common heads: 66f7d451a68b
 
@@ -241,7 +238,7 @@
   searching for changes
   taking quick initial sample
   searching: 2 queries
-  query 2; still undecided: 1050, sample size is: 11
+  query 2; still undecided: 1049, sample size is: 11
   sampling from both directions
   searching: 3 queries
   query 3; still undecided: 31, sample size is: 31
@@ -260,7 +257,7 @@
   searching for changes
   taking quick initial sample
   searching: 2 queries
-  query 2; still undecided: 1030, sample size is: 11
+  query 2; still undecided: 1029, sample size is: 11
   sampling from both directions
   searching: 3 queries
   query 3; still undecided: 16, sample size is: 16
--- a/tests/test-symlink-os-yes-fs-no.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-symlink-os-yes-fs-no.py	Tue Jun 14 20:43:04 2011 -0500
@@ -10,7 +10,7 @@
 
 # clone with symlink support
 u = ui.ui()
-hg.clone(u, BUNDLEPATH, 'test0')
+hg.clone(u, {}, BUNDLEPATH, 'test0')
 
 repo = hg.repository(u, 'test0')
 
@@ -39,4 +39,4 @@
 
 # try cloning a repo which contains symlinks
 u = ui.ui()
-hg.clone(u, BUNDLEPATH, 'test1')
+hg.clone(u, {}, BUNDLEPATH, 'test1')
--- a/tests/test-ui-color.py	Tue Jun 14 00:31:56 2011 +0400
+++ b/tests/test-ui-color.py	Tue Jun 14 20:43:04 2011 -0500
@@ -19,13 +19,13 @@
 ui_ = ui.ui()
 ui_.setconfig('ui', 'formatted', 'True')
 
+# we're not interested in the output, so write that to devnull
+ui_.fout = open(os.devnull, 'w')
+
 # call some arbitrary command just so we go through
 # color's wrapped _runcommand twice.
-# we're not interested in the output, so write that to devnull
 def runcmd():
-    sys.stdout = open(os.devnull, 'w')
     dispatch.dispatch(dispatch.request(['version', '-q'], ui_))
-    sys.stdout = sys.__stdout__
 
 runcmd()
 print "colored? " + str(issubclass(ui_.__class__, color.colorui))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-wireprotocol.py	Tue Jun 14 20:43:04 2011 -0500
@@ -0,0 +1,45 @@
+from mercurial import wireproto
+
+class proto():
+    def __init__(self, args):
+        self.args = args
+    def getargs(self, spec):
+        args = self.args
+        args.setdefault('*', {})
+        names = spec.split()
+        return [args[n] for n in names]
+
+class clientrepo(wireproto.wirerepository):
+    def __init__(self, serverrepo):
+        self.serverrepo = serverrepo
+    def _call(self, cmd, **args):
+        return wireproto.dispatch(self.serverrepo, proto(args), cmd)
+
+    @wireproto.batchable
+    def greet(self, name):
+        f = wireproto.future()
+        yield wireproto.todict(name=mangle(name)), f
+        yield unmangle(f.value)
+
+class serverrepo():
+    def greet(self, name):
+        return "Hello, " + name
+
+def mangle(s):
+    return ''.join(chr(ord(c) + 1) for c in s)
+def unmangle(s):
+    return ''.join(chr(ord(c) - 1) for c in s)
+
+def greet(repo, proto, name):
+    return mangle(repo.greet(unmangle(name)))
+
+wireproto.commands['greet'] = (greet, 'name',)
+
+srv = serverrepo()
+clt = clientrepo(srv)
+
+print clt.greet("Foobar")
+b = clt.batch()
+fs = [b.greet(s) for s in ["Fo, =;o", "Bar"]]
+b.submit()
+print [f.value for f in fs]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-wireprotocol.py.out	Tue Jun 14 20:43:04 2011 -0500
@@ -0,0 +1,2 @@
+Hello, Foobar
+['Hello, Fo, =;o', 'Hello, Bar']