changeset 14665:d89f80898178

merge with i18n
author Matt Mackall <mpm@selenic.com>
date Fri, 17 Jun 2011 15:43:50 -0500
parents 0ae98cd2a83f (diff) 71b9c29eb44a (current diff)
children 27b080aa880a
files
diffstat 30 files changed, 450 insertions(+), 75 deletions(-) [+]
line wrap: on
line diff
--- a/doc/hgmanpage.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/doc/hgmanpage.py	Fri Jun 17 15:43:50 2011 -0500
@@ -48,6 +48,7 @@
 
 from docutils import nodes, writers, languages
 import roman
+import inspect
 
 FIELD_LIST_INDENT = 7
 DEFINITION_LIST_INDENT = 7
@@ -160,7 +161,12 @@
         nodes.NodeVisitor.__init__(self, document)
         self.settings = settings = document.settings
         lcode = settings.language_code
-        self.language = languages.get_language(lcode)
+        arglen = len(inspect.getargspec(languages.get_language)[0])
+        if arglen == 2:
+            self.language = languages.get_language(lcode,
+                                                   self.document.reporter)
+        else:
+            self.language = languages.get_language(lcode)
         self.head = []
         self.body = []
         self.foot = []
--- a/hgext/fetch.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/hgext/fetch.py	Fri Jun 17 15:43:50 2011 -0500
@@ -122,7 +122,7 @@
 
         if not err:
             # we don't translate commit messages
-            message = (cmdutil.logmessage(opts) or
+            message = (cmdutil.logmessage(ui, opts) or
                        ('Automated merge with %s' %
                         util.removeauth(other.url())))
             editor = cmdutil.commiteditor
--- a/hgext/mq.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/hgext/mq.py	Fri Jun 17 15:43:50 2011 -0500
@@ -48,7 +48,7 @@
 from mercurial import commands, cmdutil, hg, scmutil, util, revset
 from mercurial import repair, extensions, url, error
 from mercurial import patch as patchmod
-import os, sys, re, errno, shutil
+import os, re, errno, shutil
 
 commands.norepo += " qclone"
 
@@ -1831,7 +1831,7 @@
                 self.checkpatchname(patchname, force)
                 try:
                     if filename == '-':
-                        text = sys.stdin.read()
+                        text = self.ui.fin.read()
                     else:
                         fp = url.open(self.ui, filename)
                         text = fp.read()
@@ -2211,7 +2211,7 @@
 
     Returns 0 on successful creation of a new patch.
     """
-    msg = cmdutil.logmessage(opts)
+    msg = cmdutil.logmessage(ui, opts)
     def getmsg():
         return ui.edit(msg, opts.get('user') or ui.username())
     q = repo.mq
@@ -2262,7 +2262,7 @@
     Returns 0 on success.
     """
     q = repo.mq
-    message = cmdutil.logmessage(opts)
+    message = cmdutil.logmessage(ui, opts)
     if opts.get('edit'):
         if not q.applied:
             ui.write(_("no patches applied\n"))
@@ -2332,7 +2332,7 @@
         raise util.Abort(_('no patches applied'))
     q.checklocalchanges(repo)
 
-    message = cmdutil.logmessage(opts)
+    message = cmdutil.logmessage(ui, opts)
     if opts.get('edit'):
         if message:
             raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
@@ -2661,7 +2661,7 @@
 
     This command is deprecated, use :hg:`rebase` instead."""
     q = repo.mq
-    message = cmdutil.logmessage(opts)
+    message = cmdutil.logmessage(ui, opts)
     ret = q.save(repo, msg=message)
     if ret:
         return ret
--- a/hgext/rebase.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/hgext/rebase.py	Fri Jun 17 15:43:50 2011 -0500
@@ -117,7 +117,7 @@
         contf = opts.get('continue')
         abortf = opts.get('abort')
         collapsef = opts.get('collapse', False)
-        collapsemsg = cmdutil.logmessage(opts)
+        collapsemsg = cmdutil.logmessage(ui, opts)
         extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
         keepf = opts.get('keep', False)
         keepbranchesf = opts.get('keepbranches', False)
--- a/i18n/pt_BR.po	Wed Jun 15 10:40:23 2011 +0200
+++ b/i18n/pt_BR.po	Fri Jun 17 15:43:50 2011 -0500
@@ -4817,8 +4817,8 @@
 msgid "only a local queue repository may be initialized"
 msgstr "apenas um repositório de fila local pode ser inicializado"
 
-msgid "there is no Mercurial repository here (.hg not found)"
-msgstr "não há um repositório do Mercurial aqui (.hg não encontrado)"
+msgid "no repository found in %r (.hg not found)"
+msgstr "não foi encontrado um repositório em %r (.hg não encontrado)"
 
 msgid "no queue repository"
 msgstr "repositório da fila não existente"
--- a/mercurial/changelog.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/changelog.py	Fri Jun 17 15:43:50 2011 -0500
@@ -107,6 +107,8 @@
         self._realopener = opener
         self._delayed = False
         self._divert = False
+        # hiddenrevs: revs that should be hidden by command and tools
+        self.hiddenrevs = set()
 
     def delayupdate(self):
         "delay visibility of index updates to other readers"
--- a/mercurial/cmdutil.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/cmdutil.py	Fri Jun 17 15:43:50 2011 -0500
@@ -76,7 +76,7 @@
     if modified or added or removed or deleted:
         raise util.Abort(_("outstanding uncommitted changes"))
 
-def logmessage(opts):
+def logmessage(ui, opts):
     """ get the log message according to -m and -l option """
     message = opts.get('message')
     logfile = opts.get('logfile')
@@ -87,7 +87,7 @@
     if not message and logfile:
         try:
             if logfile == '-':
-                message = sys.stdin.read()
+                message = ui.fin.read()
             else:
                 message = '\n'.join(util.readfile(logfile).splitlines())
         except IOError, inst:
@@ -160,8 +160,23 @@
     writable = mode not in ('r', 'rb')
 
     if not pat or pat == '-':
-        fp = writable and sys.stdout or sys.stdin
-        return os.fdopen(os.dup(fp.fileno()), mode)
+        fp = writable and repo.ui.fout or repo.ui.fin
+        if hasattr(fp, 'fileno'):
+            return os.fdopen(os.dup(fp.fileno()), mode)
+        else:
+            # if this fp can't be duped properly, return
+            # a dummy object that can be closed
+            class wrappedfileobj(object):
+                noop = lambda x: None
+                def __init__(self, f):
+                    self.f = f
+                def __getattr__(self, attr):
+                    if attr == 'close':
+                        return self.noop
+                    else:
+                        return getattr(self.f, attr)
+
+            return wrappedfileobj(fp)
     if hasattr(pat, 'write') and writable:
         return pat
     if hasattr(pat, 'read') and 'r' in mode:
@@ -1163,7 +1178,7 @@
     date = opts.get('date')
     if date:
         opts['date'] = util.parsedate(date)
-    message = logmessage(opts)
+    message = logmessage(ui, opts)
 
     # extract addremove carefully -- this function can be called from a command
     # that doesn't support addremove
--- a/mercurial/commands.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/commands.py	Fri Jun 17 15:43:50 2011 -0500
@@ -8,10 +8,11 @@
 from node import hex, bin, nullid, nullrev, short
 from lock import release
 from i18n import _, gettext
-import os, re, sys, difflib, time, tempfile, errno
+import os, re, difflib, time, tempfile, errno
 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
 import patch, help, url, encoding, templatekw, discovery
-import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
+import archival, changegroup, cmdutil, hbisect
+import sshserver, hgweb, hgweb.server, commandserver
 import merge as mergemod
 import minirst, revset, fileset
 import dagparser, context, simplemerge
@@ -336,7 +337,7 @@
     if dest == '-':
         if kind == 'files':
             raise util.Abort(_('cannot archive plain files to stdout'))
-        dest = sys.stdout
+        dest = ui.fout
         if not prefix:
             prefix = os.path.basename(repo.root) + '-%h'
 
@@ -765,6 +766,12 @@
     Use the command :hg:`update` to switch to an existing branch. Use
     :hg:`commit --close-branch` to mark this branch as closed.
 
+    .. note::
+
+       Branch names are permanent. Use :hg:`bookmark` to create a
+       light-weight bookmark instead. See :hg:`help glossary` for more
+       information about named branches and bookmarks.
+
     Returns 0 on success.
     """
 
@@ -1226,7 +1233,7 @@
 
     if text is None:
         ui.status(_("reading DAG from stdin\n"))
-        text = sys.stdin.read()
+        text = ui.fin.read()
 
     cl = repo.changelog
     if len(cl) > 0:
@@ -3091,7 +3098,7 @@
         commitid = _('to working directory')
 
         try:
-            cmdline_message = cmdutil.logmessage(opts)
+            cmdline_message = cmdutil.logmessage(ui, opts)
             if cmdline_message:
                 # pickup the cmdline msg
                 message = cmdline_message
@@ -3188,7 +3195,7 @@
 
             if pf == '-':
                 ui.status(_("applying patch from stdin\n"))
-                pf = sys.stdin
+                pf = ui.fin
             else:
                 ui.status(_("applying %s\n") % p)
                 pf = url.open(ui, pf)
@@ -3339,6 +3346,7 @@
      _('show changesets within the given named branch'), _('BRANCH')),
     ('P', 'prune', [],
      _('do not display revision or any of its ancestors'), _('REV')),
+    ('h', 'hidden', False, _('show hidden changesets')),
     ] + logopts + walkopts,
     _('[OPTION]... [FILE]'))
 def log(ui, repo, *pats, **opts):
@@ -3400,6 +3408,8 @@
             return
         if opts.get('branch') and ctx.branch() not in opts['branch']:
             return
+        if not opts.get('hidden') and ctx.hidden():
+            return
         if df and not df(ctx.date()[0]):
             return
         if opts['user'] and not [k for k in opts['user']
@@ -4409,6 +4419,7 @@
      _('FILE')),
     ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
     ('', 'stdio', None, _('for remote clients')),
+    ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
     ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
     ('', 'style', '', _('template style to use'), _('STYLE')),
     ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
@@ -4439,13 +4450,24 @@
     Returns 0 on success.
     """
 
-    if opts["stdio"]:
+    if opts["stdio"] and opts["cmdserver"]:
+        raise util.Abort(_("cannot use --stdio with --cmdserver"))
+
+    def checkrepo():
         if repo is None:
             raise error.RepoError(_("There is no Mercurial repository here"
                               " (.hg not found)"))
+
+    if opts["stdio"]:
+        checkrepo()
         s = sshserver.sshserver(ui, repo)
         s.serve_forever()
 
+    if opts["cmdserver"]:
+        checkrepo()
+        s = commandserver.server(ui, repo, opts["cmdserver"])
+        return s.serve()
+
     # this way we can check if something was given in the command-line
     if opts.get('port'):
         opts['port'] = util.getport(opts.get('port'))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/commandserver.py	Fri Jun 17 15:43:50 2011 -0500
@@ -0,0 +1,213 @@
+# commandserver.py - communicate with Mercurial's API over a pipe
+#
+#  Copyright Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from i18n import _
+import struct
+import sys
+import dispatch, encoding, util
+
+logfile = None
+
+def log(*args):
+    if not logfile:
+        return
+
+    for a in args:
+        logfile.write(str(a))
+
+    logfile.flush()
+
+class channeledoutput(object):
+    """
+    Write data from in_ to out in the following format:
+
+    data length (unsigned int),
+    data
+    """
+    def __init__(self, in_, out, channel):
+        self.in_ = in_
+        self.out = out
+        self.channel = channel
+
+    def write(self, data):
+        if not data:
+            return
+        self.out.write(struct.pack('>cI', self.channel, len(data)))
+        self.out.write(data)
+        self.out.flush()
+
+    def __getattr__(self, attr):
+        if attr in ('isatty', 'fileno'):
+            raise AttributeError, attr
+        return getattr(self.in_, attr)
+
+class channeledinput(object):
+    """
+    Read data from in_.
+
+    Requests for input are written to out in the following format:
+    channel identifier - 'I' for plain input, 'L' line based (1 byte)
+    how many bytes to send at most (unsigned int),
+
+    The client replies with:
+    data length (unsigned int), 0 meaning EOF
+    data
+    """
+
+    maxchunksize = 4 * 1024
+
+    def __init__(self, in_, out, channel):
+        self.in_ = in_
+        self.out = out
+        self.channel = channel
+
+    def read(self, size=-1):
+        if size < 0:
+            # if we need to consume all the clients input, ask for 4k chunks
+            # so the pipe doesn't fill up risking a deadlock
+            size = self.maxchunksize
+            s = self._read(size, self.channel)
+            buf = s
+            while s:
+                buf += s
+                s = self._read(size, self.channel)
+
+            return buf
+        else:
+            return self._read(size, self.channel)
+
+    def _read(self, size, channel):
+        if not size:
+            return ''
+        assert size > 0
+
+        # tell the client we need at most size bytes
+        self.out.write(struct.pack('>cI', channel, size))
+        self.out.flush()
+
+        length = self.in_.read(4)
+        length = struct.unpack('>I', length)[0]
+        if not length:
+            return ''
+        else:
+            return self.in_.read(length)
+
+    def readline(self, size=-1):
+        if size < 0:
+            size = self.maxchunksize
+            s = self._read(size, 'L')
+            buf = s
+            # keep asking for more until there's either no more or
+            # we got a full line
+            while s and s[-1] != '\n':
+                buf += s
+                s = self._read(size, 'L')
+
+            return buf
+        else:
+            return self._read(size, 'L')
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        l = self.readline()
+        if not l:
+            raise StopIteration
+        return l
+
+    def __getattr__(self, attr):
+        if attr in ('isatty', 'fileno'):
+            raise AttributeError, attr
+        return getattr(self.in_, attr)
+
+class server(object):
+    """
+    Listens for commands on stdin, runs them and writes the output on a channel
+    based stream to stdout.
+    """
+    def __init__(self, ui, repo, mode):
+        self.ui = ui
+
+        logpath = ui.config("cmdserver", "log", None)
+        if logpath:
+            global logfile
+            if logpath == '-':
+                # write log on a special 'd'ebug channel
+                logfile = channeledoutput(sys.stdout, sys.stdout, 'd')
+            else:
+                logfile = open(logpath, 'a')
+
+        self.repo = repo
+
+        if mode == 'pipe':
+            self.cerr = channeledoutput(sys.stderr, sys.stdout, 'e')
+            self.cout = channeledoutput(sys.stdout, sys.stdout, 'o')
+            self.cin = channeledinput(sys.stdin, sys.stdout, 'I')
+            self.cresult = channeledoutput(sys.stdout, sys.stdout, 'r')
+
+            self.client = sys.stdin
+        else:
+            raise util.Abort(_('unknown mode %s') % mode)
+
+    def _read(self, size):
+        data = self.client.read(size)
+
+        # is the other end closed?
+        if not data:
+            raise EOFError()
+
+        return data
+
+    def runcommand(self):
+        """ reads a list of \0 terminated arguments, executes
+        and writes the return code to the result channel """
+
+        length = struct.unpack('>I', self._read(4))[0]
+        args = self._read(length).split('\0')
+
+        # copy the ui so changes to it don't persist between requests
+        req = dispatch.request(args, self.ui.copy(), self.repo, self.cin,
+                               self.cout, self.cerr)
+
+        ret = dispatch.dispatch(req) or 0 # might return None
+
+        self.cresult.write(struct.pack('>i', int(ret)))
+
+    def getencoding(self):
+        """ writes the current encoding to the result channel """
+        self.cresult.write(encoding.encoding)
+
+    def serveone(self):
+        cmd = self.client.readline()[:-1]
+        if cmd:
+            handler = self.capabilities.get(cmd)
+            if handler:
+                handler(self)
+            else:
+                # clients are expected to check what commands are supported by
+                # looking at the servers capabilities
+                raise util.Abort(_('unknown command %s') % cmd)
+
+        return cmd != ''
+
+    capabilities = {'runcommand'  : runcommand,
+                    'getencoding' : getencoding}
+
+    def serve(self):
+        self.cout.write('capabilities: %s' % ' '.join(self.capabilities.keys()))
+        self.cout.write('encoding: %s' % encoding.encoding)
+
+        try:
+            while self.serveone():
+                pass
+        except EOFError:
+            # we'll get here if the client disconnected while we were reading
+            # its request
+            return 1
+
+        return 0
--- a/mercurial/config.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/config.py	Fri Jun 17 15:43:50 2011 -0500
@@ -75,6 +75,7 @@
         itemre = re.compile(r'([^=\s][^=]*?)\s*=\s*(.*\S|)')
         contre = re.compile(r'\s+(\S|\S.*\S)\s*$')
         emptyre = re.compile(r'(;|#|\s*$)')
+        commentre = re.compile(r'(;|#)')
         unsetre = re.compile(r'%unset\s+(\S+)')
         includere = re.compile(r'%include\s+(\S|\S.*\S)\s*$')
         section = ""
@@ -85,6 +86,8 @@
         for l in data.splitlines(True):
             line += 1
             if cont:
+                if commentre.match(l):
+                    continue
                 m = contre.match(l)
                 if m:
                     if sections and section not in sections:
--- a/mercurial/context.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/context.py	Fri Jun 17 15:43:50 2011 -0500
@@ -116,6 +116,8 @@
         return self._repo.nodetags(self._node)
     def bookmarks(self):
         return self._repo.nodebookmarks(self._node)
+    def hidden(self):
+        return self._rev in self._repo.changelog.hiddenrevs
 
     def parents(self):
         """return contexts for each parent changeset"""
--- a/mercurial/dagutil.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/dagutil.py	Fri Jun 17 15:43:50 2011 -0500
@@ -7,6 +7,7 @@
 # GNU General Public License version 2 or any later version.
 
 from node import nullrev
+from i18n import _
 
 
 class basedag(object):
--- a/mercurial/dispatch.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/dispatch.py	Fri Jun 17 15:43:50 2011 -0500
@@ -276,7 +276,7 @@
                 replace['0'] = self.name
                 replace['@'] = ' '.join(args)
                 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
-                return util.system(cmd, environ=env)
+                return util.system(cmd, environ=env, out=ui.fout)
             self.fn = fn
             return
 
--- a/mercurial/help/config.txt	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/help/config.txt	Fri Jun 17 15:43:50 2011 -0500
@@ -36,7 +36,7 @@
     this file override options in all other configuration files. On
     Unix, most of this file will be ignored if it doesn't belong to a
     trusted user or to a trusted group. See the documentation for the
-    Trusted section below for more details.
+    ``[trusted]`` section below for more details.
 
 | (Unix) ``$HOME/.hgrc``
 | (Windows) ``%USERPROFILE%\.hgrc``
@@ -99,7 +99,7 @@
 removed from values. Empty lines are skipped. Lines beginning with
 ``#`` or ``;`` are ignored and may be used to provide comments.
 
-Configuration keys can be set multiple times, in which case mercurial
+Configuration keys can be set multiple times, in which case Mercurial
 will use the value that was configured last. As an example::
 
     [spam]
@@ -110,7 +110,8 @@
 This would set the configuration key named ``eggs`` to ``small``.
 
 It is also possible to define a section multiple times. A section can
-be redefined on the same and/or on different hgrc files. For example::
+be redefined on the same and/or on different configuration files. For
+example::
 
     [foo]
     eggs=large
@@ -170,8 +171,8 @@
 --------
 
 This section describes the different sections that may appear in a
-Mercurial "hgrc" file, the purpose of each section, its possible keys,
-and their possible values.
+Mercurial configuration file, the purpose of each section, its possible
+keys, and their possible values.
 
 ``alias``
 """""""""
@@ -186,7 +187,7 @@
 
 Alias definitions consist of lines of the form::
 
-    <alias> = <command> [<argument]...
+    <alias> = <command> [<argument>]...
 
 For example, this definition::
 
@@ -227,8 +228,8 @@
 
 Authentication credentials for HTTP authentication. This section
 allows you to store usernames and passwords for use when logging
-*into* HTTP servers. See the web_ configuration section if you want to
-configure *who* can login to your HTTP server.
+*into* HTTP servers. See the ``[web]`` configuration section if
+you want to configure *who* can login to your HTTP server.
 
 Each line has the following format::
 
@@ -403,7 +404,7 @@
 
 ``method``
     Optional. Method to use to send email messages. If value is ``smtp``
-    (default), use SMTP (see the SMTP_ section for configuration).
+    (default), use SMTP (see the ``[smtp]`` section for configuration).
     Otherwise, use as name of program to run that acts like sendmail
     (takes ``-f`` option for sender, list of recipients on command line,
     message on stdin). Normally, setting this to ``sendmail`` or
@@ -1016,7 +1017,7 @@
     option supports hook syntax, so if you want to specify multiple
     ignore files, you can do so by setting something like
     ``ignore.other = ~/.hgignore2``. For details of the ignore file
-    format, see the |hgignore(5)|_ man page.
+    format, see the ``hgignore(5)`` man page.
 
 ``interactive``
     Allow to prompt the user. True or False. Default is True.
@@ -1027,7 +1028,7 @@
 ``merge``
     The conflict resolution program to use during a manual merge.
     For more information on merge tools see :hg:`help merge-tools`.
-    For configuring merge tools see the merge-tools_ section.
+    For configuring merge tools see the ``[merge-tools]`` section.
 
 ``portablefilenames``
     Check for portable filenames. Can be ``warn``, ``ignore`` or ``abort``.
--- a/mercurial/help/glossary.txt	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/help/glossary.txt	Fri Jun 17 15:43:50 2011 -0500
@@ -5,6 +5,18 @@
     changeset is an ancestor, and a parent of an ancestor is an
     ancestor. See also: 'Descendant'.
 
+Bookmark
+    Bookmarks are pointers to certain commits that move when
+    committing. They are similar to tags in that it is possible to use
+    bookmark names in all places where Mercurial expects a changeset
+    ID, e.g., with :hg:`update`. Unlike tags, bookmarks move along
+    when you make a commit.
+
+    Bookmarks can be renamed, copied and deleted. Bookmarks are local,
+    unless they are explicitly pushed or pulled between repositories.
+    Pushing and pulling bookmarks allow you to collaborate with others
+    on a branch without creating a named branch.
+
 Branch
     (Noun) A child changeset that has been created from a parent that
     is not a head. These are known as topological branches, see
@@ -339,6 +351,12 @@
     A changeset that has only the null changeset as its parent. Most
     repositories have only a single root changeset.
 
+Tag
+    An alternative name given to a changeset. Tags can be used in all
+    places where Mercurial expects a changeset ID, e.g., with
+    :hg:`update`. The creation of a tag is stored in the history and
+    will thus automatically be shared with other using push and pull.
+
 Tip
     The changeset with the highest revision number. It is the changeset
     most recently added in a repository.
--- a/mercurial/httprepo.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/httprepo.py	Fri Jun 17 15:43:50 2011 -0500
@@ -191,7 +191,10 @@
         try:
             try:
                 r = self._call(cmd, data=fp, headers=headers, **args)
-                return r.split('\n', 1)
+                vals = r.split('\n', 1)
+                if len(vals) < 2:
+                    raise error.ResponseError(_("unexpected response:"), r)
+                return vals
             except socket.error, err:
                 if err.args[0] in (errno.ECONNRESET, errno.EPIPE):
                     raise util.Abort(_('push failed: %s') % err.args[1])
--- a/mercurial/localrepo.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/localrepo.py	Fri Jun 17 15:43:50 2011 -0500
@@ -272,7 +272,9 @@
 
         try:
             fp = self.wfile('.hgtags', 'rb+')
-        except IOError:
+        except IOError, e:
+            if e.errno != errno.ENOENT:
+                raise
             fp = self.wfile('.hgtags', 'ab')
         else:
             prevtags = fp.read()
--- a/mercurial/manifest.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/manifest.py	Fri Jun 17 15:43:50 2011 -0500
@@ -142,7 +142,7 @@
             # if this is changed to support newlines in filenames,
             # be sure to check the templates/ dir again (especially *-raw.tmpl)
             hex, flags = revlog.hex, map.flags
-            text = ''.join("%s\000%s%s\n" % (f, hex(map[f]), flags(f))
+            text = ''.join("%s\0%s%s\n" % (f, hex(map[f]), flags(f))
                            for f in files)
             arraytext = array.array('c', text)
             cachedelta = None
@@ -172,7 +172,7 @@
                 # bs will either be the index of the item or the insert point
                 start, end = self._search(addbuf, f, start)
                 if not todelete:
-                    l = "%s\000%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
+                    l = "%s\0%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
                 else:
                     if start == end:
                         # item we want to delete was not found, error out
--- a/mercurial/merge.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/merge.py	Fri Jun 17 15:43:50 2011 -0500
@@ -525,7 +525,7 @@
         elif not overwrite:
             if pa == p1 or pa == p2: # linear
                 pass # all good
-            elif wc.files() or wc.deleted():
+            elif wc.dirty(missing=True):
                 raise util.Abort(_("crosses branches (merge branches or use"
                                    " --clean to discard changes)"))
             elif onode is None:
--- a/mercurial/patch.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/patch.py	Fri Jun 17 15:43:50 2011 -0500
@@ -487,23 +487,34 @@
         return sorted(self.changed)
 
 class filestore(object):
-    def __init__(self):
+    def __init__(self, maxsize=None):
         self.opener = None
         self.files = {}
         self.created = 0
+        self.maxsize = maxsize
+        if self.maxsize is None:
+            self.maxsize = 4*(2**20)
+        self.size = 0
+        self.data = {}
 
     def setfile(self, fname, data, mode, copied=None):
-        if self.opener is None:
-            root = tempfile.mkdtemp(prefix='hg-patch-')
-            self.opener = scmutil.opener(root)
-        # Avoid filename issues with these simple names
-        fn = str(self.created)
-        self.opener.write(fn, data)
-        self.created += 1
-        self.files[fname] = (fn, mode, copied)
+        if self.maxsize < 0 or (len(data) + self.size) <= self.maxsize:
+            self.data[fname] = (data, mode, copied)
+            self.size += len(data)
+        else:
+            if self.opener is None:
+                root = tempfile.mkdtemp(prefix='hg-patch-')
+                self.opener = scmutil.opener(root)
+            # Avoid filename issues with these simple names
+            fn = str(self.created)
+            self.opener.write(fn, data)
+            self.created += 1
+            self.files[fname] = (fn, mode, copied)
 
     def getfile(self, fname):
-        if fname not in self.files:
+        if fname in self.data:
+            return self.data[fname]
+        if not self.opener or fname not in self.files:
             raise IOError()
         fn, mode, copied = self.files[fname]
         return self.opener.read(fn), mode, copied
--- a/mercurial/revset.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/revset.py	Fri Jun 17 15:43:50 2011 -0500
@@ -361,6 +361,19 @@
     dm = util.matchdate(ds)
     return [r for r in subset if dm(repo[r].date()[0])]
 
+def desc(repo, subset, x):
+    """``desc(string)``
+    Search commit message for string. The match is case-insensitive.
+    """
+    # i18n: "desc" is a keyword
+    ds = getstring(x, _("desc requires a string")).lower()
+    l = []
+    for r in subset:
+        c = repo[r]
+        if ds in c.description().lower():
+            l.append(r)
+    return l
+
 def descendants(repo, subset, x):
     """``descendants(set)``
     Changesets which are descendants of changesets in set.
@@ -821,6 +834,7 @@
     "closed": closed,
     "contains": contains,
     "date": date,
+    "desc": desc,
     "descendants": descendants,
     "file": hasfile,
     "filelog": filelog,
@@ -828,22 +842,22 @@
     "grep": grep,
     "head": head,
     "heads": heads,
+    "id": node,
     "keyword": keyword,
     "last": last,
     "limit": limit,
     "max": maxrev,
+    "merge": merge,
     "min": minrev,
-    "merge": merge,
     "modifies": modifies,
-    "id": node,
     "outgoing": outgoing,
     "p1": p1,
     "p2": p2,
     "parents": parents,
     "present": present,
     "removes": removes,
+    "rev": rev,
     "reverse": reverse,
-    "rev": rev,
     "roots": roots,
     "sort": sort,
     "tag": tag,
@@ -920,7 +934,8 @@
     elif op == 'func':
         f = getstring(x[1], _("not a symbol"))
         wa, ta = optimize(x[2], small)
-        if f in "grep date user author keyword branch file outgoing closed":
+        if f in ("author branch closed date desc file grep keyword "
+                 "outgoing user"):
             w = 10 # slow
         elif f in "modifies adds removes":
             w = 30 # slower
--- a/mercurial/subrepo.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/subrepo.py	Fri Jun 17 15:43:50 2011 -0500
@@ -526,7 +526,7 @@
         self._ctx = ctx
         self._ui = ctx._repo.ui
 
-    def _svncommand(self, commands, filename=''):
+    def _svncommand(self, commands, filename='', failok=False):
         cmd = ['svn']
         extrakw = {}
         if not self._ui.interactive():
@@ -551,15 +551,16 @@
                               universal_newlines=True, env=env, **extrakw)
         stdout, stderr = p.communicate()
         stderr = stderr.strip()
-        if p.returncode:
-            raise util.Abort(stderr or 'exited with code %d' % p.returncode)
-        if stderr:
-            self._ui.warn(stderr + '\n')
-        return stdout
+        if not failok:
+            if p.returncode:
+                raise util.Abort(stderr or 'exited with code %d' % p.returncode)
+            if stderr:
+                self._ui.warn(stderr + '\n')
+        return stdout, stderr
 
     @propertycache
     def _svnversion(self):
-        output = self._svncommand(['--version'], filename=None)
+        output, err = self._svncommand(['--version'], filename=None)
         m = re.search(r'^svn,\s+version\s+(\d+)\.(\d+)', output)
         if not m:
             raise util.Abort(_('cannot retrieve svn tool version'))
@@ -569,7 +570,7 @@
         # Get the working directory revision as well as the last
         # commit revision so we can compare the subrepo state with
         # both. We used to store the working directory one.
-        output = self._svncommand(['info', '--xml'])
+        output, err = self._svncommand(['info', '--xml'])
         doc = xml.dom.minidom.parseString(output)
         entries = doc.getElementsByTagName('entry')
         lastrev, rev = '0', '0'
@@ -588,7 +589,7 @@
         if the working directory was changed, and extchanges is
         True if any of these changes concern an external entry.
         """
-        output = self._svncommand(['status', '--xml'])
+        output, err = self._svncommand(['status', '--xml'])
         externals, changes = [], []
         doc = xml.dom.minidom.parseString(output)
         for e in doc.getElementsByTagName('entry'):
@@ -623,13 +624,13 @@
         if extchanged:
             # Do not try to commit externals
             raise util.Abort(_('cannot commit svn externals'))
-        commitinfo = self._svncommand(['commit', '-m', text])
+        commitinfo, err = self._svncommand(['commit', '-m', text])
         self._ui.status(commitinfo)
         newrev = re.search('Committed revision ([0-9]+).', commitinfo)
         if not newrev:
             raise util.Abort(commitinfo.splitlines()[-1])
         newrev = newrev.groups()[0]
-        self._ui.status(self._svncommand(['update', '-r', newrev]))
+        self._ui.status(self._svncommand(['update', '-r', newrev])[0])
         return newrev
 
     def remove(self):
@@ -663,9 +664,15 @@
         if self._svnversion >= (1, 5):
             args.append('--force')
         args.extend([state[0], '--revision', state[1]])
-        status = self._svncommand(args)
+        status, err = self._svncommand(args, failok=True)
         if not re.search('Checked out revision [0-9]+.', status):
-            raise util.Abort(status.splitlines()[-1])
+            if ('is already a working copy for a different URL' in err
+                and (self._wcchanged() == (False, False))):
+                # obstructed but clean working copy, so just blow it away.
+                self.remove()
+                self.get(state, overwrite=False)
+                return
+            raise util.Abort((status or err).splitlines()[-1])
         self._ui.status(status)
 
     def merge(self, state):
--- a/mercurial/tags.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/tags.py	Fri Jun 17 15:43:50 2011 -0500
@@ -286,4 +286,7 @@
     for (name, (node, hist)) in cachetags.iteritems():
         cachefile.write("%s %s\n" % (hex(node), name))
 
-    cachefile.rename()
+    try:
+        cachefile.rename()
+    except (OSError, IOError):
+        pass
--- a/mercurial/util.py	Wed Jun 15 10:40:23 2011 +0200
+++ b/mercurial/util.py	Fri Jun 17 15:43:50 2011 -0500
@@ -354,7 +354,7 @@
     env = dict(os.environ)
     env.update((k, py2shell(v)) for k, v in environ.iteritems())
     env['HG'] = hgexecutable()
-    if out is None:
+    if out is None or out == sys.__stdout__:
         rc = subprocess.call(cmd, shell=True, close_fds=closefds,
                              env=env, cwd=cwd)
     else:
--- a/tests/test-debugcomplete.t	Wed Jun 15 10:40:23 2011 +0200
+++ b/tests/test-debugcomplete.t	Fri Jun 17 15:43:50 2011 -0500
@@ -137,6 +137,7 @@
   --accesslog
   --address
   --certificate
+  --cmdserver
   --config
   --cwd
   --daemon
@@ -194,12 +195,12 @@
   export: output, switch-parent, rev, text, git, nodates
   forget: include, exclude
   init: ssh, remotecmd, insecure
-  log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, style, template, include, exclude
+  log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, hidden, patch, git, limit, no-merges, stat, style, template, include, exclude
   merge: force, tool, rev, preview
   pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
   push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
   remove: after, force, include, exclude
-  serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
+  serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate
   status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
   summary: remote
   update: clean, check, date, rev
--- a/tests/test-http-branchmap.t	Wed Jun 15 10:40:23 2011 +0200
+++ b/tests/test-http-branchmap.t	Fri Jun 17 15:43:50 2011 -0500
@@ -79,7 +79,7 @@
   > 
   > myui = ui.ui()
   > repo = hg.repository(myui, 'a')
-  > commands.serve(myui, repo, stdio=True)
+  > commands.serve(myui, repo, stdio=True, cmdserver=False)
   > EOF
   $ echo baz >> b/foo
   $ hg -R b ci -m baz
--- a/tests/test-i18n.t	Wed Jun 15 10:40:23 2011 +0200
+++ b/tests/test-i18n.t	Fri Jun 17 15:43:50 2011 -0500
@@ -8,17 +8,17 @@
 using the "replace" error handler:
 
   $ LANGUAGE=pt_BR hg tip
-  abortado: no repository found in '$TESTTMP' (.hg not found)!
+  abortado: n?o foi encontrado um reposit?rio em '$TESTTMP' (.hg n?o encontrado)!
   [255]
 
 Using a more accomodating encoding:
 
   $ HGENCODING=UTF-8 LANGUAGE=pt_BR hg tip
-  abortado: no repository found in '$TESTTMP' (.hg not found)!
+  abortado: n\xc3\xa3o foi encontrado um reposit\xc3\xb3rio em '$TESTTMP' (.hg n\xc3\xa3o encontrado)! (esc)
   [255]
 
 Different encoding:
 
   $ HGENCODING=Latin-1 LANGUAGE=pt_BR hg tip
-  abortado: no repository found in '$TESTTMP' (.hg not found)!
+  abortado: n\xe3o foi encontrado um reposit\xf3rio em '$TESTTMP' (.hg n\xe3o encontrado)! (esc)
   [255]
--- a/tests/test-log.t	Wed Jun 15 10:40:23 2011 +0200
+++ b/tests/test-log.t	Fri Jun 17 15:43:50 2011 -0500
@@ -1138,3 +1138,21 @@
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     a
   
+  $ cat > $HGTMP/testhidden.py << EOF
+  > def reposetup(ui, repo):
+  >     for line in repo.opener('hidden'):
+  >         ctx = repo[line.strip()]
+  >         repo.changelog.hiddenrevs.add(ctx.rev())
+  > EOF
+  $ echo '[extensions]' >> $HGRCPATH
+  $ echo "hidden=$HGTMP/testhidden.py" >> $HGRCPATH
+  $ touch .hg/hidden
+  $ hg log --template='{rev}:{node}\n'
+  1:a765632148dc55d38c35c4f247c618701886cb2f
+  0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
+  $ echo a765632148dc55d38c35c4f247c618701886cb2f > .hg/hidden
+  $ hg log --template='{rev}:{node}\n'
+  0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
+  $ hg log --template='{rev}:{node}\n' --hidden
+  1:a765632148dc55d38c35c4f247c618701886cb2f
+  0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
--- a/tests/test-revset.t	Wed Jun 15 10:40:23 2011 +0200
+++ b/tests/test-revset.t	Fri Jun 17 15:43:50 2011 -0500
@@ -190,6 +190,8 @@
   1
   3
   5
+  $ log 'desc(B)'
+  5
   $ log 'descendants(2 or 3)'
   2
   3
--- a/tests/test-subrepo-svn.t	Wed Jun 15 10:40:23 2011 +0200
+++ b/tests/test-subrepo-svn.t	Fri Jun 17 15:43:50 2011 -0500
@@ -489,3 +489,33 @@
   $ if "$TESTDIR/hghave" -q svn15; then
   > hg up 2 >/dev/null 2>&1 || echo update failed
   > fi
+
+Modify one of the externals to point to a different path so we can
+test having obstructions when switching branches on checkout:
+  $ hg checkout tip
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo "obstruct =        [svn]       $SVNREPO/externals" >> .hgsub
+  $ svn co -r5 --quiet "$SVNREPO"/externals obstruct
+  $ hg commit -m 'Start making obstructed wc'
+  committing subrepository obstruct
+  $ hg book other
+  $ hg co -r 'p1(tip)'
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ echo "obstruct =        [svn]       $SVNREPO/src" >> .hgsub
+  $ svn co -r5 --quiet "$SVNREPO"/src obstruct
+  $ hg commit -m 'Other branch which will be obstructed'
+  committing subrepository obstruct
+  created new head
+
+Switching back to the head where we have another path mapped to the
+same subrepo should work if the subrepo is clean.
+  $ hg co other
+  A    $TESTTMP/rebaserepo/obstruct/other
+  Checked out revision 1.
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+This is surprising, but is also correct based on the current code:
+  $ echo "updating should (maybe) fail" > obstruct/other
+  $ hg co tip
+  abort: crosses branches (merge branches or use --clean to discard changes)
+  [255]