changeset 18891:bd6aed2ad5ee

merge with i18n Note: i18n work should normally be done on stable
author Matt Mackall <mpm@selenic.com>
date Mon, 08 Apr 2013 17:57:42 -0500
parents 8c64c4af21a4 (diff) 537e869b17e9 (current diff)
children 46c0ca1ef7e1
files
diffstat 27 files changed, 305 insertions(+), 117 deletions(-) [+]
line wrap: on
line diff
--- a/.hgsigs	Fri Apr 05 17:04:37 2013 +0200
+++ b/.hgsigs	Mon Apr 08 17:57:42 2013 -0500
@@ -67,3 +67,5 @@
 a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 0 iD8DBQBRDDROywK+sNU5EO8RAh75AJ9uJCGoCWnP0Lv/+XuYs4hvUl+sAgCcD36QgAnuw8IQXrvv684BAXAnHcA=
 7511d4df752e61fe7ae4f3682e0a0008573b0402 0 iD8DBQBRFYaoywK+sNU5EO8RAuErAJoDyhXn+lptU3+AevVdwAIeNFyR2gCdHzPHyWd+JDeWCUR+pSOBi8O2ppM=
 5b7175377babacce80a6c1e12366d8032a6d4340 0 iD8DBQBRMCYgywK+sNU5EO8RAq1/AKCWKlt9ysibyQgYwoxxIOZv5J8rpwCcDSHQaaf1fFZUTnQsOePwcM2Y/Sg=
+50c922c1b5145dab8baefefb0437d363b6a6c21c 0 iD8DBQBRWnUnywK+sNU5EO8RAuQRAJwM42cJqJPeqJ0jVNdMqKMDqr4dSACeP0cRVGz1gitMuV0x8f3mrZrqc7I=
+8a7bd2dccd44ed571afe7424cd7f95594f27c092 0 iD8DBQBRXfBvywK+sNU5EO8RAn+LAKCsMmflbuXjYRxlzFwId5ptm8TZcwCdGkyLbZcASBOkzQUm/WW1qfknJHU=
--- a/.hgtags	Fri Apr 05 17:04:37 2013 +0200
+++ b/.hgtags	Mon Apr 08 17:57:42 2013 -0500
@@ -80,3 +80,5 @@
 a6088c05e43a8aee0472ca3a4f6f8d7dd914ebbf 2.5
 7511d4df752e61fe7ae4f3682e0a0008573b0402 2.5.1
 5b7175377babacce80a6c1e12366d8032a6d4340 2.5.2
+50c922c1b5145dab8baefefb0437d363b6a6c21c 2.5.3
+8a7bd2dccd44ed571afe7424cd7f95594f27c092 2.5.4
--- a/contrib/perf.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/contrib/perf.py	Mon Apr 08 17:57:42 2013 -0500
@@ -2,7 +2,7 @@
 '''helper extension to measure performance'''
 
 from mercurial import cmdutil, scmutil, util, match, commands, obsolete
-from mercurial import repoview, branchmap, merge
+from mercurial import repoview, branchmap, merge, copies
 import time, os, sys
 
 cmdtable = {}
@@ -54,6 +54,15 @@
     #                                                False))))
     timer(lambda: sum(map(len, repo.status(**opts))))
 
+@command('perfaddremove')
+def perfaddremove(ui, repo):
+    try:
+        oldquiet = repo.ui.quiet
+        repo.ui.quiet = True
+        timer(lambda: scmutil.addremove(repo, dry_run=True))
+    finally:
+        repo.ui.quiet = oldquiet
+
 def clearcaches(cl):
     # behave somewhat consistently across internal API changes
     if util.safehasattr(cl, 'clearcaches'):
@@ -99,6 +108,15 @@
             rev in s
     timer(d)
 
+@command('perfdirs')
+def perfdirs(ui, repo):
+    dirstate = repo.dirstate
+    'a' in dirstate
+    def d():
+        dirstate.dirs()
+        del dirstate._dirs
+    timer(d)
+
 @command('perfdirstate')
 def perfdirstate(ui, repo):
     "a" in repo.dirstate
@@ -140,6 +158,14 @@
                                acceptremote=True)
     timer(d)
 
+@command('perfpathcopies', [], "REV REV")
+def perfpathcopies(ui, repo, rev1, rev2):
+    ctx1 = scmutil.revsingle(repo, rev1, rev1)
+    ctx2 = scmutil.revsingle(repo, rev2, rev2)
+    def d():
+        copies.pathcopies(ctx1, ctx2)
+    timer(d)
+
 @command('perfmanifest')
 def perfmanifest(ui, repo):
     def d():
--- a/hgext/patchbomb.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/hgext/patchbomb.py	Mon Apr 08 17:57:42 2013 -0500
@@ -540,7 +540,13 @@
                 fp.close()
         else:
             if not sendmail:
-                sendmail = mail.connect(ui, mbox=mbox)
+                verifycert = ui.config('smtp', 'verifycert')
+                if opts.get('insecure'):
+                    ui.setconfig('smtp', 'verifycert', 'loose')
+                try:
+                    sendmail = mail.connect(ui, mbox=mbox)
+                finally:
+                    ui.setconfig('smtp', 'verifycert', verifycert)
             ui.status(_('sending '), subj, ' ...\n')
             ui.progress(_('sending'), i, item=subj, total=len(msgs))
             if not mbox:
--- a/mercurial/bookmarks.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/bookmarks.py	Mon Apr 08 17:57:42 2013 -0500
@@ -221,14 +221,13 @@
     finally:
         w.release()
 
-def updatefromremote(ui, repo, remote, path):
+def updatefromremote(ui, repo, remotemarks, path):
     ui.debug("checking for updated bookmarks\n")
-    rb = remote.listkeys('bookmarks')
     changed = False
     localmarks = repo._bookmarks
-    for k in sorted(rb):
+    for k in sorted(remotemarks):
         if k in localmarks:
-            nr, nl = rb[k], localmarks[k]
+            nr, nl = remotemarks[k], localmarks[k]
             if nr in repo:
                 cr = repo[nr]
                 cl = repo[nl]
@@ -257,9 +256,9 @@
                     localmarks[n] = cr.node()
                     changed = True
                     ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n))
-        elif rb[k] in repo:
+        elif remotemarks[k] in repo:
             # add remote bookmarks for changes we already have
-            localmarks[k] = repo[rb[k]].node()
+            localmarks[k] = repo[remotemarks[k]].node()
             changed = True
             ui.status(_("adding remote bookmark %s\n") % k)
 
--- a/mercurial/cmdutil.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/cmdutil.py	Mon Apr 08 17:57:42 2013 -0500
@@ -1590,10 +1590,13 @@
     forgot.extend(forget)
     return bad, forgot
 
-def duplicatecopies(repo, rev, p1):
-    "Reproduce copies found in the source revision in the dirstate for grafts"
-    for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems():
-        repo.dirstate.copy(src, dst)
+def duplicatecopies(repo, rev, fromrev):
+    '''reproduce copies from fromrev to rev in the dirstate'''
+    for dst, src in copies.pathcopies(repo[fromrev], repo[rev]).iteritems():
+        # copies.pathcopies returns backward renames, so dst might not
+        # actually be in the dirstate
+        if repo.dirstate[dst] in "nma":
+            repo.dirstate.copy(src, dst)
 
 def commit(ui, repo, commitfunc, pats, opts):
     '''commit the specified files or all outstanding changes'''
--- a/mercurial/commands.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/commands.py	Mon Apr 08 17:57:42 2013 -0500
@@ -2095,7 +2095,7 @@
 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
     """create arbitrary obsolete marker
 
-    With no arguments it it display the list obsolescence marker."""
+    With no arguments, displays the list of obsolescence markers."""
     def parsenodeid(s):
         try:
             # We do not use revsingle/revrange functions here to accept
@@ -2917,9 +2917,13 @@
         return -1
 
     # check for ancestors of dest branch
-    for rev in repo.revs('::. and %ld', revs):
-        ui.warn(_('skipping ancestor revision %s\n') % rev)
-        revs.remove(rev)
+    crev = repo['.'].rev()
+    ancestors = repo.changelog.ancestors([crev], inclusive=True)
+    # don't mutate while iterating, create a copy
+    for rev in list(revs):
+        if rev in ancestors:
+            ui.warn(_('skipping ancestor revision %s\n') % rev)
+            revs.remove(rev)
     if not revs:
         return -1
 
@@ -2933,7 +2937,9 @@
 
     # check ancestors for earlier grafts
     ui.debug('scanning for duplicate grafts\n')
-    for ctx in repo.set("::. - ::%ld", revs):
+
+    for rev in repo.changelog.findmissingrevs(revs, [crev]):
+        ctx = repo[rev]
         n = ctx.extra().get('source')
         if n in ids:
             r = repo[n].rev()
@@ -2947,7 +2953,7 @@
         elif ctx.hex() in ids:
             r = ids[ctx.hex()]
             ui.warn(_('skipping already grafted revision %s '
-                            '(was grafted from %d)\n') % (r, ctx.rev()))
+                            '(was grafted from %d)\n') % (r, rev))
             revs.remove(r)
     if not revs:
         return -1
@@ -4496,14 +4502,15 @@
     ui.status(_('pulling from %s\n') % util.hidepassword(source))
     revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
 
+    remotebookmarks = other.listkeys('bookmarks')
+
     if opts.get('bookmark'):
         if not revs:
             revs = []
-        rb = other.listkeys('bookmarks')
         for b in opts['bookmark']:
-            if b not in rb:
+            if b not in remotebookmarks:
                 raise util.Abort(_('remote bookmark %s not found!') % b)
-            revs.append(rb[b])
+            revs.append(remotebookmarks[b])
 
     if revs:
         try:
@@ -4514,7 +4521,7 @@
             raise util.Abort(err)
 
     modheads = repo.pull(other, heads=revs, force=opts.get('force'))
-    bookmarks.updatefromremote(ui, repo, other, source)
+    bookmarks.updatefromremote(ui, repo, remotebookmarks, source)
     if checkout:
         checkout = str(repo.changelog.rev(other.lookup(checkout)))
     repo._subtoppath = source
@@ -4530,7 +4537,7 @@
         for b in opts['bookmark']:
             # explicit pull overrides local bookmark if any
             ui.status(_("importing bookmark %s\n") % b)
-            marks[b] = repo[rb[b]].node()
+            marks[b] = repo[remotebookmarks[b]].node()
         marks.write()
 
     return ret
--- a/mercurial/context.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/context.py	Mon Apr 08 17:57:42 2013 -0500
@@ -291,16 +291,16 @@
             try:
                 return self._manifest[path], self._manifest.flags(path)
             except KeyError:
-                raise error.LookupError(self._node, path,
-                                        _('not found in manifest'))
+                raise error.ManifestLookupError(self._node, path,
+                                                _('not found in manifest'))
         if '_manifestdelta' in self.__dict__ or path in self.files():
             if path in self._manifestdelta:
                 return (self._manifestdelta[path],
                         self._manifestdelta.flags(path))
         node, flag = self._repo.manifest.find(self._changeset[0], path)
         if not node:
-            raise error.LookupError(self._node, path,
-                                    _('not found in manifest'))
+            raise error.ManifestLookupError(self._node, path,
+                                            _('not found in manifest'))
 
         return node, flag
 
--- a/mercurial/copies.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/copies.py	Mon Apr 08 17:57:42 2013 -0500
@@ -133,11 +133,13 @@
     # we currently don't try to find where old files went, too expensive
     # this means we can miss a case like 'hg rm b; hg cp a b'
     cm = {}
-    for f in b:
-        if f not in a:
-            ofctx = _tracefile(b[f], a)
-            if ofctx:
-                cm[f] = ofctx.path()
+    missing = set(b.manifest().iterkeys())
+    missing.difference_update(a.manifest().iterkeys())
+
+    for f in missing:
+        ofctx = _tracefile(b[f], a)
+        if ofctx:
+            cm[f] = ofctx.path()
 
     # combine copies from dirstate if necessary
     if w is not None:
--- a/mercurial/dicthelpers.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/dicthelpers.py	Mon Apr 08 17:57:42 2013 -0500
@@ -5,20 +5,46 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-def _diffjoin(d1, d2, default, compare):
+def diff(d1, d2, default=None):
+    '''Return all key-value pairs that are different between d1 and d2.
+
+    This includes keys that are present in one dict but not the other, and
+    keys whose values are different. The return value is a dict with values
+    being pairs of values from d1 and d2 respectively, and missing values
+    represented as default.'''
     res = {}
-    if d1 is d2 and compare:
+    if d1 is d2:
         # same dict, so diff is empty
         return res
 
     for k1, v1 in d1.iteritems():
         if k1 in d2:
             v2 = d2[k1]
-            if not compare or v1 != v2:
+            if v1 != v2:
                 res[k1] = (v1, v2)
         else:
             res[k1] = (v1, default)
 
+    for k2 in d2:
+        if k2 not in d1:
+            res[k2] = (default, d2[k2])
+
+    return res
+
+def join(d1, d2, default=None):
+    '''Return all key-value pairs from both d1 and d2.
+
+    This is akin to an outer join in relational algebra. The return value is a
+    dict with values being pairs of values from d1 and d2 respectively, and
+    missing values represented as default.'''
+    res = {}
+
+    for k1, v1 in d1.iteritems():
+        if k1 in d2:
+            res[k1] = (v1, d2[k1])
+        else:
+            res[k1] = (v1, default)
+
     if d1 is d2:
         return res
 
@@ -27,9 +53,3 @@
             res[k2] = (default, d2[k2])
 
     return res
-
-def diff(d1, d2, default=None):
-    return _diffjoin(d1, d2, default, True)
-
-def join(d1, d2, default=None):
-    return _diffjoin(d1, d2, default, False)
--- a/mercurial/dirstate.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/dirstate.py	Mon Apr 08 17:57:42 2013 -0500
@@ -154,11 +154,14 @@
     def flagfunc(self, buildfallback):
         if self._checklink and self._checkexec:
             def f(x):
-                p = self._join(x)
-                if os.path.islink(p):
-                    return 'l'
-                if util.isexec(p):
-                    return 'x'
+                try:
+                    st = os.lstat(self._join(x))
+                    if util.statislink(st):
+                        return 'l'
+                    if util.statisexec(st):
+                        return 'x'
+                except OSError:
+                    pass
                 return ''
             return f
 
--- a/mercurial/error.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/error.py	Mon Apr 08 17:57:42 2013 -0500
@@ -27,6 +27,9 @@
     def __str__(self):
         return RevlogError.__str__(self)
 
+class ManifestLookupError(LookupError):
+    pass
+
 class CommandError(Exception):
     """Exception raised on errors in parsing the command line."""
 
--- a/mercurial/help/config.txt	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/help/config.txt	Mon Apr 08 17:57:42 2013 -0500
@@ -1052,6 +1052,16 @@
     Optional. Method to enable TLS when connecting to mail server: starttls,
     smtps or none. Default: none.
 
+``verifycert``
+    Optional. Verification for the certificate of mail server, when
+    ``tls`` is starttls or smtps. "strict", "loose" or False. For
+    "strict" or "loose", the certificate is verified as same as the
+    verification for HTTPS connections (see ``[hostfingerprints]`` and
+    ``[web] cacerts`` also). For "strict", sending email is also
+    aborted, if there is no configuration for mail server in
+    ``[hostfingerprints]`` and ``[web] cacerts``.  --insecure for
+    :hg:`email` overwrites this as "loose". Default: "strict".
+
 ``username``
     Optional. User name for authenticating with the SMTP server.
     Default: none.
--- a/mercurial/hgweb/hgweb_mod.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/hgweb/hgweb_mod.py	Mon Apr 08 17:57:42 2013 -0500
@@ -250,7 +250,8 @@
         except (error.LookupError, error.RepoLookupError), err:
             req.respond(HTTP_NOT_FOUND, ctype)
             msg = str(err)
-            if util.safehasattr(err, 'name') and 'manifest' not in msg:
+            if (util.safehasattr(err, 'name') and
+                not isinstance(err,  error.ManifestLookupError)):
                 msg = 'revision not found: %s' % err.name
             return tmpl('error', error=msg)
         except (error.RepoError, error.RevlogError), inst:
--- a/mercurial/mail.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/mail.py	Mon Apr 08 17:57:42 2013 -0500
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 from i18n import _
-import util, encoding
+import util, encoding, sslutil
 import os, smtplib, socket, quopri, time
 import email.Header, email.MIMEText, email.Utils
 
@@ -30,6 +30,59 @@
 
 email.Header.Header.__dict__['__init__'] = _unifiedheaderinit
 
+class STARTTLS(smtplib.SMTP):
+    '''Derived class to verify the peer certificate for STARTTLS.
+
+    This class allows to pass any keyword arguments to SSL socket creation.
+    '''
+    def __init__(self, sslkwargs, **kwargs):
+        smtplib.SMTP.__init__(self, **kwargs)
+        self._sslkwargs = sslkwargs
+
+    def starttls(self, keyfile=None, certfile=None):
+        if not self.has_extn("starttls"):
+            msg = "STARTTLS extension not supported by server"
+            raise smtplib.SMTPException(msg)
+        (resp, reply) = self.docmd("STARTTLS")
+        if resp == 220:
+            self.sock = sslutil.ssl_wrap_socket(self.sock, keyfile, certfile,
+                                                **self._sslkwargs)
+            if not util.safehasattr(self.sock, "read"):
+                # using httplib.FakeSocket with Python 2.5.x or earlier
+                self.sock.read = self.sock.recv
+            self.file = smtplib.SSLFakeFile(self.sock)
+            self.helo_resp = None
+            self.ehlo_resp = None
+            self.esmtp_features = {}
+            self.does_esmtp = 0
+        return (resp, reply)
+
+if util.safehasattr(smtplib.SMTP, '_get_socket'):
+    class SMTPS(smtplib.SMTP):
+        '''Derived class to verify the peer certificate for SMTPS.
+
+        This class allows to pass any keyword arguments to SSL socket creation.
+        '''
+        def __init__(self, sslkwargs, keyfile=None, certfile=None, **kwargs):
+            self.keyfile = keyfile
+            self.certfile = certfile
+            smtplib.SMTP.__init__(self, **kwargs)
+            self.default_port = smtplib.SMTP_SSL_PORT
+            self._sslkwargs = sslkwargs
+
+        def _get_socket(self, host, port, timeout):
+            if self.debuglevel > 0:
+                print >> stderr, 'connect:', (host, port)
+            new_socket = socket.create_connection((host, port), timeout)
+            new_socket = sslutil.ssl_wrap_socket(new_socket,
+                                                 self.keyfile, self.certfile,
+                                                 **self._sslkwargs)
+            self.file = smtplib.SSLFakeFile(new_socket)
+            return new_socket
+else:
+    def SMTPS(sslkwargs, keyfile=None, certfile=None, **kwargs):
+        raise util.Abort(_('SMTPS requires Python 2.6 or later'))
+
 def _smtp(ui):
     '''build an smtp connection and return a function to send mail'''
     local_hostname = ui.config('smtp', 'local_hostname')
@@ -39,14 +92,25 @@
     smtps = tls == 'smtps'
     if (starttls or smtps) and not util.safehasattr(socket, 'ssl'):
         raise util.Abort(_("can't use TLS: Python SSL support not installed"))
-    if smtps:
-        ui.note(_('(using smtps)\n'))
-        s = smtplib.SMTP_SSL(local_hostname=local_hostname)
-    else:
-        s = smtplib.SMTP(local_hostname=local_hostname)
     mailhost = ui.config('smtp', 'host')
     if not mailhost:
         raise util.Abort(_('smtp.host not configured - cannot send mail'))
+    verifycert = ui.config('smtp', 'verifycert', 'strict')
+    if verifycert not in ['strict', 'loose']:
+        if util.parsebool(verifycert) is not False:
+            raise util.Abort(_('invalid smtp.verifycert configuration: %s')
+                             % (verifycert))
+    if (starttls or smtps) and verifycert:
+        sslkwargs = sslutil.sslkwargs(ui, mailhost)
+    else:
+        sslkwargs = {}
+    if smtps:
+        ui.note(_('(using smtps)\n'))
+        s = SMTPS(sslkwargs, local_hostname=local_hostname)
+    elif starttls:
+        s = STARTTLS(sslkwargs, local_hostname=local_hostname)
+    else:
+        s = smtplib.SMTP(local_hostname=local_hostname)
     mailport = util.getport(ui.config('smtp', 'port', 25))
     ui.note(_('sending mail: smtp host %s, port %s\n') %
             (mailhost, mailport))
@@ -56,6 +120,9 @@
         s.ehlo()
         s.starttls()
         s.ehlo()
+    if (starttls or smtps) and verifycert:
+        ui.note(_('(verifying remote certificate)\n'))
+        sslutil.validator(ui, mailhost)(s.sock, verifycert == 'strict')
     username = ui.config('smtp', 'username')
     password = ui.config('smtp', 'password')
     if username and not password:
--- a/mercurial/posix.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/posix.py	Mon Apr 08 17:57:42 2013 -0500
@@ -557,3 +557,11 @@
         if self.realpath != self.path:
             okayifmissing(os.unlink, self.realpath)
             okayifmissing(os.rmdir, os.path.dirname(self.realpath))
+
+def statislink(st):
+    '''check whether a stat result is a symlink'''
+    return st and stat.S_ISLNK(st.st_mode)
+
+def statisexec(st):
+    '''check whether a stat result is an executable file'''
+    return st and (st.st_mode & 0100 != 0)
--- a/mercurial/scmutil.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/scmutil.py	Mon Apr 08 17:57:42 2013 -0500
@@ -676,26 +676,32 @@
     m.bad = lambda x, y: rejected.append(x)
 
     ctx = repo[None]
-    walkresults = repo.dirstate.walk(m, sorted(ctx.substate), True, False)
-    for abs in sorted(walkresults):
-        st = walkresults[abs]
-        dstate = repo.dirstate[abs]
+    dirstate = repo.dirstate
+    walkresults = dirstate.walk(m, sorted(ctx.substate), True, False)
+    for abs, st in walkresults.iteritems():
+        dstate = dirstate[abs]
         if dstate == '?' and audit_path.check(abs):
             unknown.append(abs)
-            if repo.ui.verbose or not m.exact(abs):
-                rel = m.rel(abs)
-                repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
-        elif (dstate != 'r' and (not st or
-               (stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode)))):
+        elif dstate != 'r' and not st:
             deleted.append(abs)
-            if repo.ui.verbose or not m.exact(abs):
-                rel = m.rel(abs)
-                repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
         # for finding renames
         elif dstate == 'r':
             removed.append(abs)
         elif dstate == 'a':
             added.append(abs)
+
+    unknownset = set(unknown)
+    toprint = unknownset.copy()
+    toprint.update(deleted)
+    for abs in sorted(toprint):
+        if repo.ui.verbose or not m.exact(abs):
+            rel = m.rel(abs)
+            if abs in unknownset:
+                status = _('adding %s\n') % ((pats and rel) or abs)
+            else:
+                status = _('removing %s\n') % ((pats and rel) or abs)
+            repo.ui.status(status)
+
     copies = {}
     if similarity > 0:
         for old, new, score in similar.findrenames(repo,
@@ -722,49 +728,6 @@
             return 1
     return 0
 
-def updatedir(ui, repo, patches, similarity=0):
-    '''Update dirstate after patch application according to metadata'''
-    if not patches:
-        return []
-    copies = []
-    removes = set()
-    cfiles = patches.keys()
-    cwd = repo.getcwd()
-    if cwd:
-        cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
-    for f in patches:
-        gp = patches[f]
-        if not gp:
-            continue
-        if gp.op == 'RENAME':
-            copies.append((gp.oldpath, gp.path))
-            removes.add(gp.oldpath)
-        elif gp.op == 'COPY':
-            copies.append((gp.oldpath, gp.path))
-        elif gp.op == 'DELETE':
-            removes.add(gp.path)
-
-    wctx = repo[None]
-    for src, dst in copies:
-        dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
-    if (not similarity) and removes:
-        wctx.remove(sorted(removes), True)
-
-    for f in patches:
-        gp = patches[f]
-        if gp and gp.mode:
-            islink, isexec = gp.mode
-            dst = repo.wjoin(gp.path)
-            # patch won't create empty files
-            if gp.op == 'ADD' and not os.path.lexists(dst):
-                flags = (isexec and 'x' or '') + (islink and 'l' or '')
-                repo.wwrite(gp.path, '', flags)
-            util.setflags(dst, islink, isexec)
-    addremove(repo, cfiles, similarity=similarity)
-    files = patches.keys()
-    files.extend([r for r in removes if r not in files])
-    return sorted(files)
-
 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
     """Update the dirstate to reflect the intent of copying src to dst. For
     different reasons it might not end with dst being marked as copied from src.
--- a/mercurial/sslutil.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/sslutil.py	Mon Apr 08 17:57:42 2013 -0500
@@ -99,7 +99,7 @@
         self.ui = ui
         self.host = host
 
-    def __call__(self, sock):
+    def __call__(self, sock, strict=False):
         host = self.host
         cacerts = self.ui.config('web', 'cacerts')
         hostfingerprint = self.ui.config('hostfingerprints', host)
@@ -107,13 +107,22 @@
             if hostfingerprint:
                 raise util.Abort(_("host fingerprint for %s can't be "
                                    "verified (Python too old)") % host)
+            if strict:
+                raise util.Abort(_("certificate for %s can't be verified "
+                                   "(Python too old)") % host)
             if self.ui.configbool('ui', 'reportoldssl', True):
                 self.ui.warn(_("warning: certificate for %s can't be verified "
                                "(Python too old)\n") % host)
             return
+
         if not sock.cipher(): # work around http://bugs.python.org/issue13721
             raise util.Abort(_('%s ssl connection error') % host)
-        peercert = sock.getpeercert(True)
+        try:
+            peercert = sock.getpeercert(True)
+            peercert2 = sock.getpeercert()
+        except AttributeError:
+            raise util.Abort(_('%s ssl connection error') % host)
+
         if not peercert:
             raise util.Abort(_('%s certificate error: '
                                'no certificate received') % host)
@@ -129,13 +138,18 @@
             self.ui.debug('%s certificate matched fingerprint %s\n' %
                           (host, nicefingerprint))
         elif cacerts:
-            msg = _verifycert(sock.getpeercert(), host)
+            msg = _verifycert(peercert2, host)
             if msg:
                 raise util.Abort(_('%s certificate error: %s') % (host, msg),
                                  hint=_('configure hostfingerprint %s or use '
                                         '--insecure to connect insecurely') %
                                       nicefingerprint)
             self.ui.debug('%s certificate successfully verified\n' % host)
+        elif strict:
+            raise util.Abort(_('%s certificate with fingerprint %s not '
+                               'verified') % (host, nicefingerprint),
+                             hint=_('check hostfingerprints or web.cacerts '
+                                     'config setting'))
         else:
             self.ui.warn(_('warning: %s certificate with fingerprint %s not '
                            'verified (check hostfingerprints or web.cacerts '
--- a/mercurial/subrepo.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/subrepo.py	Mon Apr 08 17:57:42 2013 -0500
@@ -547,9 +547,10 @@
             else:
                 self._repo.ui.status(_('pulling subrepo %s from %s\n')
                                      % (subrelpath(self), srcurl))
+                remotebookmarks = other.listkeys('bookmarks')
                 self._repo.pull(other)
-                bookmarks.updatefromremote(self._repo.ui, self._repo, other,
-                                           srcurl)
+                bookmarks.updatefromremote(self._repo.ui, self._repo,
+                                           remotebookmarks, srcurl)
 
     @annotatesubrepoerror
     def get(self, state, overwrite=False):
--- a/mercurial/templater.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/templater.py	Mon Apr 08 17:57:42 2013 -0500
@@ -66,7 +66,10 @@
                     break
                 pos += 1
             sym = program[s:pos]
-            yield ('symbol', sym, s)
+            try:
+                yield ('string', str(int(sym)), s)
+            except ValueError:
+                yield ('symbol', sym, s)
             pos -= 1
         elif c == '}':
             pos += 1
--- a/mercurial/util.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/util.py	Mon Apr 08 17:57:42 2013 -0500
@@ -65,6 +65,8 @@
 split = platform.split
 sshargs = platform.sshargs
 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
+statisexec = platform.statisexec
+statislink = platform.statislink
 termwidth = platform.termwidth
 testpid = platform.testpid
 umask = platform.umask
--- a/mercurial/windows.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/mercurial/windows.py	Mon Apr 08 17:57:42 2013 -0500
@@ -337,3 +337,11 @@
             pass
 
 expandglobs = True
+
+def statislink(st):
+    '''check whether a stat result is a symlink'''
+    return False
+
+def statisexec(st):
+    '''check whether a stat result is an executable file'''
+    return False
--- a/setup.py	Fri Apr 05 17:04:37 2013 +0200
+++ b/setup.py	Mon Apr 08 17:57:42 2013 -0500
@@ -148,7 +148,7 @@
     # fine, we don't want to load it anyway.  Python may warn about
     # a missing __init__.py in mercurial/locale, we also ignore that.
     err = [e for e in err.splitlines()
-           if not e.startswith(b('Not trusting file')) \
+           if not e.startswith(b('not trusting file')) \
               and not e.startswith(b('warning: Not importing')) \
               and not e.startswith(b('obsolete feature not enabled'))]
     if err:
--- a/tests/test-bookmarks-pushpull.t	Fri Apr 05 17:04:37 2013 +0200
+++ b/tests/test-bookmarks-pushpull.t	Mon Apr 08 17:57:42 2013 -0500
@@ -204,6 +204,39 @@
      Y                         3:f6fc62dde3c0
      Z                         1:0d2164f0ce0d
 
+update a bookmark in the middle of a client pulling changes
+
+  $ cd ..
+  $ hg clone -q a pull-race
+  $ hg clone -q pull-race pull-race2
+  $ cd pull-race
+  $ hg up -q Y
+  $ echo c4 > f2
+  $ hg ci -Am4
+  $ echo c5 > f3
+  $ cat <<EOF > .hg/hgrc
+  > [hooks]
+  > outgoing.makecommit = hg ci -Am5; echo committed in pull-race
+  > EOF
+  $ cd ../pull-race2
+  $ hg pull
+  pulling from $TESTTMP/pull-race (glob)
+  searching for changes
+  adding changesets
+  adding f3
+  committed in pull-race
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files
+  updating bookmark Y
+  (run 'hg update' to get a working copy)
+  $ hg book
+   * @                         1:0d2164f0ce0d
+     X                         1:0d2164f0ce0d
+     Y                         4:b0a5eff05604
+     Z                         1:0d2164f0ce0d
+  $ cd ../b
+
 diverging a remote bookmark fails
 
   $ hg up -q 4e3505fd9583
--- a/tests/test-command-template.t	Fri Apr 05 17:04:37 2013 +0200
+++ b/tests/test-command-template.t	Mon Apr 08 17:57:42 2013 -0500
@@ -1360,6 +1360,12 @@
   $ hg log -l1 --template '{date|age}\n'
   7 years from now
 
+Filter with int function argument:
+
+  $ hg log --template '{fill(author, 20)}\n' -r 0
+  User Name
+  <user@hostname>
+
 Error on syntax:
 
   $ echo 'x = "f' >> t
--- a/tests/test-hook.t	Fri Apr 05 17:04:37 2013 +0200
+++ b/tests/test-hook.t	Mon Apr 08 17:57:42 2013 -0500
@@ -198,7 +198,6 @@
   listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
   no changes found
   listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
-  listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
   adding remote bookmark bar
   importing bookmark bar
   $ cd ../a
@@ -232,6 +231,7 @@
   abort: prelistkeys hook exited with status 1
   [255]
   $ cd ../a
+  $ rm .hg/hgrc
 
 prechangegroup hook can prevent incoming changes
 
--- a/tests/test-pull-http.t	Fri Apr 05 17:04:37 2013 +0200
+++ b/tests/test-pull-http.t	Mon Apr 08 17:57:42 2013 -0500
@@ -58,7 +58,6 @@
 
   $ req
   pulling from http://localhost:$HGPORT/
-  searching for changes
   abort: authorization failed
   % serve errors