changeset 18618:43ffd0279876

merge with stable
author Kevin Bullock <kbullock@ringworld.org>
date Sat, 09 Feb 2013 22:25:58 +0000
parents e7b89b5127c2 (diff) 227479f61db9 (current diff)
children c768e3da3cc2
files mercurial/scmutil.py tests/test-obsolete.t
diffstat 72 files changed, 727 insertions(+), 460 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sat Feb 09 17:54:01 2013 +0000
+++ b/Makefile	Sat Feb 09 22:25:58 2013 +0000
@@ -94,6 +94,9 @@
 test-%:
 	cd tests && $(PYTHON) run-tests.py $(TESTFLAGS) $@
 
+check-code:
+	hg manifest | xargs python contrib/check-code.py
+
 update-pot: i18n/hg.pot
 
 i18n/hg.pot: $(PYFILES) $(DOCFILES)
--- a/contrib/check-code.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/contrib/check-code.py	Sat Feb 09 22:25:58 2013 +0000
@@ -80,6 +80,7 @@
     (r'^diff.*-\w*N', "don't use 'diff -N'"),
     (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"),
     (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"),
+    (r'kill (`|\$\()', "don't use kill, use killdaemons.py")
   ]
 ]
 
--- a/hgext/convert/git.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/hgext/convert/git.py	Sat Feb 09 22:25:58 2013 +0000
@@ -6,6 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 import os
+import subprocess
 from mercurial import util, config
 from mercurial.node import hex, nullid
 from mercurial.i18n import _
@@ -29,13 +30,15 @@
     # cannot remove environment variable. Just assume none have
     # both issues.
     if util.safehasattr(os, 'unsetenv'):
-        def gitopen(self, s, noerr=False):
+        def gitopen(self, s, err=None):
             prevgitdir = os.environ.get('GIT_DIR')
             os.environ['GIT_DIR'] = self.path
             try:
-                if noerr:
+                if err == subprocess.PIPE:
                     (stdin, stdout, stderr) = util.popen3(s)
                     return stdout
+                elif err == subprocess.STDOUT:
+                    return self.popen_with_stderr(s)
                 else:
                     return util.popen(s, 'rb')
             finally:
@@ -44,13 +47,25 @@
                 else:
                     os.environ['GIT_DIR'] = prevgitdir
     else:
-        def gitopen(self, s, noerr=False):
-            if noerr:
+        def gitopen(self, s, err=None):
+            if err == subprocess.PIPE:
                 (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
                 return so
+            elif err == subprocess.STDOUT:
+                    return self.popen_with_stderr(s)
             else:
                 return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
 
+    def popen_with_stderr(self, s):
+        p = subprocess.Popen(s, shell=True, bufsize=-1,
+                             close_fds=util.closefds,
+                             stdin=subprocess.PIPE,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT,
+                             universal_newlines=False,
+                             env=None)
+        return p.stdout
+
     def gitread(self, s):
         fh = self.gitopen(s)
         data = fh.read()
@@ -209,12 +224,15 @@
     def gettags(self):
         tags = {}
         alltags = {}
-        fh = self.gitopen('git ls-remote --tags "%s"' % self.path)
+        fh = self.gitopen('git ls-remote --tags "%s"' % self.path,
+                          err=subprocess.STDOUT)
         prefix = 'refs/tags/'
 
         # Build complete list of tags, both annotated and bare ones
         for line in fh:
             line = line.strip()
+            if line.startswith("error:") or line.startswith("fatal:"):
+                raise util.Abort(_('cannot read tags from %s') % self.path)
             node, tag = line.split(None, 1)
             if not tag.startswith(prefix):
                 continue
@@ -266,7 +284,7 @@
         # Origin heads
         for reftype in gitcmd:
             try:
-                fh = self.gitopen(gitcmd[reftype], noerr=True)
+                fh = self.gitopen(gitcmd[reftype], err=subprocess.PIPE)
                 for line in fh:
                     line = line.strip()
                     rev, name = line.split(None, 1)
--- a/hgext/histedit.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/hgext/histedit.py	Sat Feb 09 22:25:58 2013 +0000
@@ -498,8 +498,8 @@
         keep = opts.get('keep', False)
         revs = between(repo, parent, topmost, keep)
         if not revs:
-            ui.warn(_('nothing to edit\n'))
-            return 1
+            raise util.Abort(_('%s is not an ancestor of working directory') %
+                             node.short(parent))
 
         ctxs = [repo[r] for r in revs]
         rules = opts.get('commands', '')
@@ -583,7 +583,7 @@
     if not newchildren:
         # `parentctxnode` should match but no result. This means that
         # currentnode is not a descendant from parentctxnode.
-        msg = _('working directory parent is not a descendant of %s')
+        msg = _('%s is not an ancestor of working directory')
         hint = _('update to %s or descendant and run "hg histedit '
                  '--continue" again') % parentctx
         raise util.Abort(msg % parentctx, hint=hint)
--- a/hgext/largefiles/__init__.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/hgext/largefiles/__init__.py	Sat Feb 09 22:25:58 2013 +0000
@@ -41,11 +41,20 @@
 enabled for this to work.
 
 When you pull a changeset that affects largefiles from a remote
-repository, Mercurial behaves as normal. However, when you update to
-such a revision, any largefiles needed by that revision are downloaded
-and cached (if they have never been downloaded before). This means
-that network access may be required to update to changesets you have
-not previously updated to.
+repository, the largefiles for the changeset usually won't be
+pulled down until you update to the revision (there is one exception
+to this case).  However, when you update to such a revision, any
+largefiles needed by that revision are downloaded and cached (if
+they have never been downloaded before).  This means that network
+access may be required to update to changesets you have no
+previously updated to.
+
+The one exception to the "largefiles won't be pulled until you update
+to a revision that changes them" rule is when you pull new heads.
+Because you could be pulling new heads (that you may later want to
+merge with) from a non-default location (that Mercurial won't know
+about later), when you pull new heads, largefiles revisions for those
+heads are downloaded and cached locally.
 
 If you already have large files tracked by Mercurial without the
 largefiles extension, you will need to convert your repository in
--- a/hgext/largefiles/basestore.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/hgext/largefiles/basestore.py	Sat Feb 09 22:25:58 2013 +0000
@@ -43,7 +43,8 @@
         raise NotImplementedError('abstract method')
 
     def exists(self, hashes):
-        '''Check to see if the store contains the given hashes.'''
+        '''Check to see if the store contains the given hashes. Given an
+        iterable of hashes it returns a mapping from hash to bool.'''
         raise NotImplementedError('abstract method')
 
     def get(self, files):
@@ -96,10 +97,10 @@
         '''Verify the existence (and, optionally, contents) of every big
         file revision referenced by every changeset in revs.
         Return 0 if all is well, non-zero on any errors.'''
-        write = self.ui.write
         failed = False
 
-        write(_('searching %d changesets for largefiles\n') % len(revs))
+        self.ui.status(_('searching %d changesets for largefiles\n') %
+                       len(revs))
         verified = set()                # set of (filename, filenode) tuples
 
         for rev in revs:
@@ -113,12 +114,13 @@
         numrevs = len(verified)
         numlfiles = len(set([fname for (fname, fnode) in verified]))
         if contents:
-            write(_('verified contents of %d revisions of %d largefiles\n')
-                  % (numrevs, numlfiles))
+            self.ui.status(
+                _('verified contents of %d revisions of %d largefiles\n')
+                % (numrevs, numlfiles))
         else:
-            write(_('verified existence of %d revisions of %d largefiles\n')
-                  % (numrevs, numlfiles))
-
+            self.ui.status(
+                _('verified existence of %d revisions of %d largefiles\n')
+                % (numrevs, numlfiles))
         return int(failed)
 
     def _getfile(self, tmpfile, filename, hash):
@@ -131,6 +133,11 @@
 
     def _verifyfile(self, cctx, cset, contents, standin, verified):
         '''Perform the actual verification of a file in the store.
+        'cset' is only used in warnings.
+        'contents' controls verification of content hash.
+        'standin' is the standin path of the largefile to verify.
+        'verified' is maintained as a set of already verified files.
+        Returns _true_ if it is a standin and any problems are found!
         '''
         raise NotImplementedError('abstract method')
 
--- a/hgext/largefiles/lfcommands.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/hgext/largefiles/lfcommands.py	Sat Feb 09 22:25:58 2013 +0000
@@ -368,9 +368,9 @@
     ui.progress(_('uploading largefiles'), None)
 
 def verifylfiles(ui, repo, all=False, contents=False):
-    '''Verify that every big file revision in the current changeset
+    '''Verify that every largefile revision in the current changeset
     exists in the central store.  With --contents, also verify that
-    the contents of each big file revision are correct (SHA-1 hash
+    the contents of each local largefile file revision are correct (SHA-1 hash
     matches the revision ID).  With --all, check every changeset in
     this repository.'''
     if all:
--- a/hgext/largefiles/localstore.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/hgext/largefiles/localstore.py	Sat Feb 09 22:25:58 2013 +0000
@@ -63,23 +63,19 @@
             return False
 
         expecthash = fctx.data()[0:40]
+        storepath = lfutil.storepath(self.remote, expecthash)
         verified.add(key)
         if not lfutil.instore(self.remote, expecthash):
             self.ui.warn(
-                _('changeset %s: %s missing\n'
-                  '  (looked for hash %s)\n')
-                % (cset, filename, expecthash))
+                _('changeset %s: %s references missing %s\n')
+                % (cset, filename, storepath))
             return True                 # failed
 
         if contents:
-            storepath = lfutil.storepath(self.remote, expecthash)
             actualhash = lfutil.hashfile(storepath)
             if actualhash != expecthash:
                 self.ui.warn(
-                    _('changeset %s: %s: contents differ\n'
-                      '  (%s:\n'
-                      '  expected hash %s,\n'
-                      '  but got %s)\n')
-                    % (cset, filename, storepath, expecthash, actualhash))
+                    _('changeset %s: %s references corrupted %s\n')
+                    % (cset, filename, storepath))
                 return True             # failed
         return False
--- a/hgext/largefiles/overrides.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/hgext/largefiles/overrides.py	Sat Feb 09 22:25:58 2013 +0000
@@ -274,7 +274,7 @@
     contents = opts.pop('lfc', False)
 
     result = orig(ui, repo, *pats, **opts)
-    if large:
+    if large or all or contents:
         result = result or lfcommands.verifylfiles(ui, repo, all, contents)
     return result
 
@@ -360,15 +360,17 @@
 # Finally, the merge.applyupdates function will then take care of
 # writing the files into the working copy and lfcommands.updatelfiles
 # will update the largefiles.
-def overridemanifestmerge(origfn, repo, p1, p2, pa, overwrite, partial):
-    actions = origfn(repo, p1, p2, pa, overwrite, partial)
+def overridemanifestmerge(origfn, repo, p1, p2, pa, branchmerge, force,
+                          partial):
+    overwrite = force and not branchmerge
+    actions = origfn(repo, p1, p2, pa, branchmerge, force, partial)
     processed = []
 
     for action in actions:
         if overwrite:
             processed.append(action)
             continue
-        f, m = action[:2]
+        f, m, args, msg = action
 
         choices = (_('&Largefile'), _('&Normal file'))
         if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
@@ -379,10 +381,10 @@
             msg = _('%s has been turned into a largefile\n'
                     'use (l)argefile or keep as (n)ormal file?') % lfile
             if repo.ui.promptchoice(msg, choices, 0) == 0:
-                processed.append((lfile, "r"))
-                processed.append((standin, "g", p2.flags(standin)))
+                processed.append((lfile, "r", None, msg))
+                processed.append((standin, "g", (p2.flags(standin),), msg))
             else:
-                processed.append((standin, "r"))
+                processed.append((standin, "r", None, msg))
         elif m == "g" and lfutil.standin(f) in p1 and f in p2:
             # Case 2: largefile in the working copy, normal file in
             # the second parent
@@ -391,10 +393,10 @@
             msg = _('%s has been turned into a normal file\n'
                     'keep as (l)argefile or use (n)ormal file?') % lfile
             if repo.ui.promptchoice(msg, choices, 0) == 0:
-                processed.append((lfile, "r"))
+                processed.append((lfile, "r", None, msg))
             else:
-                processed.append((standin, "r"))
-                processed.append((lfile, "g", p2.flags(lfile)))
+                processed.append((standin, "r", None, msg))
+                processed.append((lfile, "g", (p2.flags(lfile),), msg))
         else:
             processed.append(action)
 
@@ -735,10 +737,11 @@
         # will run into a problem later if we try to merge or rebase with one of
         # these heads, so cache the largefiles now directly into the system
         # cache.
-        ui.status(_("caching new largefiles\n"))
         numcached = 0
         heads = lfutil.getcurrentheads(repo)
         newheads = set(heads).difference(set(oldheads))
+        if len(newheads) > 0:
+            ui.status(_("caching largefiles for %s heads\n") % len(newheads))
         for head in newheads:
             (cached, missing) = lfcommands.cachelfiles(ui, repo, head)
             numcached += len(cached)
--- a/hgext/largefiles/remotestore.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/hgext/largefiles/remotestore.py	Sat Feb 09 22:25:58 2013 +0000
@@ -29,7 +29,7 @@
             _('remotestore: put %s to remote store %s') % (source, self.url))
 
     def exists(self, hashes):
-        return self._verify(hashes)
+        return dict((h, s == 0) for (h, s) in self._stat(hashes).iteritems())
 
     def sendfile(self, filename, hash):
         self.ui.debug('remotestore: sendfile(%s, %s)\n' % (filename, hash))
@@ -76,9 +76,6 @@
             infile = lfutil.limitreader(infile, length)
         return lfutil.copyandhash(lfutil.blockstream(infile), tmpfile)
 
-    def _verify(self, hashes):
-        return dict((h, s == 0) for (h, s) in self._stat(hashes).iteritems())
-
     def _verifyfile(self, cctx, cset, contents, standin, verified):
         filename = lfutil.splitstandin(standin)
         if not filename:
--- a/hgext/largefiles/uisetup.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/hgext/largefiles/uisetup.py	Sat Feb 09 22:25:58 2013 +0000
@@ -52,11 +52,12 @@
     entry = extensions.wrapcommand(commands.table, 'verify',
                                    overrides.overrideverify)
 
-    verifyopt = [('', 'large', None, _('verify largefiles')),
+    verifyopt = [('', 'large', None,
+                  _('verify that all largefiles in current revision exists')),
                  ('', 'lfa', None,
-                     _('verify all revisions of largefiles not just current')),
+                  _('verify largefiles in all revisions, not just current')),
                  ('', 'lfc', None,
-                     _('verify largefile contents not just existence'))]
+                  _('verify local largefile contents, not just existence'))]
     entry[1].extend(verifyopt)
 
     entry = extensions.wrapcommand(commands.table, 'debugstate',
--- a/mercurial/bdiff.c	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/bdiff.c	Sat Feb 09 22:25:58 2013 +0000
@@ -347,6 +347,11 @@
 	if (!PyArg_ParseTuple(args, "s#s#:bdiff", &sa, &la, &sb, &lb))
 		return NULL;
 
+	if (la > UINT_MAX || lb > UINT_MAX) {
+		PyErr_SetString(PyExc_ValueError, "bdiff inputs too large");
+		return NULL;
+	}
+
 	_save = PyEval_SaveThread();
 	an = splitlines(sa, la, &al);
 	bn = splitlines(sb, lb, &bl);
@@ -381,18 +386,9 @@
 	for (h = l.next; h; h = h->next) {
 		if (h->a1 != la || h->b1 != lb) {
 			len = bl[h->b1].l - bl[lb].l;
-
-#define checkputbe32(__x, __c) \
-	if (__x > UINT_MAX) { \
-		PyErr_SetString(PyExc_ValueError, \
-		                "bdiff: value too large for putbe32"); \
-		goto nomem; \
-	} \
-	putbe32((uint32_t)(__x), __c);
-
-			checkputbe32(al[la].l - al->l, rb);
-			checkputbe32(al[h->a1].l - al->l, rb + 4);
-			checkputbe32(len, rb + 8);
+			putbe32((uint32_t)(al[la].l - al->l), rb);
+			putbe32((uint32_t)(al[h->a1].l - al->l), rb + 4);
+			putbe32((uint32_t)len, rb + 8);
 			memcpy(rb + 12, bl[lb].l, len);
 			rb += 12 + len;
 		}
--- a/mercurial/byterange.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/byterange.py	Sat Feb 09 22:25:58 2013 +0000
@@ -238,7 +238,6 @@
                    unquote, addclosehook, addinfourl
 import ftplib
 import socket
-import sys
 import mimetypes
 import email
 
@@ -320,7 +319,7 @@
             headers = email.message_from_string(headers)
             return addinfourl(fp, headers, req.get_full_url())
         except ftplib.all_errors, msg:
-            raise IOError('ftp error', msg), sys.exc_info()[2]
+            raise IOError('ftp error', msg)
 
     def connect_ftp(self, user, passwd, host, port, dirs):
         fw = ftpwrapper(user, passwd, host, port, dirs)
@@ -350,7 +349,7 @@
             try:
                 self.ftp.nlst(file)
             except ftplib.error_perm, reason:
-                raise IOError('ftp error', reason), sys.exc_info()[2]
+                raise IOError('ftp error', reason)
             # Restore the transfer mode!
             self.ftp.voidcmd(cmd)
             # Try to retrieve as a file
@@ -364,7 +363,7 @@
                     fp = RangeableFileObject(fp, (rest,''))
                     return (fp, retrlen)
                 elif not str(reason).startswith('550'):
-                    raise IOError('ftp error', reason), sys.exc_info()[2]
+                    raise IOError('ftp error', reason)
         if not conn:
             # Set transfer mode to ASCII!
             self.ftp.voidcmd('TYPE A')
--- a/mercurial/changelog.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/changelog.py	Sat Feb 09 22:25:58 2013 +0000
@@ -183,7 +183,7 @@
         """filtered version of revlog.rev"""
         r = super(changelog, self).rev(node)
         if r in self.filteredrevs:
-            raise error.LookupError(node, self.indexfile, _('no node'))
+            raise error.LookupError(hex(node), self.indexfile, _('no node'))
         return r
 
     def node(self, rev):
--- a/mercurial/cmdutil.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/cmdutil.py	Sat Feb 09 22:25:58 2013 +0000
@@ -12,6 +12,7 @@
 import match as matchmod
 import subrepo, context, repair, graphmod, revset, phases, obsolete
 import changelog
+import bookmarks
 import lock as lockmod
 
 def parsealiases(cmd):
@@ -169,7 +170,8 @@
                          inst.args[0])
 
 def makefileobj(repo, pat, node=None, desc=None, total=None,
-                seqno=None, revwidth=None, mode='wb', pathname=None):
+                seqno=None, revwidth=None, mode='wb', modemap={},
+                pathname=None):
 
     writable = mode not in ('r', 'rb')
 
@@ -195,9 +197,11 @@
         return pat
     if util.safehasattr(pat, 'read') and 'r' in mode:
         return pat
-    return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
-                              pathname),
-                mode)
+    fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
+    mode = modemap.get(fn, mode)
+    if mode == 'wb':
+        modemap[fn] = 'ab'
+    return open(fn, mode)
 
 def openrevlog(repo, cmd, file_, opts):
     """opens the changelog, manifest, a filelog or a given revlog"""
@@ -538,6 +542,7 @@
 
     total = len(revs)
     revwidth = max([len(str(rev)) for rev in revs])
+    filemode = {}
 
     def single(rev, seqno, fp):
         ctx = repo[rev]
@@ -553,7 +558,8 @@
             desc_lines = ctx.description().rstrip().split('\n')
             desc = desc_lines[0]    #Commit always has a first line.
             fp = makefileobj(repo, template, node, desc=desc, total=total,
-                             seqno=seqno, revwidth=revwidth, mode='ab')
+                             seqno=seqno, revwidth=revwidth, mode='wb',
+                             modemap=filemode)
             if fp != template:
                 shouldclose = True
         if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
@@ -1793,6 +1799,8 @@
         edittext.append(_("HG: branch merge"))
     if ctx.branch():
         edittext.append(_("HG: branch '%s'") % ctx.branch())
+    if bookmarks.iscurrent(repo):
+        edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
     edittext.extend([_("HG: subrepo %s") % s for s in subs])
     edittext.extend([_("HG: added %s") % f for f in added])
     edittext.extend([_("HG: changed %s") % f for f in modified])
--- a/mercurial/dirstate.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/dirstate.py	Sat Feb 09 22:25:58 2013 +0000
@@ -9,10 +9,8 @@
 from node import nullid
 from i18n import _
 import scmutil, util, ignore, osutil, parsers, encoding
-import struct, os, stat, errno
-import cStringIO
+import os, stat, errno
 
-_format = ">cllll"
 propertycache = util.propertycache
 filecache = scmutil.filecache
 _rangemask = 0x7fffffff
@@ -508,38 +506,7 @@
         # use the modification time of the newly created temporary file as the
         # filesystem's notion of 'now'
         now = util.fstat(st).st_mtime
-        copymap = self._copymap
-        try:
-            finish(parsers.pack_dirstate(self._map, copymap, self._pl, now))
-            return
-        except AttributeError:
-            pass
-
-        now = int(now)
-        cs = cStringIO.StringIO()
-        pack = struct.pack
-        write = cs.write
-        write("".join(self._pl))
-        for f, e in self._map.iteritems():
-            if e[0] == 'n' and e[3] == now:
-                # The file was last modified "simultaneously" with the current
-                # write to dirstate (i.e. within the same second for file-
-                # systems with a granularity of 1 sec). This commonly happens
-                # for at least a couple of files on 'update'.
-                # The user could change the file without changing its size
-                # within the same second. Invalidate the file's stat data in
-                # dirstate, forcing future 'status' calls to compare the
-                # contents of the file. This prevents mistakenly treating such
-                # files as clean.
-                e = (e[0], 0, -1, -1)   # mark entry as 'unset'
-                self._map[f] = e
-
-            if f in copymap:
-                f = "%s\0%s" % (f, copymap[f])
-            e = pack(_format, e[0], e[1], e[2], e[3], len(f))
-            write(e)
-            write(f)
-        finish(cs.getvalue())
+        finish(parsers.pack_dirstate(self._map, self._copymap, self._pl, now))
 
     def _dirignore(self, f):
         if f == '.':
--- a/mercurial/dispatch.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/dispatch.py	Sat Feb 09 22:25:58 2013 +0000
@@ -748,6 +748,7 @@
 def lsprofile(ui, func, fp):
     format = ui.config('profiling', 'format', default='text')
     field = ui.config('profiling', 'sort', default='inlinetime')
+    limit = ui.configint('profiling', 'limit', default=30)
     climit = ui.configint('profiling', 'nested', default=5)
 
     if format not in ['text', 'kcachegrind']:
@@ -776,7 +777,7 @@
             # format == 'text'
             stats = lsprof.Stats(p.getstats())
             stats.sort(field)
-            stats.pprint(limit=30, file=fp, climit=climit)
+            stats.pprint(limit=limit, file=fp, climit=climit)
 
 def statprofile(ui, func, fp):
     try:
--- a/mercurial/help/config.txt	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/help/config.txt	Sat Feb 09 22:25:58 2013 +0000
@@ -996,6 +996,10 @@
     ``inlinetime``.
     Default: inlinetime.
 
+``limit``
+    Number of lines to show. Specific to the ``ls`` instrumenting profiler.
+    Default: 30.
+
 ``nested``
     Show at most this number of lines of drill-down info in a tree structure
     after each main entry. This can help explain the difference between Total
--- a/mercurial/help/dates.txt	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/help/dates.txt	Sat Feb 09 22:25:58 2013 +0000
@@ -18,6 +18,9 @@
 - ``12-6``
 - ``12/6``
 - ``12/6/6`` (Dec 6 2006)
+- ``today`` (midnight)
+- ``yesterday`` (midnight)
+- ``now`` - right now
 
 Lastly, there is Mercurial's internal format:
 
--- a/mercurial/help/templates.txt	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/help/templates.txt	Sat Feb 09 22:25:58 2013 +0000
@@ -44,19 +44,21 @@
 
 In addition to filters, there are some basic built-in functions:
 
+- date(date[, fmt])
+
+- fill(text[, width])
+
+- get(dict, key)
+
 - if(expr, then[, else])
 
 - ifeq(expr, expr, then[, else])
 
-- sub(pat, repl, expr)
-
 - join(list, sep)
 
 - label(label, expr)
 
-- date(date[, fmt])
-
-- fill(text[, width])
+- sub(pat, repl, expr)
 
 Also, for any expression that returns a list, there is a list operator:
 
--- a/mercurial/hgweb/webcommands.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/hgweb/webcommands.py	Sat Feb 09 22:25:58 2013 +0000
@@ -89,6 +89,7 @@
                 author=fctx.user(),
                 date=fctx.date(),
                 desc=fctx.description(),
+                extra=fctx.extra(),
                 branch=webutil.nodebranchnodefault(fctx),
                 parent=webutil.parents(fctx),
                 child=webutil.children(fctx),
@@ -162,6 +163,7 @@
                        child=webutil.children(ctx),
                        changelogtag=showtags,
                        desc=ctx.description(),
+                       extra=ctx.extra(),
                        date=ctx.date(),
                        files=files,
                        rev=ctx.rev(),
@@ -216,6 +218,7 @@
                       "child": webutil.children(ctx, i + 1),
                       "changelogtag": showtags,
                       "desc": ctx.description(),
+                      "extra": ctx.extra(),
                       "date": ctx.date(),
                       "files": files,
                       "rev": i,
@@ -299,6 +302,7 @@
                 changesetbranch=showbranch,
                 author=ctx.user(),
                 desc=ctx.description(),
+                extra=ctx.extra(),
                 date=ctx.date(),
                 files=files,
                 diffsummary=lambda **x: webutil.diffsummary(diffstatgen),
@@ -531,6 +535,7 @@
                 parity=parity.next(),
                 author=ctx.user(),
                 desc=ctx.description(),
+                extra=ctx.extra(),
                 date=ctx.date(),
                 rev=i,
                 node=hn,
@@ -590,6 +595,7 @@
                 rev=ctx.rev(),
                 date=ctx.date(),
                 desc=ctx.description(),
+                extra=ctx.extra(),
                 author=ctx.user(),
                 rename=rename,
                 branch=webutil.nodebranchnodefault(ctx),
@@ -651,6 +657,7 @@
                 rev=ctx.rev(),
                 date=ctx.date(),
                 desc=ctx.description(),
+                extra=ctx.extra(),
                 author=ctx.user(),
                 rename=rename,
                 branch=webutil.nodebranchnodefault(ctx),
@@ -689,6 +696,7 @@
                    "rev": f.rev(),
                    "author": f.user(),
                    "desc": f.description(),
+                   "extra": f.extra(),
                    "file": f.path(),
                    "targetline": targetline,
                    "line": l,
@@ -705,6 +713,7 @@
                 author=fctx.user(),
                 date=fctx.date(),
                 desc=fctx.description(),
+                extra=fctx.extra(),
                 rename=webutil.renamelink(fctx),
                 branch=webutil.nodebranchnodefault(fctx),
                 parent=webutil.parents(fctx),
@@ -770,6 +779,7 @@
                       "parent": webutil.parents(iterfctx),
                       "child": webutil.children(iterfctx),
                       "desc": iterfctx.description(),
+                      "extra": iterfctx.extra(),
                       "tags": webutil.nodetagsdict(repo, iterfctx.node()),
                       "bookmarks": webutil.nodebookmarksdict(
                           repo, iterfctx.node()),
--- a/mercurial/manifest.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/manifest.py	Sat Feb 09 22:25:58 2013 +0000
@@ -28,7 +28,8 @@
 
 class manifest(revlog.revlog):
     def __init__(self, opener):
-        self._mancache = None
+        # we expect to deal with not more than three revs at a time in merge
+        self._mancache = util.lrucachedict(3)
         revlog.revlog.__init__(self, opener, "00manifest.i")
 
     def parse(self, lines):
@@ -51,12 +52,12 @@
     def read(self, node):
         if node == revlog.nullid:
             return manifestdict() # don't upset local cache
-        if self._mancache and self._mancache[0] == node:
-            return self._mancache[1]
+        if node in self._mancache:
+            return self._mancache[node][0]
         text = self.revision(node)
         arraytext = array.array('c', text)
         mapping = self.parse(text)
-        self._mancache = (node, mapping, arraytext)
+        self._mancache[node] = (mapping, arraytext)
         return mapping
 
     def _search(self, m, s, lo=0, hi=None):
@@ -102,8 +103,9 @@
     def find(self, node, f):
         '''look up entry for a single file efficiently.
         return (node, flags) pair if found, (None, None) if not.'''
-        if self._mancache and self._mancache[0] == node:
-            return self._mancache[1].get(f), self._mancache[1].flags(f)
+        if node in self._mancache:
+            mapping = self._mancache[node][0]
+            return mapping.get(f), mapping.flags(f)
         text = self.revision(node)
         start, end = self._search(text, f)
         if start == end:
@@ -143,7 +145,7 @@
 
         # if we're using the cache, make sure it is valid and
         # parented by the same node we're diffing against
-        if not (changed and self._mancache and p1 and self._mancache[0] == p1):
+        if not (changed and p1 and (p1 in self._mancache)):
             files = sorted(map)
             checkforbidden(files)
 
@@ -156,7 +158,7 @@
             cachedelta = None
         else:
             added, removed = changed
-            addlist = self._mancache[2]
+            addlist = self._mancache[p1][1]
 
             checkforbidden(added)
             # combine the changed lists into one list for sorting
@@ -208,6 +210,6 @@
             text = util.buffer(arraytext)
 
         n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
-        self._mancache = (n, map, arraytext)
+        self._mancache[n] = (map, arraytext)
 
         return n
--- a/mercurial/merge.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/merge.py	Sat Feb 09 22:25:58 2013 +0000
@@ -176,62 +176,59 @@
     state = branchmerge and 'r' or 'f'
     for f in wctx.deleted():
         if f not in mctx:
-            actions.append((f, state))
+            actions.append((f, state, None, "forget deleted"))
 
     if not branchmerge:
         for f in wctx.removed():
             if f not in mctx:
-                actions.append((f, "f"))
+                actions.append((f, "f", None, "forget removed"))
 
     return actions
 
-def manifestmerge(repo, p1, p2, pa, overwrite, partial):
+def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial):
     """
     Merge p1 and p2 with ancestor pa and generate merge action list
 
-    overwrite = whether we clobber working files
+    branchmerge and force are as passed in to update
     partial = function to filter file lists
     """
 
-    def act(msg, m, f, *args):
-        repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
-        actions.append((f, m) + args)
-
+    overwrite = force and not branchmerge
     actions, copy, movewithdir = [], {}, {}
 
     if overwrite:
-        pa = p1
+        pa = wctx
     elif pa == p2: # backwards
-        pa = p1.p1()
+        pa = wctx.p1()
+    elif not branchmerge and not wctx.dirty(missing=True):
+        pass
     elif pa and repo.ui.configbool("merge", "followcopies", True):
-        ret = copies.mergecopies(repo, p1, p2, pa)
+        ret = copies.mergecopies(repo, wctx, p2, pa)
         copy, movewithdir, diverge, renamedelete = ret
         for of, fl in diverge.iteritems():
-            act("divergent renames", "dr", of, fl)
+            actions.append((of, "dr", (fl,), "divergent renames"))
         for of, fl in renamedelete.iteritems():
-            act("rename and delete", "rd", of, fl)
+            actions.append((of, "rd", (fl,), "rename and delete"))
 
     repo.ui.note(_("resolving manifests\n"))
-    repo.ui.debug(" overwrite: %s, partial: %s\n"
-                  % (bool(overwrite), bool(partial)))
-    repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, p1, p2))
+    repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
+                  % (bool(branchmerge), bool(force), bool(partial)))
+    repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
 
-    m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
+    m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
     copied = set(copy.values())
     copied.update(movewithdir.values())
 
     if '.hgsubstate' in m1:
         # check whether sub state is modified
-        for s in sorted(p1.substate):
-            if p1.sub(s).dirty():
+        for s in sorted(wctx.substate):
+            if wctx.sub(s).dirty():
                 m1['.hgsubstate'] += "+"
                 break
 
+    aborts, prompts = [], []
     # Compare manifests
-    visit = m1.iteritems()
-    if repo.ui.debugflag:
-        visit = sorted(visit)
-    for f, n in visit:
+    for f, n in m1.iteritems():
         if partial and not partial(f):
             continue
         if f in m2:
@@ -245,72 +242,101 @@
                 pass # remote unchanged - keep local
             elif n == a and fl1 == fla: # local unchanged - use remote
                 if n == n2: # optimization: keep local content
-                    act("update permissions", "e", f, fl2)
+                    actions.append((f, "e", (fl2,), "update permissions"))
                 else:
-                    act("remote is newer", "g", f, fl2)
+                    actions.append((f, "g", (fl2,), "remote is newer"))
             elif nol and n2 == a: # remote only changed 'x'
-                act("update permissions", "e", f, fl2)
+                actions.append((f, "e", (fl2,), "update permissions"))
             elif nol and n == a: # local only changed 'x'
-                act("remote is newer", "g", f, fl1)
+                actions.append((f, "g", (fl1,), "remote is newer"))
             else: # both changed something
-                act("versions differ", "m", f, f, f, False)
+                actions.append((f, "m", (f, f, False), "versions differ"))
         elif f in copied: # files we'll deal with on m2 side
             pass
         elif f in movewithdir: # directory rename
             f2 = movewithdir[f]
-            act("remote renamed directory to " + f2, "d", f, None, f2,
-                m1.flags(f))
+            actions.append((f, "d", (None, f2, m1.flags(f)),
+                            "remote renamed directory to " + f2))
         elif f in copy:
             f2 = copy[f]
-            act("local copied/moved to " + f2, "m", f, f2, f, False)
+            actions.append((f, "m", (f2, f, False),
+                            "local copied/moved to " + f2))
         elif f in ma: # clean, a different, no remote
             if n != ma[f]:
-                if repo.ui.promptchoice(
-                    _(" local changed %s which remote deleted\n"
-                      "use (c)hanged version or (d)elete?") % f,
-                    (_("&Changed"), _("&Delete")), 0):
-                    act("prompt delete", "r", f)
-                else:
-                    act("prompt keep", "a", f)
+                prompts.append((f, "cd")) # prompt changed/deleted
             elif n[20:] == "a": # added, no remote
-                act("remote deleted", "f", f)
+                actions.append((f, "f", None, "remote deleted"))
             else:
-                act("other deleted", "r", f)
+                actions.append((f, "r", None, "other deleted"))
 
-    visit = m2.iteritems()
-    if repo.ui.debugflag:
-        visit = sorted(visit)
-    for f, n in visit:
+    for f, n in m2.iteritems():
         if partial and not partial(f):
             continue
         if f in m1 or f in copied: # files already visited
             continue
         if f in movewithdir:
             f2 = movewithdir[f]
-            act("local renamed directory to " + f2, "d", None, f, f2,
-                m2.flags(f))
+            actions.append((None, "d", (f, f2, m2.flags(f)),
+                            "local renamed directory to " + f2))
         elif f in copy:
             f2 = copy[f]
             if f2 in m2:
-                act("remote copied to " + f, "m",
-                    f2, f, f, False)
+                actions.append((f2, "m", (f, f, False),
+                                "remote copied to " + f))
             else:
-                act("remote moved to " + f, "m",
-                    f2, f, f, True)
+                actions.append((f2, "m", (f, f, True),
+                                "remote moved to " + f))
         elif f not in ma:
-            if (not overwrite
-                and _checkunknownfile(repo, p1, p2, f)):
-                act("remote differs from untracked local",
-                    "m", f, f, f, False)
+            # local unknown, remote created: the logic is described by the
+            # following table:
+            #
+            # force  branchmerge  different  |  action
+            #   n         *           n      |    get
+            #   n         *           y      |   abort
+            #   y         n           *      |    get
+            #   y         y           n      |    get
+            #   y         y           y      |   merge
+            #
+            # Checking whether the files are different is expensive, so we
+            # don't do that when we can avoid it.
+            if force and not branchmerge:
+                actions.append((f, "g", (m2.flags(f),), "remote created"))
             else:
-                act("remote created", "g", f, m2.flags(f))
+                different = _checkunknownfile(repo, wctx, p2, f)
+                if force and branchmerge and different:
+                    actions.append((f, "m", (f, f, False),
+                                    "remote differs from untracked local"))
+                elif not force and different:
+                    aborts.append((f, "ud"))
+                else:
+                    actions.append((f, "g", (m2.flags(f),), "remote created"))
         elif n != ma[f]:
+            prompts.append((f, "dc")) # prompt deleted/changed
+
+    for f, m in sorted(aborts):
+        if m == "ud":
+            repo.ui.warn(_("%s: untracked file differs\n") % f)
+        else: assert False, m
+    if aborts:
+        raise util.Abort(_("untracked files in working directory differ "
+                           "from files in requested revision"))
+
+    for f, m in sorted(prompts):
+        if m == "cd":
+            if repo.ui.promptchoice(
+                _("local changed %s which remote deleted\n"
+                  "use (c)hanged version or (d)elete?") % f,
+                (_("&Changed"), _("&Delete")), 0):
+                actions.append((f, "r", None, "prompt delete"))
+            else:
+                actions.append((f, "a", None, "prompt keep"))
+        elif m == "dc":
             if repo.ui.promptchoice(
                 _("remote changed %s which local deleted\n"
                   "use (c)hanged version or leave (d)eleted?") % f,
                 (_("&Changed"), _("&Deleted")), 0) == 0:
-                act("prompt recreating", "g", f, m2.flags(f))
-
+                actions.append((f, "g", (m2.flags(f),), "prompt recreating"))
+        else: assert False, m
     return actions
 
 def actionkey(a):
@@ -335,12 +361,13 @@
 
     # prescan for merges
     for a in actions:
-        f, m = a[:2]
+        f, m, args, msg = a
+        repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
         if m == "m": # merge
-            f2, fd, move = a[2:]
+            f2, fd, move = args
             if fd == '.hgsubstate': # merged internally
                 continue
-            repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
+            repo.ui.debug("  preserving %s for resolve of %s\n" % (f, fd))
             fcl = wctx[f]
             fco = mctx[f2]
             if mctx == actx: # backwards, use working dir parent as ancestor
@@ -367,7 +394,7 @@
 
     numupdates = len(actions)
     for i, a in enumerate(actions):
-        f, m = a[:2]
+        f, m, args, msg = a
         repo.ui.progress(_('updating'), i + 1, item=f, total=numupdates,
                          unit=_('files'))
         if m == "r": # remove
@@ -386,7 +413,7 @@
                 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
                                  overwrite)
                 continue
-            f2, fd, move = a[2:]
+            f2, fd, move = args
             audit(fd)
             r = ms.resolve(fd, wctx, mctx)
             if r is not None and r > 0:
@@ -397,14 +424,14 @@
                 else:
                     merged += 1
         elif m == "g": # get
-            flags = a[2]
+            flags, = args
             repo.ui.note(_("getting %s\n") % f)
             repo.wwrite(f, mctx.filectx(f).data(), flags)
             updated += 1
             if f == '.hgsubstate': # subrepo states need updating
                 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
         elif m == "d": # directory rename
-            f2, fd, flags = a[2:]
+            f2, fd, flags = args
             if f:
                 repo.ui.note(_("moving %s to %s\n") % (f, fd))
                 audit(f)
@@ -415,19 +442,19 @@
                 repo.wwrite(fd, mctx.filectx(f2).data(), flags)
             updated += 1
         elif m == "dr": # divergent renames
-            fl = a[2]
+            fl, = args
             repo.ui.warn(_("note: possible conflict - %s was renamed "
                            "multiple times to:\n") % f)
             for nf in fl:
                 repo.ui.warn(" %s\n" % nf)
         elif m == "rd": # rename and delete
-            fl = a[2]
+            fl, = args
             repo.ui.warn(_("note: possible conflict - %s was deleted "
                            "and renamed to:\n") % f)
             for nf in fl:
                 repo.ui.warn(" %s\n" % nf)
         elif m == "e": # exec
-            flags = a[2]
+            flags, = args
             audit(f)
             util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
             updated += 1
@@ -447,13 +474,11 @@
             _checkcollision(mctx, None)
         else:
             _checkcollision(mctx, (tctx, ancestor))
-    if not force:
-        _checkunknown(repo, tctx, mctx)
     if tctx.rev() is None:
         actions += _forgetremoved(tctx, mctx, branchmerge)
     actions += manifestmerge(repo, tctx, mctx,
                              ancestor,
-                             force and not branchmerge,
+                             branchmerge, force,
                              partial)
     return actions
 
@@ -461,7 +486,7 @@
     "record merge actions to the dirstate"
 
     for a in actions:
-        f, m = a[:2]
+        f, m, args, msg = a
         if m == "r": # remove
             if branchmerge:
                 repo.dirstate.remove(f)
@@ -480,7 +505,7 @@
             else:
                 repo.dirstate.normal(f)
         elif m == "m": # merge
-            f2, fd, move = a[2:]
+            f2, fd, move = args
             if branchmerge:
                 # We've done a branch merge, mark this file as merged
                 # so that we properly record the merger later
@@ -503,7 +528,7 @@
                 if move:
                     repo.dirstate.drop(f)
         elif m == "d": # directory rename
-            f2, fd, flag = a[2:]
+            f2, fd, flag = args
             if not f2 and f not in repo.dirstate:
                 # untracked file moved
                 continue
--- a/mercurial/parsers.c	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/parsers.c	Sat Feb 09 22:25:58 2013 +0000
@@ -326,7 +326,8 @@
 		if (getintat(v, 3, &mtime) == -1)
 			goto bail;
 		if (*s == 'n' && mtime == (uint32_t)now) {
-			/* See dirstate.py:write for why we do this. */
+			/* See pure/parsers.py:pack_dirstate for why we do
+			 * this. */
 			if (PyDict_SetItem(map, k, dirstate_unset) == -1)
 				goto bail;
 			mode = 0, size = -1, mtime = -1;
--- a/mercurial/pure/parsers.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/pure/parsers.py	Sat Feb 09 22:25:58 2013 +0000
@@ -7,7 +7,7 @@
 
 from mercurial.node import bin, nullid
 from mercurial import util
-import struct, zlib
+import struct, zlib, cStringIO
 
 _pack = struct.pack
 _unpack = struct.unpack
@@ -87,3 +87,29 @@
             copymap[f] = c
         dmap[f] = e[:4]
     return parents
+
+def pack_dirstate(dmap, copymap, pl, now):
+    now = int(now)
+    cs = cStringIO.StringIO()
+    write = cs.write
+    write("".join(pl))
+    for f, e in dmap.iteritems():
+        if e[0] == 'n' and e[3] == now:
+            # The file was last modified "simultaneously" with the current
+            # write to dirstate (i.e. within the same second for file-
+            # systems with a granularity of 1 sec). This commonly happens
+            # for at least a couple of files on 'update'.
+            # The user could change the file without changing its size
+            # within the same second. Invalidate the file's stat data in
+            # dirstate, forcing future 'status' calls to compare the
+            # contents of the file. This prevents mistakenly treating such
+            # files as clean.
+            e = (e[0], 0, -1, -1)   # mark entry as 'unset'
+            dmap[f] = e
+
+        if f in copymap:
+            f = "%s\0%s" % (f, copymap[f])
+        e = _pack(">cllll", e[0], e[1], e[2], e[3], len(f))
+        write(e)
+        write(f)
+    return cs.getvalue()
--- a/mercurial/revlog.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/revlog.py	Sat Feb 09 22:25:58 2013 +0000
@@ -91,6 +91,14 @@
         return bin[1:]
     raise RevlogError(_("unknown compression type %r") % t)
 
+# index v0:
+#  4 bytes: offset
+#  4 bytes: compressed length
+#  4 bytes: base rev
+#  4 bytes: link rev
+# 32 bytes: parent 1 nodeid
+# 32 bytes: parent 2 nodeid
+# 32 bytes: nodeid
 indexformatv0 = ">4l20s20s20s"
 v0shaoffset = 56
 
--- a/mercurial/revset.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/revset.py	Sat Feb 09 22:25:58 2013 +0000
@@ -277,20 +277,32 @@
     return checkstatus(repo, subset, pat, 1)
 
 def ancestor(repo, subset, x):
-    """``ancestor(single, single)``
-    Greatest common ancestor of the two changesets.
+    """``ancestor(*changeset)``
+    Greatest common ancestor of the changesets.
+
+    Accepts 0 or more changesets.
+    Will return empty list when passed no args.
+    Greatest common ancestor of a single changeset is that changeset.
     """
     # i18n: "ancestor" is a keyword
-    l = getargs(x, 2, 2, _("ancestor requires two arguments"))
-    r = list(repo)
-    a = getset(repo, r, l[0])
-    b = getset(repo, r, l[1])
-    if len(a) != 1 or len(b) != 1:
-        # i18n: "ancestor" is a keyword
-        raise error.ParseError(_("ancestor arguments must be single revisions"))
-    an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
+    l = getlist(x)
+    rl = list(repo)
+    anc = None
 
-    return [r for r in an if r in subset]
+    # (getset(repo, rl, i) for i in l) generates a list of lists
+    rev = repo.changelog.rev
+    ancestor = repo.changelog.ancestor
+    node = repo.changelog.node
+    for revs in (getset(repo, rl, i) for i in l):
+        for r in revs:
+            if anc is None:
+                anc = r
+            else:
+                anc = rev(ancestor(node(anc), node(r)))
+
+    if anc is not None and anc in subset:
+        return [anc]
+    return []
 
 def _ancestors(repo, subset, x, followfirst=False):
     args = getset(repo, list(repo), x)
--- a/mercurial/scmutil.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/scmutil.py	Sat Feb 09 22:25:58 2013 +0000
@@ -38,6 +38,11 @@
     for c in (':', '\0', '\n', '\r'):
         if c in lbl:
             raise util.Abort(_("%r cannot be used in a name") % c)
+    try:
+        int(lbl)
+        raise util.Abort(_("a %s cannot have an integer as its name") % kind)
+    except ValueError:
+        pass
 
 def checkfilename(f):
     '''Check that the filename f is an acceptable filename for a tracked file'''
@@ -737,29 +742,33 @@
     rejected = []
     m.bad = lambda x, y: rejected.append(x)
 
-    for abs in repo.walk(m):
-        target = repo.wjoin(abs)
+    ctx = repo[None]
+    walkresults = repo.dirstate.walk(m, sorted(ctx.substate), True, False)
+    for abs in sorted(walkresults):
         good = True
         try:
             audit_path(abs)
         except (OSError, util.Abort):
             good = False
-        rel = m.rel(abs)
-        exact = m.exact(abs)
-        if good and abs not in repo.dirstate:
+
+        st = walkresults[abs]
+        dstate = repo.dirstate[abs]
+        if good and dstate == '?':
             unknown.append(abs)
-            if repo.ui.verbose or not exact:
+            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 (repo.dirstate[abs] != 'r' and
-              (not good or not os.path.lexists(target) or
-               (os.path.isdir(target) and not os.path.islink(target)))):
+        elif (dstate != 'r' and
+              (not good or not st or
+               (stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode)))):
             deleted.append(abs)
-            if repo.ui.verbose or not exact:
+            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 repo.dirstate[abs] == 'r':
+        elif dstate == 'r':
             removed.append(abs)
-        elif repo.dirstate[abs] == 'a':
+        elif dstate == 'a':
             added.append(abs)
     copies = {}
     if similarity > 0:
--- a/mercurial/templater.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/templater.py	Sat Feb 09 22:25:58 2013 +0000
@@ -207,6 +207,19 @@
         f = context._filters[n]
         return (runfilter, (args[0][0], args[0][1], f))
 
+def get(context, mapping, args):
+    if len(args) != 2:
+        # i18n: "get" is a keyword
+        raise error.ParseError(_("get() expects two arguments"))
+
+    dictarg = args[0][0](context, mapping, args[0][1])
+    if not util.safehasattr(dictarg, 'get'):
+        # i18n: "get" is a keyword
+        raise error.ParseError(_("get() expects a dict as first argument"))
+
+    key = args[1][0](context, mapping, args[1][1])
+    yield dictarg.get(key)
+
 def join(context, mapping, args):
     if not (1 <= len(args) <= 2):
         # i18n: "join" is a keyword
@@ -285,11 +298,12 @@
     }
 
 funcs = {
+    "get": get,
     "if": if_,
     "ifeq": ifeq,
     "join": join,
+    "label": label,
     "sub": sub,
-    "label": label,
 }
 
 # template engine
--- a/mercurial/util.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/mercurial/util.py	Sat Feb 09 22:25:58 2013 +0000
@@ -211,6 +211,31 @@
                     del self[i]
                     break
 
+class lrucachedict(object):
+    '''cache most recent gets from or sets to this dictionary'''
+    def __init__(self, maxsize):
+        self._cache = {}
+        self._maxsize = maxsize
+        self._order = deque()
+
+    def __getitem__(self, key):
+        value = self._cache[key]
+        self._order.remove(key)
+        self._order.append(key)
+        return value
+
+    def __setitem__(self, key, value):
+        if key not in self._cache:
+            if len(self._cache) >= self._maxsize:
+                del self._cache[self._order.popleft()]
+        else:
+            self._order.remove(key)
+        self._cache[key] = value
+        self._order.append(key)
+
+    def __contains__(self, key):
+        return key in self._cache
+
 def lrucachefunc(func):
     '''cache most recent results of function calls'''
     cache = {}
@@ -1027,6 +1052,20 @@
 
     The date may be a "unixtime offset" string or in one of the specified
     formats. If the date already is a (unixtime, offset) tuple, it is returned.
+
+    >>> parsedate(' today ') == parsedate(\
+                                  datetime.date.today().strftime('%b %d'))
+    True
+    >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
+                                               datetime.timedelta(days=1)\
+                                              ).strftime('%b %d'))
+    True
+    >>> now, tz = makedate()
+    >>> strnow, strtz = parsedate('now')
+    >>> (strnow - now) < 1
+    True
+    >>> tz == strtz
+    True
     """
     if not date:
         return 0, 0
@@ -1035,6 +1074,15 @@
     if not formats:
         formats = defaultdateformats
     date = date.strip()
+
+    if date == _('now'):
+        return makedate()
+    if date == _('today'):
+        date = datetime.date.today().strftime('%b %d')
+    elif date == _('yesterday'):
+        date = (datetime.date.today() -
+                datetime.timedelta(days=1)).strftime('%b %d')
+
     try:
         when, offset = map(int, date.split(' '))
     except ValueError:
--- a/tests/run-tests.py	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/run-tests.py	Sat Feb 09 22:25:58 2013 +0000
@@ -622,6 +622,7 @@
         script.append('set -x\n')
     if os.getenv('MSYSTEM'):
         script.append('alias pwd="pwd -W"\n')
+    n = 0
     for n, l in enumerate(t):
         if not l.endswith('\n'):
             l += '\n'
--- a/tests/test-bookmarks.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-bookmarks.t	Sat Feb 09 22:25:58 2013 +0000
@@ -257,6 +257,12 @@
   abort: a bookmark cannot have the name of an existing branch
   [255]
 
+bookmark with integer name
+
+  $ hg bookmark 10
+  abort: a bookmark cannot have an integer as its name
+  [255]
+
 incompatible options
 
   $ hg bookmark -m Y -d Z
--- a/tests/test-check-code-hg.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-check-code-hg.t	Sat Feb 09 22:25:58 2013 +0000
@@ -1,6 +1,6 @@
   $ check_code="$TESTDIR"/../contrib/check-code.py
   $ cd "$TESTDIR"/..
-  $ if hg identify -q > /dev/null; then :
+  $ if hg identify -q > /dev/null 2>&1; then :
   > else
   >     echo "skipped: not a Mercurial working dir" >&2
   >     exit 80
@@ -8,4 +8,6 @@
 
 New errors are not allowed. Warnings are strongly discouraged.
 
-  $ hg manifest | xargs "$check_code" --warnings --nolineno --per-file=0
+  $ hg manifest 2>/dev/null \
+  >   | xargs "$check_code" --warnings --nolineno --per-file=0 \
+  >   || false
--- a/tests/test-commit.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-commit.t	Sat Feb 09 22:25:58 2013 +0000
@@ -263,6 +263,7 @@
   $ cd commitmsg
   $ echo changed > changed
   $ echo removed > removed
+  $ hg book currentbookmark
   $ hg ci -qAm init
 
   $ hg rm removed
@@ -277,6 +278,7 @@
   HG: --
   HG: user: test
   HG: branch 'default'
+  HG: bookmark 'currentbookmark'
   HG: added added
   HG: changed changed
   HG: removed removed
--- a/tests/test-convert-git.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-convert-git.t	Sat Feb 09 22:25:58 2013 +0000
@@ -281,24 +281,6 @@
   abort: --sourcesort is not supported by this data source
   [255]
 
-damage git repository and convert again
-
-  $ cat > damage.py <<EOF
-  > import os
-  > import stat
-  > for root, dirs, files in os.walk('git-repo4/.git/objects'):
-  >     if files:
-  >         path = os.path.join(root, files[0])
-  >         if os.name == 'nt':
-  >             os.chmod(path, stat.S_IWUSR)
-  >         os.remove(path)
-  >         break
-  > EOF
-  $ python damage.py
-  $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | \
-  >     grep 'abort:' | sed 's/abort:.*/abort:/g'
-  abort:
-
 test sub modules
 
   $ mkdir git-repo5
@@ -345,3 +327,32 @@
   $ cd git-repo5
   $ cat foo
   sub
+
+  $ cd ../..
+
+damaged git repository tests:
+In case the hard-coded hashes change, the following commands can be used to
+list the hashes and their corresponding types in the repository:
+cd git-repo4/.git/objects
+find . -type f | cut -c 3- | sed 's_/__' | xargs -n 1 -t git cat-file -t
+cd ../../..
+
+damage git repository by renaming a commit object
+  $ COMMIT_OBJ=1c/0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
+  $ mv git-repo4/.git/objects/$COMMIT_OBJ git-repo4/.git/objects/$COMMIT_OBJ.tmp
+  $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
+  abort: cannot read tags from git-repo4/.git
+  $ mv git-repo4/.git/objects/$COMMIT_OBJ.tmp git-repo4/.git/objects/$COMMIT_OBJ
+damage git repository by renaming a blob object
+
+  $ BLOB_OBJ=8b/137891791fe96927ad78e64b0aad7bded08bdc
+  $ mv git-repo4/.git/objects/$BLOB_OBJ git-repo4/.git/objects/$BLOB_OBJ.tmp
+  $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
+  abort: cannot read 'blob' object at 8b137891791fe96927ad78e64b0aad7bded08bdc
+  $ mv git-repo4/.git/objects/$BLOB_OBJ.tmp git-repo4/.git/objects/$BLOB_OBJ
+damage git repository by renaming a tree object
+
+  $ TREE_OBJ=72/49f083d2a63a41cc737764a86981eb5f3e4635
+  $ mv git-repo4/.git/objects/$TREE_OBJ git-repo4/.git/objects/$TREE_OBJ.tmp
+  $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
+  abort: cannot read changes in 1c0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
--- a/tests/test-copy-move-merge.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-copy-move-merge.t	Sat Feb 09 22:25:58 2013 +0000
@@ -29,12 +29,12 @@
      src: 'a' -> dst: 'c' *
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: b8bf91eeebbc, local: add3f11052fa+, remote: 17c05bb7fcb6
    a: remote moved to b -> m
+    preserving a for resolve of b
    a: remote moved to c -> m
-  preserving a for resolve of b
-  preserving a for resolve of c
+    preserving a for resolve of c
   removing a
   updating: a 1/2 files (50.00%)
   picked tool 'internal:merge' for b (binary False symlink False)
--- a/tests/test-double-merge.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-double-merge.t	Sat Feb 09 22:25:58 2013 +0000
@@ -33,12 +33,12 @@
      src: 'foo' -> dst: 'bar' *
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: e6dc8efe11cc, local: 6a0df1dad128+, remote: 484bf6903104
+   foo: remote copied to bar -> m
+    preserving foo for resolve of bar
    foo: versions differ -> m
-   foo: remote copied to bar -> m
-  preserving foo for resolve of bar
-  preserving foo for resolve of foo
+    preserving foo for resolve of foo
   updating: foo 1/2 files (50.00%)
   picked tool 'internal:merge' for bar (binary False symlink False)
   merging foo and bar to bar
--- a/tests/test-export.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-export.t	Sat Feb 09 22:25:58 2013 +0000
@@ -91,13 +91,28 @@
   foo-foo_10.patch
   foo-foo_11.patch
 
+Doing it again clobbers the files rather than appending:
+  $ hg export -v -o "foo-%m.patch" 2:3
+  exporting patches:
+  foo-foo_2.patch
+  foo-foo_3.patch
+  $ grep HG foo-foo_2.patch | wc -l
+  \s*1 (re)
+  $ grep HG foo-foo_3.patch | wc -l
+  \s*1 (re)
+
 Exporting 4 changesets to a file:
 
   $ hg export -o export_internal 1 2 3 4
   $ grep HG export_internal | wc -l
   \s*4 (re)
 
-Exporting 4 changesets to a file:
+Doing it again clobbers the file rather than appending:
+  $ hg export -o export_internal 1 2 3 4
+  $ grep HG export_internal | wc -l
+  \s*4 (re)
+
+Exporting 4 changesets to stdout:
 
   $ hg export 1 2 3 4 | grep HG | wc -l
   \s*4 (re)
--- a/tests/test-graft.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-graft.t	Sat Feb 09 22:25:58 2013 +0000
@@ -134,10 +134,10 @@
      src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: True, partial: False
    ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
    b: local copied/moved to a -> m
-  preserving b for resolve of b
+    preserving b for resolve of b
   updating: b 1/1 files (100.00%)
   picked tool 'internal:merge' for b (binary False symlink False)
   merging b and a to b
@@ -147,7 +147,7 @@
   grafting revision 5
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: True, partial: False
    ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
    e: remote is newer -> g
   updating: e 1/1 files (100.00%)
@@ -156,11 +156,11 @@
   grafting revision 4
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: True, partial: False
    ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
    d: remote is newer -> g
    e: versions differ -> m
-  preserving e for resolve of e
+    preserving e for resolve of e
   updating: d 1/2 files (50.00%)
   getting d
   updating: e 2/2 files (100.00%)
--- a/tests/test-hgweb-raw.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-hgweb-raw.t	Sat Feb 09 22:25:58 2013 +0000
@@ -19,7 +19,7 @@
   $ cat hg.pid >> $DAEMON_PIDS
   $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '?f=bf0ff59095c9;file=sub/some%20text%25.txt;style=raw' content-type content-length content-disposition) >getoutput.txt
 
-  $ while kill `cat hg.pid` 2>/dev/null; do sleep 0; done
+  $ "$TESTDIR/killdaemons.py" hg.pid
 
   $ cat getoutput.txt
   200 Script output follows
@@ -40,7 +40,7 @@
 
   $ cat hg.pid >> $DAEMON_PIDS
   $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '?f=bf0ff59095c9;file=sub/some%20text%25.txt;style=raw' content-type content-length content-disposition) >getoutput.txt
-  $ while kill `cat hg.pid` 2>/dev/null; do sleep 0; done
+  $ "$TESTDIR/killdaemons.py" hg.pid
 
   $ cat getoutput.txt
   200 Script output follows
--- a/tests/test-histedit-edit.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-histedit-edit.t	Sat Feb 09 22:25:58 2013 +0000
@@ -73,7 +73,7 @@
   $ hg up 0
   0 files updated, 0 files merged, 3 files removed, 0 files unresolved
   $ HGEDITOR='echo foobaz > ' hg histedit --continue
-  abort: working directory parent is not a descendant of 055a42cdd887
+  abort: 055a42cdd887 is not an ancestor of working directory
   (update to 055a42cdd887 or descendant and run "hg histedit --continue" again)
   [255]
   $ hg up 3
--- a/tests/test-histedit-revspec.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-histedit-revspec.t	Sat Feb 09 22:25:58 2013 +0000
@@ -65,5 +65,5 @@
   $ hg up 2
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg histedit -r 4
-  nothing to edit
-  [1]
+  abort: 08d98a8350f3 is not an ancestor of working directory
+  [255]
--- a/tests/test-https.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-https.t	Sat Feb 09 22:25:58 2013 +0000
@@ -233,11 +233,13 @@
   (check hostfingerprint configuration)
   [255]
 
+
 - ignores that certificate doesn't match hostname
   $ hg -R copy-pull id https://127.0.0.1:$HGPORT/
   5fed3813f7f5
 
-  $ while kill `cat hg1.pid` 2>/dev/null; do sleep 0; done
+HGPORT1 is reused below for tinyproxy tests. Kill that server.
+  $ "$TESTDIR/killdaemons.py" hg1.pid
 
 Prepare for connecting through proxy
 
--- a/tests/test-inotify-debuginotify.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-inotify-debuginotify.t	Sat Feb 09 22:25:58 2013 +0000
@@ -38,4 +38,4 @@
   directories being watched:
     /
     .hg/
-  $ kill `cat hg.pid`
+  $ "$TESTDIR/killdaemons.py" hg.pid
--- a/tests/test-inotify-issue1371.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-inotify-issue1371.t	Sat Feb 09 22:25:58 2013 +0000
@@ -41,4 +41,4 @@
 
 Are we able to kill the service? if not, the service died on some error
 
-  $ kill `cat hg.pid`
+  $ "$TESTDIR/killdaemons.py" hg.pid
--- a/tests/test-inotify-issue1542.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-inotify-issue1542.t	Sat Feb 09 22:25:58 2013 +0000
@@ -33,4 +33,4 @@
 
 Are we able to kill the service? if not, the service died on some error
 
-  $ kill `cat hg.pid`
+  $ "$TESTDIR/killdaemons.py" hg.pid
--- a/tests/test-inotify-issue1556.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-inotify-issue1556.t	Sat Feb 09 22:25:58 2013 +0000
@@ -28,4 +28,4 @@
 
 Are we able to kill the service? if not, the service died on some error
 
-  $ kill `cat hg.pid`
+  $ "$TESTDIR/killdaemons.py" hg.pid
--- a/tests/test-inotify-lookup.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-inotify-lookup.t	Sat Feb 09 22:25:58 2013 +0000
@@ -11,4 +11,4 @@
   $ hg st
   $ cat a
   a
-  $ kill `cat .hg/inotify.pid`
+  $ "$TESTDIR/killdaemons.py" .hg/inotify.pid
--- a/tests/test-inotify.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-inotify.t	Sat Feb 09 22:25:58 2013 +0000
@@ -32,7 +32,7 @@
 
 make sure that pidfile worked. Output should be silent.
 
-  $ kill `cat ../hg2.pid`
+  $ "$TESTDIR/killdaemons.py" ../hg2.pid
   $ cd ../repo1
 
 inserve
@@ -157,7 +157,7 @@
 build/x & build/y shouldn't appear in "hg st"
 
   $ hg st
-  $ kill `cat hg.pid`
+  $ "$TESTDIR/killdaemons.py" hg.pid
 
   $ cd ..
 
@@ -179,4 +179,4 @@
   abort: inotify-server: cannot start: socket is already bound
   [255]
 
-  $ kill `cat hg3.pid`
+  $ "$TESTDIR/killdaemons.py" hg3.pid
--- a/tests/test-issue1802.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-issue1802.t	Sat Feb 09 22:25:58 2013 +0000
@@ -55,7 +55,7 @@
     unmatched files in local:
      b
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: a03b0deabf2b, local: d6fa54f68ae1+, remote: 2d8bcf2dda39
    a: update permissions -> e
   updating: a 1/1 files (100.00%)
--- a/tests/test-issue522.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-issue522.t	Sat Feb 09 22:25:58 2013 +0000
@@ -29,7 +29,7 @@
     unmatched files in local:
      bar
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: bbd179dfa0a7, local: 71766447bdbb+, remote: 4d9e78aaceee
    foo: remote is newer -> g
   updating: foo 1/1 files (100.00%)
--- a/tests/test-issue672.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-issue672.t	Sat Feb 09 22:25:58 2013 +0000
@@ -32,7 +32,7 @@
      src: '1' -> dst: '1a' 
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 81f4b099af3d, local: c64f439569a9+, remote: c12dcd37c90a
    1: other deleted -> r
    1a: remote created -> g
@@ -63,10 +63,10 @@
      src: '1' -> dst: '1a' *
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: c64f439569a9, local: e327dca35ac8+, remote: 746e9549ea96
    1a: local copied/moved to 1 -> m
-  preserving 1a for resolve of 1a
+    preserving 1a for resolve of 1a
   updating: 1a 1/1 files (100.00%)
   picked tool 'internal:merge' for 1a (binary False symlink False)
   merging 1a and 1 to 1a
@@ -86,10 +86,10 @@
      src: '1' -> dst: '1a' *
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: c64f439569a9, local: 746e9549ea96+, remote: e327dca35ac8
    1: remote moved to 1a -> m
-  preserving 1 for resolve of 1a
+    preserving 1 for resolve of 1a
   removing 1
   updating: 1 1/1 files (100.00%)
   picked tool 'internal:merge' for 1a (binary False symlink False)
--- a/tests/test-largefiles-cache.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-largefiles-cache.t	Sat Feb 09 22:25:58 2013 +0000
@@ -37,7 +37,7 @@
   adding file changes
   added 1 changesets with 1 changes to 1 files
   (run 'hg update' to get a working copy)
-  caching new largefiles
+  caching largefiles for 1 heads
   0 largefiles cached
 
 Update working directory to "tip", which requires largefile("large"),
--- a/tests/test-largefiles.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-largefiles.t	Sat Feb 09 22:25:58 2013 +0000
@@ -883,7 +883,7 @@
   adding file changes
   added 6 changesets with 16 changes to 8 files
   (run 'hg update' to get a working copy)
-  caching new largefiles
+  caching largefiles for 1 heads
   3 largefiles cached
   3 additional largefiles cached
   $ cd ..
@@ -974,7 +974,7 @@
   adding file changes
   added 1 changesets with 2 changes to 2 files (+1 heads)
   (run 'hg heads' to see heads, 'hg merge' to merge)
-  caching new largefiles
+  caching largefiles for 1 heads
   0 largefiles cached
   $ hg rebase
   Invoking status precommit hook
@@ -1210,20 +1210,14 @@
   checking files
   10 files, 10 changesets, 28 total revisions
   searching 1 changesets for largefiles
-  changeset 9:598410d3eb9a: sub/large4 missing
-    (looked for hash e166e74c7303192238d60af5a9c4ce9bef0b7928)
+  changeset 9:598410d3eb9a: sub/large4 references missing $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 (glob)
   verified existence of 3 revisions of 3 largefiles
   [1]
 
 - introduce corruption and make sure that it is caught when checking content:
   $ echo '5 cents' > $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928
   $ hg verify -q --large --lfc
-  searching 1 changesets for largefiles
-  changeset 9:598410d3eb9a: sub/large4: contents differ
-    ($TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928: (glob)
-    expected hash e166e74c7303192238d60af5a9c4ce9bef0b7928,
-    but got 1f19b76d5b3cad1472c87efb42b582c97e040060)
-  verified contents of 3 revisions of 3 largefiles
+  changeset 9:598410d3eb9a: sub/large4 references corrupted $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 (glob)
   [1]
 
 - cleanup
@@ -1231,31 +1225,14 @@
 
 - verifying all revisions will fail because we didn't clone all largefiles to d:
   $ echo 'T-shirt' > $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
-  $ hg verify -q --large --lfa --lfc
-  searching 10 changesets for largefiles
-  changeset 0:30d30fe6a5be: large1 missing
-    (looked for hash 4669e532d5b2c093a78eca010077e708a071bb64)
-  changeset 0:30d30fe6a5be: sub/large2 missing
-    (looked for hash 1deebade43c8c498a3c8daddac0244dc55d1331d)
-  changeset 1:ce8896473775: large1 missing
-    (looked for hash 5f78770c0e77ba4287ad6ef3071c9bf9c379742f)
-  changeset 1:ce8896473775: sub/large2: contents differ
-    ($TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4: (glob)
-    expected hash eb7338044dc27f9bc59b8dd5a246b065ead7a9c4,
-    but got cfef678f24d3e339944138ecdd8fd85ca21d820f)
-  changeset 3:9e8fbc4bce62: large1: contents differ
-    ($TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4: (glob)
-    expected hash eb7338044dc27f9bc59b8dd5a246b065ead7a9c4,
-    but got cfef678f24d3e339944138ecdd8fd85ca21d820f)
-  changeset 4:74c02385b94c: large3: contents differ
-    ($TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4: (glob)
-    expected hash eb7338044dc27f9bc59b8dd5a246b065ead7a9c4,
-    but got cfef678f24d3e339944138ecdd8fd85ca21d820f)
-  changeset 4:74c02385b94c: sub/large4: contents differ
-    ($TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4: (glob)
-    expected hash eb7338044dc27f9bc59b8dd5a246b065ead7a9c4,
-    but got cfef678f24d3e339944138ecdd8fd85ca21d820f)
-  verified contents of 15 revisions of 6 largefiles
+  $ hg verify -q --lfa --lfc
+  changeset 0:30d30fe6a5be: large1 references missing $TESTTMP/d/.hg/largefiles/4669e532d5b2c093a78eca010077e708a071bb64 (glob)
+  changeset 0:30d30fe6a5be: sub/large2 references missing $TESTTMP/d/.hg/largefiles/1deebade43c8c498a3c8daddac0244dc55d1331d (glob)
+  changeset 1:ce8896473775: large1 references missing $TESTTMP/d/.hg/largefiles/5f78770c0e77ba4287ad6ef3071c9bf9c379742f (glob)
+  changeset 1:ce8896473775: sub/large2 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
+  changeset 3:9e8fbc4bce62: large1 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
+  changeset 4:74c02385b94c: large3 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
+  changeset 4:74c02385b94c: sub/large4 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 (glob)
   [1]
 
 - cleanup
@@ -1296,7 +1273,7 @@
   adding file changes
   added 2 changesets with 4 changes to 4 files (+1 heads)
   (run 'hg heads' to see heads, 'hg merge' to merge)
-  caching new largefiles
+  caching largefiles for 1 heads
   2 largefiles cached
   $ hg merge
   merging sub/large4
@@ -1664,8 +1641,6 @@
   [1]
   $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/
   $ hg -R http-clone -q verify --large --lfa
-  searching 1 changesets for largefiles
-  verified existence of 1 revisions of 1 largefiles
 
 largefiles pulled on update - a largefile missing on the server:
   $ mv empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 .
@@ -1701,7 +1676,7 @@
   $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/
   $ hg -R http-clone --debug up --config largefiles.usercache=http-clone-usercache
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: False, force: False, partial: False
    ancestor: 000000000000, local: 000000000000+, remote: cf03e5bb9936
    .hglf/f1: remote created -> g
   updating: .hglf/f1 1/1 files (100.00%)
--- a/tests/test-lfconvert.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-lfconvert.t	Sat Feb 09 22:25:58 2013 +0000
@@ -317,18 +317,12 @@
   checking files
   8 files, 7 changesets, 12 total revisions
   searching 7 changesets for largefiles
-  changeset 0:d4892ec57ce2: large missing
-    (looked for hash 2e000fa7e85759c7f4c254d4d9c33ef481e459a7)
-  changeset 1:334e5237836d: sub/maybelarge.dat missing
-    (looked for hash 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c)
-  changeset 2:261ad3f3f037: stuff/maybelarge.dat missing
-    (looked for hash 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c)
-  changeset 3:55759520c76f: sub/maybelarge.dat missing
-    (looked for hash 76236b6a2c6102826c61af4297dd738fb3b1de38)
-  changeset 5:9cc5aa7204f0: stuff/maybelarge.dat missing
-    (looked for hash 76236b6a2c6102826c61af4297dd738fb3b1de38)
-  changeset 6:17126745edfd: anotherlarge missing
-    (looked for hash 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3)
+  changeset 0:d4892ec57ce2: large references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/2e000fa7e85759c7f4c254d4d9c33ef481e459a7 (glob)
+  changeset 1:334e5237836d: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c (glob)
+  changeset 2:261ad3f3f037: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c (glob)
+  changeset 3:55759520c76f: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/76236b6a2c6102826c61af4297dd738fb3b1de38 (glob)
+  changeset 5:9cc5aa7204f0: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/76236b6a2c6102826c61af4297dd738fb3b1de38 (glob)
+  changeset 6:17126745edfd: anotherlarge references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3 (glob)
   verified existence of 6 revisions of 4 largefiles
   [1]
   $ hg -R largefiles-repo-hg showconfig paths
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-lrucachedict.py	Sat Feb 09 22:25:58 2013 +0000
@@ -0,0 +1,35 @@
+from mercurial import util
+
+def printifpresent(d, xs):
+    for x in xs:
+        present = x in d
+        print "'%s' in d: %s" % (x, present)
+        if present:
+            print "d['%s']: %s" % (x, d[x])
+
+def test_lrucachedict():
+    d = util.lrucachedict(4)
+    d['a'] = 'va'
+    d['b'] = 'vb'
+    d['c'] = 'vc'
+    d['d'] = 'vd'
+
+    # all of these should be present
+    printifpresent(d, ['a', 'b', 'c', 'd'])
+
+    # 'a' should be dropped because it was least recently used
+    d['e'] = 've'
+    printifpresent(d, ['a', 'b', 'c', 'd', 'e'])
+
+    # touch entries in some order (get or set).
+    d['e']
+    d['c'] = 'vc2'
+    d['d']
+    d['b'] = 'vb2'
+
+    # 'e' should be dropped now
+    d['f'] = 'vf'
+    printifpresent(d, ['b', 'c', 'd', 'e', 'f'])
+
+if __name__ == '__main__':
+    test_lrucachedict()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-lrucachedict.py.out	Sat Feb 09 22:25:58 2013 +0000
@@ -0,0 +1,26 @@
+'a' in d: True
+d['a']: va
+'b' in d: True
+d['b']: vb
+'c' in d: True
+d['c']: vc
+'d' in d: True
+d['d']: vd
+'a' in d: False
+'b' in d: True
+d['b']: vb
+'c' in d: True
+d['c']: vc
+'d' in d: True
+d['d']: vd
+'e' in d: True
+d['e']: ve
+'b' in d: True
+d['b']: vb2
+'c' in d: True
+d['c']: vc2
+'d' in d: True
+d['d']: vd
+'e' in d: False
+'f' in d: True
+d['f']: vf
--- a/tests/test-merge-commit.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-merge-commit.t	Sat Feb 09 22:25:58 2013 +0000
@@ -69,10 +69,10 @@
   $ hg --debug merge 3
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 0f2ff26688b9, local: 2263c1be0967+, remote: 0555950ead28
    bar: versions differ -> m
-  preserving bar for resolve of bar
+    preserving bar for resolve of bar
   updating: bar 1/1 files (100.00%)
   picked tool 'internal:merge' for bar (binary False symlink False)
   merging bar
@@ -156,10 +156,10 @@
   $ hg --debug merge 3
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 0f2ff26688b9, local: 2263c1be0967+, remote: 3ffa6b9e35f0
    bar: versions differ -> m
-  preserving bar for resolve of bar
+    preserving bar for resolve of bar
   updating: bar 1/1 files (100.00%)
   picked tool 'internal:merge' for bar (binary False symlink False)
   merging bar
--- a/tests/test-merge-prompt.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-merge-prompt.t	Sat Feb 09 22:25:58 2013 +0000
@@ -42,7 +42,7 @@
 Non-interactive merge:
 
   $ hg merge -y
-   local changed file1 which remote deleted
+  local changed file1 which remote deleted
   use (c)hanged version or (d)elete? c
   remote changed file2 which local deleted
   use (c)hanged version or leave (d)eleted? c
@@ -70,7 +70,7 @@
   > c
   > d
   > EOF
-   local changed file1 which remote deleted
+  local changed file1 which remote deleted
   use (c)hanged version or (d)elete? remote changed file2 which local deleted
   use (c)hanged version or leave (d)eleted? 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
@@ -97,11 +97,11 @@
   > baz
   > c
   > EOF
-   local changed file1 which remote deleted
+  local changed file1 which remote deleted
   use (c)hanged version or (d)elete? unrecognized response
-   local changed file1 which remote deleted
+  local changed file1 which remote deleted
   use (c)hanged version or (d)elete? unrecognized response
-   local changed file1 which remote deleted
+  local changed file1 which remote deleted
   use (c)hanged version or (d)elete? remote changed file2 which local deleted
   use (c)hanged version or leave (d)eleted? unrecognized response
   remote changed file2 which local deleted
@@ -126,7 +126,7 @@
   $ hg merge --config ui.interactive=true <<EOF
   > d
   > EOF
-   local changed file1 which remote deleted
+  local changed file1 which remote deleted
   use (c)hanged version or (d)elete? remote changed file2 which local deleted
   use (c)hanged version or leave (d)eleted? abort: response expected
   [255]
--- a/tests/test-merge-types.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-merge-types.t	Sat Feb 09 22:25:58 2013 +0000
@@ -32,10 +32,10 @@
   $ hg merge --debug
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: c334dc3be0da, local: 521a1e40188f+, remote: 3574f3e69b1c
    a: versions differ -> m
-  preserving a for resolve of a
+    preserving a for resolve of a
   updating: a 1/1 files (100.00%)
   picked tool 'internal:merge' for a (binary False symlink True)
   merging a
@@ -65,10 +65,10 @@
   $ hg merge --debug
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
    a: versions differ -> m
-  preserving a for resolve of a
+    preserving a for resolve of a
   updating: a 1/1 files (100.00%)
   picked tool 'internal:merge' for a (binary False symlink True)
   merging a
@@ -99,10 +99,10 @@
   $ HGMERGE= hg up -y --debug
     searching for copies back to rev 2
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: False, force: False, partial: False
    ancestor: c334dc3be0da, local: c334dc3be0da+, remote: 521a1e40188f
    a: versions differ -> m
-  preserving a for resolve of a
+    preserving a for resolve of a
   updating: a 1/1 files (100.00%)
   (couldn't find merge tool hgmerge|tool hgmerge can't handle symlinks) (re)
   picked tool 'internal:prompt' for a (binary False symlink True)
--- a/tests/test-merge7.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-merge7.t	Sat Feb 09 22:25:58 2013 +0000
@@ -81,10 +81,10 @@
   $ hg merge --debug
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 96b70246a118, local: 50c3a7e29886+, remote: 40d11a4173a8
    test.txt: versions differ -> m
-  preserving test.txt for resolve of test.txt
+    preserving test.txt for resolve of test.txt
   updating: test.txt 1/1 files (100.00%)
   picked tool 'internal:merge' for test.txt (binary False symlink False)
   merging test.txt
--- a/tests/test-obsolete.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-obsolete.t	Sat Feb 09 22:25:58 2013 +0000
@@ -752,7 +752,7 @@
 
 check that web.view config option:
 
-  $ kill `cat hg.pid`
+  $ "$TESTDIR/killdaemons.py" hg.pid
   $ cat >> .hg/hgrc << EOF
   > [web]
   > view=all
@@ -761,7 +761,7 @@
   $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
   $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/67'
   200 Script output follows
-  $ kill `cat hg.pid`
+  $ "$TESTDIR/killdaemons.py" hg.pid
 
 Checking _enable=False warning if obsolete marker exists
 
--- a/tests/test-parse-date.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-parse-date.t	Sat Feb 09 22:25:58 2013 +0000
@@ -234,3 +234,25 @@
   Sat Apr 15 13:30:00 2006 +0000
   Wed Feb 01 13:00:30 2006 -0500
   Wed Feb 01 13:00:30 2006 +0000
+
+Test issue 3764 (interpreting 'today' and 'yesterday')
+  $ echo "hello" >> a
+  >>> import datetime
+  >>> today = datetime.date.today().strftime("%b %d")
+  >>> yesterday = (datetime.date.today() - datetime.timedelta(days=1)).strftime("%b %d")
+  >>> dates = open('dates', 'w')
+  >>> dates.write(today + '\n')
+  >>> dates.write(yesterday)
+  >>> dates.close()
+  $ hg ci -d "`sed -n '1p' dates`" -m "today is a good day to code"
+  $ hg log -d today --template '{desc}\n'
+  today is a good day to code
+  $ echo "goodbye" >> a
+  $ hg ci -d "`sed -n '2p' dates`" -m "the time traveler's code"
+  $ hg log -d yesterday --template '{desc}\n'
+  the time traveler's code
+  $ echo "foo" >> a
+  $ hg commit -d now -m 'Explicitly committed now.'
+  $ hg log -d today --template '{desc}\n'
+  Explicitly committed now.
+  today is a good day to code
--- a/tests/test-rebase-collapse.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-rebase-collapse.t	Sat Feb 09 22:25:58 2013 +0000
@@ -496,15 +496,15 @@
   $ hg ci -Am 'A'
   adding a
 
-  $ hg branch '1'
-  marked working directory as branch 1
+  $ hg branch 'one'
+  marked working directory as branch one
   (branches are permanent and global, did you want a bookmark?)
   $ echo 'b' > b
   $ hg ci -Am 'B'
   adding b
 
-  $ hg branch '2'
-  marked working directory as branch 2
+  $ hg branch 'two'
+  marked working directory as branch two
   (branches are permanent and global, did you want a bookmark?)
   $ echo 'c' > c
   $ hg ci -Am 'C'
@@ -518,9 +518,9 @@
   $ hg tglog
   @  3: 'D'
   |
-  | o  2: 'C' 2
+  | o  2: 'C' two
   | |
-  | o  1: 'B' 1
+  | o  1: 'B' one
   |/
   o  0: 'A'
   
@@ -546,9 +546,9 @@
   |/
   o  3: 'D'
   |
-  | o  2: 'C' 2
+  | o  2: 'C' two
   | |
-  | o  1: 'B' 1
+  | o  1: 'B' one
   |/
   o  0: 'A'
   
@@ -559,9 +559,9 @@
   |
   o  3: 'D'
   |
-  | o  2: 'C' 2
+  | o  2: 'C' two
   | |
-  | o  1: 'B' 1
+  | o  1: 'B' one
   |/
   o  0: 'A'
   
--- a/tests/test-rename-dir-merge.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-rename-dir-merge.t	Sat Feb 09 22:25:58 2013 +0000
@@ -37,7 +37,7 @@
      discovered dir src: 'a/' -> dst: 'b/'
      pending file src: 'a/c' -> dst: 'b/c'
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: f9b20c0d4c51, local: ce36d17b18fb+, remote: 397f8b00a740
    a/a: other deleted -> r
    a/b: other deleted -> r
@@ -88,7 +88,7 @@
      discovered dir src: 'a/' -> dst: 'b/'
      pending file src: 'a/c' -> dst: 'b/c'
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: f9b20c0d4c51, local: 397f8b00a740+, remote: ce36d17b18fb
    None: local renamed directory to b/c -> d
   updating:None 1/1 files (100.00%)
--- a/tests/test-rename-merge1.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-rename-merge1.t	Sat Feb 09 22:25:58 2013 +0000
@@ -33,13 +33,13 @@
      src: 'a2' -> dst: 'b2' !
      src: 'a2' -> dst: 'c2' !
     checking for directory renames
-   a2: divergent renames -> dr
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: af1939970a1c, local: 044f8520aeeb+, remote: 85c198ef2f6c
    a: remote moved to b -> m
+    preserving a for resolve of b
+   a2: divergent renames -> dr
    b2: remote created -> g
-  preserving a for resolve of b
   removing a
   updating: a 1/3 files (33.33%)
   picked tool 'internal:merge' for b (binary False symlink False)
@@ -178,10 +178,10 @@
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
      src: 'file' -> dst: 'newfile' %
     checking for directory renames
+  resolving manifests
+   branchmerge: True, force: False, partial: False
+   ancestor: 19d7f95df299, local: 0084274f6b67+, remote: 5d32493049f0
    file: rename and delete -> rd
-  resolving manifests
-   overwrite: False, partial: False
-   ancestor: 19d7f95df299, local: 0084274f6b67+, remote: 5d32493049f0
    newfile: remote created -> g
   updating: file 1/2 files (50.00%)
   note: possible conflict - file was deleted and renamed to:
--- a/tests/test-rename-merge2.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-rename-merge2.t	Sat Feb 09 22:25:58 2013 +0000
@@ -84,12 +84,12 @@
      src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: e300d1c794ec+, remote: 4ce40f5aca24
+   a: remote copied to b -> m
+    preserving a for resolve of b
    rev: versions differ -> m
-   a: remote copied to b -> m
-  preserving a for resolve of b
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: a 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging a and b to b
@@ -119,13 +119,13 @@
      src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 86a2aa42fc76+, remote: f4db7e329e71
    a: remote is newer -> g
    b: local copied/moved to a -> m
+    preserving b for resolve of b
    rev: versions differ -> m
-  preserving b for resolve of b
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: a 1/3 files (33.33%)
   getting a
   updating: b 2/3 files (66.67%)
@@ -157,12 +157,12 @@
      src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: e300d1c794ec+, remote: bdb19105162a
+   a: remote moved to b -> m
+    preserving a for resolve of b
    rev: versions differ -> m
-   a: remote moved to b -> m
-  preserving a for resolve of b
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   removing a
   updating: a 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
@@ -192,12 +192,12 @@
      src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 02963e448370+, remote: f4db7e329e71
    b: local copied/moved to a -> m
+    preserving b for resolve of b
    rev: versions differ -> m
-  preserving b for resolve of b
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: b 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b and a to b
@@ -226,11 +226,11 @@
      src: 'a' -> dst: 'b' 
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: 4ce40f5aca24
+   b: remote created -> g
    rev: versions differ -> m
-   b: remote created -> g
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: b 1/2 files (50.00%)
   getting b
   updating: rev 2/2 files (100.00%)
@@ -256,10 +256,10 @@
      src: 'a' -> dst: 'b' 
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 97c705ade336
    rev: versions differ -> m
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: rev 1/1 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
@@ -283,12 +283,12 @@
      src: 'a' -> dst: 'b' 
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 94b33a1b7f2d+, remote: bdb19105162a
    a: other deleted -> r
+   b: remote created -> g
    rev: versions differ -> m
-   b: remote created -> g
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: a 1/3 files (33.33%)
   removing a
   updating: b 2/3 files (66.67%)
@@ -315,10 +315,10 @@
      src: 'a' -> dst: 'b' 
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 02963e448370+, remote: 97c705ade336
    rev: versions differ -> m
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: rev 1/1 files (100.00%)
   picked tool 'python ../merge' for rev (binary False symlink False)
   merging rev
@@ -336,12 +336,12 @@
   --------------
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 62e7bf090eba+, remote: 49b6d8032493
    b: versions differ -> m
+    preserving b for resolve of b
    rev: versions differ -> m
-  preserving b for resolve of b
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: b 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b
@@ -373,13 +373,13 @@
      src: 'a' -> dst: 'b' !
      src: 'a' -> dst: 'c' !
     checking for directory renames
-   a: divergent renames -> dr
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 02963e448370+, remote: fe905ef2c33e
-   rev: versions differ -> m
+   a: divergent renames -> dr
    c: remote created -> g
-  preserving rev for resolve of rev
+   rev: versions differ -> m
+    preserving rev for resolve of rev
   updating: a 1/3 files (33.33%)
   note: possible conflict - a was renamed multiple times to:
    b
@@ -404,12 +404,12 @@
   --------------
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 86a2aa42fc76+, remote: af30c7647fc7
    b: versions differ -> m
+    preserving b for resolve of b
    rev: versions differ -> m
-  preserving b for resolve of b
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: b 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b
@@ -432,13 +432,13 @@
   --------------
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
    a: other deleted -> r
    b: versions differ -> m
+    preserving b for resolve of b
    rev: versions differ -> m
-  preserving b for resolve of b
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: a 1/3 files (33.33%)
   removing a
   updating: b 2/3 files (66.67%)
@@ -462,13 +462,13 @@
   --------------
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
    a: remote is newer -> g
    b: versions differ -> m
+    preserving b for resolve of b
    rev: versions differ -> m
-  preserving b for resolve of b
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: a 1/3 files (33.33%)
   getting a
   updating: b 2/3 files (66.67%)
@@ -493,13 +493,13 @@
   --------------
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
    a: other deleted -> r
    b: versions differ -> m
+    preserving b for resolve of b
    rev: versions differ -> m
-  preserving b for resolve of b
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: a 1/3 files (33.33%)
   removing a
   updating: b 2/3 files (66.67%)
@@ -523,13 +523,13 @@
   --------------
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
    a: remote is newer -> g
    b: versions differ -> m
+    preserving b for resolve of b
    rev: versions differ -> m
-  preserving b for resolve of b
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: a 1/3 files (33.33%)
   getting a
   updating: b 2/3 files (66.67%)
@@ -554,12 +554,12 @@
   --------------
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 0b76e65c8289+, remote: 4ce40f5aca24
    b: versions differ -> m
+    preserving b for resolve of b
    rev: versions differ -> m
-  preserving b for resolve of b
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: b 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b
@@ -582,15 +582,15 @@
   --------------
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 02963e448370+, remote: 8dbce441892a
-   b: versions differ -> m
-   rev: versions differ -> m
   remote changed a which local deleted
   use (c)hanged version or leave (d)eleted? c
    a: prompt recreating -> g
-  preserving b for resolve of b
-  preserving rev for resolve of rev
+   b: versions differ -> m
+    preserving b for resolve of b
+   rev: versions differ -> m
+    preserving rev for resolve of rev
   updating: a 1/3 files (33.33%)
   getting a
   updating: b 2/3 files (66.67%)
@@ -615,15 +615,15 @@
   --------------
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 0b76e65c8289+, remote: bdb19105162a
-   local changed a which remote deleted
+  local changed a which remote deleted
   use (c)hanged version or (d)elete? c
    a: prompt keep -> a
    b: versions differ -> m
+    preserving b for resolve of b
    rev: versions differ -> m
-  preserving b for resolve of b
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: a 1/3 files (33.33%)
   updating: b 2/3 files (66.67%)
   picked tool 'python ../merge' for b (binary False symlink False)
@@ -652,12 +652,12 @@
      src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: e300d1c794ec+, remote: 49b6d8032493
+   a: remote moved to b -> m
+    preserving a for resolve of b
    rev: versions differ -> m
-   a: remote moved to b -> m
-  preserving a for resolve of b
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   removing a
   updating: a 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
@@ -686,12 +686,12 @@
      src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 62e7bf090eba+, remote: f4db7e329e71
    b: local copied/moved to a -> m
+    preserving b for resolve of b
    rev: versions differ -> m
-  preserving b for resolve of b
-  preserving rev for resolve of rev
+    preserving rev for resolve of rev
   updating: b 1/2 files (50.00%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b and a to b
@@ -724,13 +724,13 @@
      src: 'a' -> dst: 'b' *
     checking for directory renames
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 02963e448370+, remote: 2b958612230f
    b: local copied/moved to a -> m
-   rev: versions differ -> m
+    preserving b for resolve of b
    c: remote created -> g
-  preserving b for resolve of b
-  preserving rev for resolve of rev
+   rev: versions differ -> m
+    preserving rev for resolve of rev
   updating: b 1/3 files (33.33%)
   picked tool 'python ../merge' for b (binary False symlink False)
   merging b and a to b
--- a/tests/test-revset.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-revset.t	Sat Feb 09 22:25:58 2013 +0000
@@ -218,17 +218,29 @@
   $ log 'date(2005) and 1::'
   4
 
+ancestor can accept 0 or more arguments
+
+  $ log 'ancestor()'
   $ log 'ancestor(1)'
-  hg: parse error: ancestor requires two arguments
-  [255]
+  1
   $ log 'ancestor(4,5)'
   1
   $ log 'ancestor(4,5) and 4'
+  $ log 'ancestor(0,0,1,3)'
+  0
+  $ log 'ancestor(3,1,5,3,5,1)'
+  1
+  $ log 'ancestor(0,1,3,5)'
+  0
+  $ log 'ancestor(1,2,3,4,5)'
+  1
   $ log 'ancestors(5)'
   0
   1
   3
   5
+  $ log 'ancestor(ancestors(5))'
+  0
   $ log 'author(bob)'
   2
   $ log 'author("re:bob|test")'
--- a/tests/test-serve.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-serve.t	Sat Feb 09 22:25:58 2013 +0000
@@ -9,12 +9,7 @@
   >    cat hg.pid >> "$DAEMON_PIDS"
   >    echo % errors
   >    cat errors.log
-  >    if [ "$KILLQUIETLY" = "Y" ]; then
-  >        kill `cat hg.pid` 2>/dev/null
-  >    else
-  >        kill `cat hg.pid`
-  >    fi
-  >    while kill -0 `cat hg.pid` 2>/dev/null; do sleep 0; done
+  >    "$TESTDIR/killdaemons.py" hg.pid
   > }
 
   $ hg init test
--- a/tests/test-subrepo.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-subrepo.t	Sat Feb 09 22:25:58 2013 +0000
@@ -203,16 +203,15 @@
   $ hg merge 6 --debug # test change
     searching for copies back to rev 2
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
    .hgsubstate: versions differ -> m
   updating: .hgsubstate 1/1 files (100.00%)
   subrepo merge f0d2028bf86d+ 1831e14459c4 1f14a2e2d3ec
     subrepo t: other changed, get t:6747d179aa9a688023c4b0cad32e4c92bb7f34ad:hg
   getting subrepo t
-    searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: False, force: False, partial: False
    ancestor: 60ca1237c194, local: 60ca1237c194+, remote: 6747d179aa9a
    t: remote is newer -> g
   updating: t 1/1 files (100.00%)
@@ -232,7 +231,7 @@
   $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
     searching for copies back to rev 2
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
    .hgsubstate: versions differ -> m
   updating: .hgsubstate 1/1 files (100.00%)
@@ -241,10 +240,10 @@
   merging subrepo t
     searching for copies back to rev 2
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: False, partial: False
    ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
    t: versions differ -> m
-  preserving t for resolve of t
+    preserving t for resolve of t
   updating: t 1/1 files (100.00%)
   picked tool 'internal:merge' for t (binary False symlink False)
   merging t
--- a/tests/test-up-local-change.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-up-local-change.t	Sat Feb 09 22:25:58 2013 +0000
@@ -44,11 +44,11 @@
     unmatched files in other:
      b
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: False, force: False, partial: False
    ancestor: c19d34741b0a, local: c19d34741b0a+, remote: 1e71731e6fbb
    a: versions differ -> m
+    preserving a for resolve of a
    b: remote created -> g
-  preserving a for resolve of a
   updating: a 1/2 files (50.00%)
   picked tool 'true' for a (binary False symlink False)
   merging a
@@ -65,11 +65,11 @@
   
   $ hg --debug up 0
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: False, force: False, partial: False
    ancestor: 1e71731e6fbb, local: 1e71731e6fbb+, remote: c19d34741b0a
+   b: other deleted -> r
    a: versions differ -> m
-   b: other deleted -> r
-  preserving a for resolve of a
+    preserving a for resolve of a
   updating: b 1/2 files (50.00%)
   removing b
   updating: a 2/2 files (100.00%)
@@ -98,11 +98,11 @@
     unmatched files in other:
      b
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: False, force: False, partial: False
    ancestor: c19d34741b0a, local: c19d34741b0a+, remote: 1e71731e6fbb
    a: versions differ -> m
+    preserving a for resolve of a
    b: remote created -> g
-  preserving a for resolve of a
   updating: a 1/2 files (50.00%)
   picked tool 'true' for a (binary False symlink False)
   merging a
@@ -176,12 +176,12 @@
   $ hg --debug merge -f
     searching for copies back to rev 1
   resolving manifests
-   overwrite: False, partial: False
+   branchmerge: True, force: True, partial: False
    ancestor: c19d34741b0a, local: 1e71731e6fbb+, remote: 83c51d0caff4
    a: versions differ -> m
+    preserving a for resolve of a
    b: versions differ -> m
-  preserving a for resolve of a
-  preserving b for resolve of b
+    preserving b for resolve of b
   updating: a 1/2 files (50.00%)
   picked tool 'true' for a (binary False symlink False)
   merging a
--- a/tests/test-update-reverse.t	Sat Feb 09 17:54:01 2013 +0000
+++ b/tests/test-update-reverse.t	Sat Feb 09 22:25:58 2013 +0000
@@ -66,7 +66,7 @@
 
   $ hg update --debug -C 1
   resolving manifests
-   overwrite: True, partial: False
+   branchmerge: False, force: True, partial: False
    ancestor: 91ebc10ed028+, local: 91ebc10ed028+, remote: 71a760306caf
    side1: other deleted -> r
    side2: other deleted -> r