changeset 24163:bb11081562d7

merge with stable
author Matt Mackall <mpm@selenic.com>
date Mon, 02 Mar 2015 01:20:14 -0600
parents 758dd85b6ad6 (diff) 80af610c4ffb (current diff)
children 90f08728e0f7
files hgext/largefiles/overrides.py hgext/largefiles/reposetup.py mercurial/branchmap.py mercurial/dispatch.py mercurial/revset.py tests/test-largefiles-misc.t tests/test-revset.t
diffstat 102 files changed, 2115 insertions(+), 627 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/check-commit	Mon Mar 02 01:06:31 2015 -0600
+++ b/contrib/check-commit	Mon Mar 02 01:20:14 2015 -0600
@@ -25,6 +25,7 @@
     (r"^# .*\n(?!merge with )[^#]\S+[^:] ",
      "summary line doesn't start with 'topic: '"),
     (r"^# .*\n[A-Z][a-z]\S+", "don't capitalize summary lines"),
+    (r"^# .*\n[^\n]*: *[A-Z][a-z]\S+", "don't capitalize summary lines"),
     (r"^# .*\n.*\.\s+$", "don't add trailing period on summary line"),
     (r"^# .*\n.{78,}", "summary line too long"),
     (r"^\+\n \n", "adds double empty line"),
--- a/hgext/churn.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/hgext/churn.py	Mon Mar 02 01:20:14 2015 -0600
@@ -46,7 +46,7 @@
             date = datetime.datetime(*time.gmtime(float(t) - tz)[:6])
             return date.strftime(opts['dateformat'])
     else:
-        tmpl = opts.get('template', '{author|email}')
+        tmpl = opts.get('oldtemplate') or opts.get('template')
         tmpl = maketemplater(ui, repo, tmpl)
         def getkey(ctx):
             ui.pushbuffer()
@@ -95,7 +95,9 @@
      _('count rate for the specified revision or revset'), _('REV')),
     ('d', 'date', '',
      _('count rate for revisions matching date spec'), _('DATE')),
-    ('t', 'template', '{author|email}',
+    ('t', 'oldtemplate', '',
+     _('template to group changesets (DEPRECATED)'), _('TEMPLATE')),
+    ('T', 'template', '{author|email}',
      _('template to group changesets'), _('TEMPLATE')),
     ('f', 'dateformat', '',
      _('strftime-compatible format for grouping by date'), _('FORMAT')),
--- a/hgext/color.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/hgext/color.py	Mon Mar 02 01:20:14 2015 -0600
@@ -140,6 +140,17 @@
 either using ansi mode (or auto mode), or by using less -r (which will
 pass through all terminal control codes, not just color control
 codes).
+
+On some systems (such as MSYS in Windows), the terminal may support
+a different color mode than the pager (activated via the "pager"
+extension). It is possible to define separate modes depending on whether
+the pager is active::
+
+  [color]
+  mode = auto
+  pagermode = ansi
+
+If ``pagermode`` is not defined, the ``mode`` will be used.
 '''
 
 import os
@@ -213,11 +224,30 @@
     formatted = always or (os.environ.get('TERM') != 'dumb' and ui.formatted())
 
     mode = ui.config('color', 'mode', 'auto')
+
+    # If pager is active, color.pagermode overrides color.mode.
+    if getattr(ui, 'pageractive', False):
+        mode = ui.config('color', 'pagermode', mode)
+
     realmode = mode
     if mode == 'auto':
-        if os.name == 'nt' and 'TERM' not in os.environ:
-            # looks line a cmd.exe console, use win32 API or nothing
-            realmode = 'win32'
+        if os.name == 'nt':
+            term = os.environ.get('TERM')
+            # TERM won't be defined in a vanilla cmd.exe environment.
+            if not term:
+                realmode = 'win32'
+
+            # UNIX-like environments on Windows such as Cygwin and MSYS will
+            # set TERM. They appear to make a best effort attempt at setting it
+            # to something appropriate. However, not all environments with TERM
+            # defined support ANSI. Since "ansi" could result in terminal
+            # gibberish, we error on the side of selecting "win32". However, if
+            # w32effects is not defined, we almost certainly don't support
+            # "win32", so don't even try.
+            if 'xterm' in term or not w32effects:
+                realmode = 'ansi'
+            else:
+                realmode = 'win32'
         else:
             realmode = 'ansi'
 
--- a/hgext/histedit.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/hgext/histedit.py	Mon Mar 02 01:20:14 2015 -0600
@@ -158,6 +158,7 @@
 from mercurial import error
 from mercurial import copies
 from mercurial import context
+from mercurial import extensions
 from mercurial import hg
 from mercurial import node
 from mercurial import repair
@@ -189,13 +190,13 @@
 """)
 
 class histeditstate(object):
-    def __init__(self, repo, parentctx=None, rules=None, keep=None,
+    def __init__(self, repo, parentctxnode=None, rules=None, keep=None,
             topmost=None, replacements=None, lock=None, wlock=None):
         self.repo = repo
         self.rules = rules
         self.keep = keep
         self.topmost = topmost
-        self.parentctx = parentctx
+        self.parentctxnode = parentctxnode
         self.lock = lock
         self.wlock = wlock
         if replacements is None:
@@ -214,7 +215,7 @@
 
         parentctxnode, rules, keep, topmost, replacements = pickle.load(fp)
 
-        self.parentctx = self.repo[parentctxnode]
+        self.parentctxnode = parentctxnode
         self.rules = rules
         self.keep = keep
         self.topmost = topmost
@@ -222,7 +223,7 @@
 
     def write(self):
         fp = self.repo.vfs('histedit-state', 'w')
-        pickle.dump((self.parentctx.node(), self.rules, self.keep,
+        pickle.dump((self.parentctxnode, self.rules, self.keep,
                      self.topmost, self.replacements), fp)
         fp.close()
 
@@ -346,10 +347,11 @@
     return repo.commitctx(new)
 
 def pick(ui, state, ha, opts):
-    repo, ctx = state.repo, state.parentctx
+    repo, ctxnode = state.repo, state.parentctxnode
+    ctx = repo[ctxnode]
     oldctx = repo[ha]
     if oldctx.parents()[0] == ctx:
-        ui.debug('node %s unchanged\n' % ha)
+        ui.debug('node %s unchanged\n' % ha[:12])
         return oldctx, []
     hg.update(repo, ctx.node())
     stats = applychanges(ui, repo, oldctx, opts)
@@ -361,14 +363,15 @@
     n = commit(text=oldctx.description(), user=oldctx.user(),
                date=oldctx.date(), extra=oldctx.extra())
     if n is None:
-        ui.warn(_('%s: empty changeset\n') % node.hex(ha))
+        ui.warn(_('%s: empty changeset\n') % ha[:12])
         return ctx, []
     new = repo[n]
     return new, [(oldctx.node(), (n,))]
 
 
 def edit(ui, state, ha, opts):
-    repo, ctx = state.repo, state.parentctx
+    repo, ctxnode = state.repo, state.parentctxnode
+    ctx = repo[ctxnode]
     oldctx = repo[ha]
     hg.update(repo, ctx.node())
     applychanges(ui, repo, oldctx, opts)
@@ -382,17 +385,18 @@
     return fold(ui, state, ha, rollupopts)
 
 def fold(ui, state, ha, opts):
-    repo, ctx = state.repo, state.parentctx
+    repo, ctxnode = state.repo, state.parentctxnode
+    ctx = repo[ctxnode]
     oldctx = repo[ha]
     hg.update(repo, ctx.node())
     stats = applychanges(ui, repo, oldctx, opts)
     if stats and stats[3] > 0:
         raise error.InterventionRequired(
             _('Fix up the change and run hg histedit --continue'))
-    n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(),
+    n = repo.commit(text='fold-temp-revision %s' % ha[:12], user=oldctx.user(),
                     date=oldctx.date(), extra=oldctx.extra())
     if n is None:
-        ui.warn(_('%s: empty changeset') % node.hex(ha))
+        ui.warn(_('%s: empty changeset') % ha[:12])
         return ctx, []
     return finishfold(ui, repo, ctx, oldctx, n, opts, [])
 
@@ -438,12 +442,14 @@
     return repo[n], replacements
 
 def drop(ui, state, ha, opts):
-    repo, ctx = state.repo, state.parentctx
+    repo, ctxnode = state.repo, state.parentctxnode
+    ctx = repo[ctxnode]
     return ctx, [(repo[ha].node(), ())]
 
 
 def message(ui, state, ha, opts):
-    repo, ctx = state.repo, state.parentctx
+    repo, ctxnode = state.repo, state.parentctxnode
+    ctx = repo[ctxnode]
     oldctx = repo[ha]
     hg.update(repo, ctx.node())
     stats = applychanges(ui, repo, oldctx, opts)
@@ -503,6 +509,7 @@
     [('', 'commands', '',
       _('Read history edits from the specified file.')),
      ('c', 'continue', False, _('continue an edit already in progress')),
+     ('', 'edit-plan', False, _('edit remaining actions list')),
      ('k', 'keep', False,
       _("don't strip old nodes after edit is complete")),
      ('', 'abort', False, _('abort an edit in progress')),
@@ -552,6 +559,7 @@
     # basic argument incompatibility processing
     outg = opts.get('outgoing')
     cont = opts.get('continue')
+    editplan = opts.get('edit_plan')
     abort = opts.get('abort')
     force = opts.get('force')
     rules = opts.get('commands', '')
@@ -560,13 +568,18 @@
     if force and not outg:
         raise util.Abort(_('--force only allowed with --outgoing'))
     if cont:
-        if util.any((outg, abort, revs, freeargs, rules)):
+        if util.any((outg, abort, revs, freeargs, rules, editplan)):
             raise util.Abort(_('no arguments allowed with --continue'))
         goal = 'continue'
     elif abort:
-        if util.any((outg, revs, freeargs, rules)):
+        if util.any((outg, revs, freeargs, rules, editplan)):
             raise util.Abort(_('no arguments allowed with --abort'))
         goal = 'abort'
+    elif editplan:
+        if util.any((outg, revs, freeargs)):
+            raise util.Abort(_('only --commands argument allowed with'
+                               '--edit-plan'))
+        goal = 'edit-plan'
     else:
         if os.path.exists(os.path.join(repo.path, 'histedit-state')):
             raise util.Abort(_('history edit already in progress, try '
@@ -579,6 +592,10 @@
                     _('only one repo argument allowed with --outgoing'))
         else:
             revs.extend(freeargs)
+            if len(revs) == 0:
+                histeditdefault = ui.config('histedit', 'defaultrev')
+                if histeditdefault:
+                    revs.append(histeditdefault)
             if len(revs) != 1:
                 raise util.Abort(
                     _('histedit requires exactly one ancestor revision'))
@@ -589,17 +606,34 @@
 
     # rebuild state
     if goal == 'continue':
-        state = histeditstate(repo)
         state.read()
         state = bootstrapcontinue(ui, state, opts)
+    elif goal == 'edit-plan':
+        state = histeditstate(repo)
+        state.read()
+        if not rules:
+            comment = editcomment % (state.parentctx, node.short(state.topmost))
+            rules = ruleeditor(repo, ui, state.rules, comment)
+        else:
+            if rules == '-':
+                f = sys.stdin
+            else:
+                f = open(rules)
+            rules = f.read()
+            f.close()
+        rules = [l for l in (r.strip() for r in rules.splitlines())
+                 if l and not l.startswith('#')]
+        rules = verifyrules(rules, repo, [repo[c] for [_a, c] in state.rules])
+        state.rules = rules
+        state.write()
+        return
     elif goal == 'abort':
-        state = histeditstate(repo)
         state.read()
         mapping, tmpnodes, leafs, _ntm = processreplacement(state)
         ui.debug('restore wc to old parent %s\n' % node.short(state.topmost))
         # check whether we should update away
         parentnodes = [c.node() for c in repo[None].parents()]
-        for n in leafs | set([state.parentctx.node()]):
+        for n in leafs | set([state.parentctxnode]):
             if n in parentnodes:
                 hg.clean(repo, state.topmost)
                 break
@@ -634,16 +668,8 @@
 
         ctxs = [repo[r] for r in revs]
         if not rules:
-            rules = '\n'.join([makedesc(c) for c in ctxs])
-            rules += '\n\n'
-            rules += editcomment % (node.short(root), node.short(topmost))
-            rules = ui.edit(rules, ui.username())
-            # Save edit rules in .hg/histedit-last-edit.txt in case
-            # the user needs to ask for help after something
-            # surprising happens.
-            f = open(repo.join('histedit-last-edit.txt'), 'w')
-            f.write(rules)
-            f.close()
+            comment = editcomment % (node.short(root), node.short(topmost))
+            rules = ruleeditor(repo, ui, [['pick', c] for c in ctxs], comment)
         else:
             if rules == '-':
                 f = sys.stdin
@@ -655,9 +681,9 @@
                  if l and not l.startswith('#')]
         rules = verifyrules(rules, repo, ctxs)
 
-        parentctx = repo[root].parents()[0]
+        parentctxnode = repo[root].parents()[0].node()
 
-        state.parentctx = parentctx
+        state.parentctxnode = parentctxnode
         state.rules = rules
         state.keep = keep
         state.topmost = topmost
@@ -666,12 +692,14 @@
     while state.rules:
         state.write()
         action, ha = state.rules.pop(0)
-        ui.debug('histedit: processing %s %s\n' % (action, ha))
+        ui.debug('histedit: processing %s %s\n' % (action, ha[:12]))
         actfunc = actiontable[action]
-        state.parentctx, replacement_ = actfunc(ui, state, ha, opts)
+        parentctx, replacement_ = actfunc(ui, state, ha, opts)
+        state.parentctxnode = parentctx.node()
         state.replacements.extend(replacement_)
+    state.write()
 
-    hg.update(repo, state.parentctx.node())
+    hg.update(repo, state.parentctxnode)
 
     mapping, tmpnodes, created, ntm = processreplacement(state)
     if mapping:
@@ -724,7 +752,8 @@
     return newchildren
 
 def bootstrapcontinue(ui, state, opts):
-    repo, parentctx = state.repo, state.parentctx
+    repo, parentctxnode = state.repo, state.parentctxnode
+    parentctx = repo[parentctxnode]
     action, currentnode = state.rules.pop(0)
     ctx = repo[currentnode]
 
@@ -736,7 +765,7 @@
     if s.modified or s.added or s.removed or s.deleted:
         # prepare the message for the commit to comes
         if action in ('f', 'fold', 'r', 'roll'):
-            message = 'fold-temp-revision %s' % currentnode
+            message = 'fold-temp-revision %s' % currentnode[:12]
         else:
             message = ctx.description()
         editopt = action in ('e', 'edit', 'm', 'mess')
@@ -780,7 +809,7 @@
         # otherwise update "parentctx" before proceeding to further operation
         parentctx = repo[newchildren[-1]]
 
-    state.parentctx = parentctx
+    state.parentctxnode = parentctx.node()
     state.replacements.extend(replacements)
 
     return state
@@ -801,20 +830,40 @@
             raise util.Abort(_('cannot edit immutable changeset: %s') % root)
     return [c.node() for c in ctxs]
 
-def makedesc(c):
-    """build a initial action line for a ctx `c`
+def makedesc(repo, action, rev):
+    """build a initial action line for a ctx
 
     line are in the form:
 
-      pick <hash> <rev> <summary>
+      <action> <hash> <rev> <summary>
     """
+    ctx = repo[rev]
     summary = ''
-    if c.description():
-        summary = c.description().splitlines()[0]
-    line = 'pick %s %d %s' % (c, c.rev(), summary)
+    if ctx.description():
+        summary = ctx.description().splitlines()[0]
+    line = '%s %s %d %s' % (action, ctx, ctx.rev(), summary)
     # trim to 80 columns so it's not stupidly wide in my editor
     return util.ellipsis(line, 80)
 
+def ruleeditor(repo, ui, rules, editcomment=""):
+    """open an editor to edit rules
+
+    rules are in the format [ [act, ctx], ...] like in state.rules
+    """
+    rules = '\n'.join([makedesc(repo, act, rev) for [act, rev] in rules])
+    rules += '\n\n'
+    rules += editcomment
+    rules = ui.edit(rules, ui.username())
+
+    # Save edit rules in .hg/histedit-last-edit.txt in case
+    # the user needs to ask for help after something
+    # surprising happens.
+    f = open(repo.join('histedit-last-edit.txt'), 'w')
+    f.write(rules)
+    f.close()
+
+    return rules
+
 def verifyrules(rules, repo, ctxs):
     """Verify that there exists exactly one edit rule per given changeset.
 
@@ -822,7 +871,7 @@
     or a rule on a changeset outside of the user-given range.
     """
     parsed = []
-    expected = set(str(c) for c in ctxs)
+    expected = set(c.hex() for c in ctxs)
     seen = set()
     for r in rules:
         if ' ' not in r:
@@ -830,22 +879,24 @@
         action, rest = r.split(' ', 1)
         ha = rest.strip().split(' ', 1)[0]
         try:
-            ha = str(repo[ha])  # ensure its a short hash
+            ha = repo[ha].hex()
         except error.RepoError:
-            raise util.Abort(_('unknown changeset %s listed') % ha)
+            raise util.Abort(_('unknown changeset %s listed') % ha[:12])
         if ha not in expected:
             raise util.Abort(
                 _('may not use changesets other than the ones listed'))
         if ha in seen:
-            raise util.Abort(_('duplicated command for changeset %s') % ha)
+            raise util.Abort(_('duplicated command for changeset %s') %
+                    ha[:12])
         seen.add(ha)
         if action not in actiontable:
             raise util.Abort(_('unknown action "%s"') % action)
         parsed.append([action, ha])
     missing = sorted(expected - seen)  # sort to stabilize output
     if missing:
-        raise util.Abort(_('missing rules for changeset %s') % missing[0],
-                         hint=_('do you want to use the drop action?'))
+        raise util.Abort(_('missing rules for changeset %s') %
+                missing[0][:12],
+                hint=_('do you want to use the drop action?'))
     return parsed
 
 def processreplacement(state):
@@ -965,6 +1016,23 @@
     finally:
         release(lock)
 
+def stripwrapper(orig, ui, repo, nodelist, *args, **kwargs):
+    if isinstance(nodelist, str):
+        nodelist = [nodelist]
+    if os.path.exists(os.path.join(repo.path, 'histedit-state')):
+        state = histeditstate(repo)
+        state.read()
+        histedit_nodes = set([ctx for (action, ctx) in state.rules])
+        strip_nodes = set([repo[n].hex() for n in nodelist])
+        common_nodes = histedit_nodes & strip_nodes
+        if common_nodes:
+            raise util.Abort(_('unable to strip %s. Nodes are '
+                               'used by history edit in progress.')
+                             % ', '.join(common_nodes))
+    return orig(ui, repo, nodelist, *args, **kwargs)
+
+extensions.wrapfunction(repair, 'strip', stripwrapper)
+
 def summaryhook(ui, repo):
     if not os.path.exists(repo.join('histedit-state')):
         return
--- a/hgext/largefiles/overrides.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/hgext/largefiles/overrides.py	Mon Mar 02 01:20:14 2015 -0600
@@ -559,16 +559,6 @@
         # this isn't legal, let the original function deal with it
         return orig(ui, repo, pats, opts, rename)
 
-    def makestandin(relpath):
-        path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
-        return os.path.join(repo.wjoin(lfutil.standin(path)))
-
-    fullpats = scmutil.expandpats(pats)
-    dest = fullpats[-1]
-
-    if os.path.isdir(dest):
-        if not os.path.isdir(makestandin(dest)):
-            os.makedirs(makestandin(dest))
     # This could copy both lfiles and normal files in one command,
     # but we don't want to do that. First replace their matcher to
     # only match normal files and run it, then replace it to just
@@ -595,6 +585,17 @@
     except OSError:
         return result
 
+    def makestandin(relpath):
+        path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
+        return os.path.join(repo.wjoin(lfutil.standin(path)))
+
+    fullpats = scmutil.expandpats(pats)
+    dest = fullpats[-1]
+
+    if os.path.isdir(dest):
+        if not os.path.isdir(makestandin(dest)):
+            os.makedirs(makestandin(dest))
+
     try:
         try:
             # When we call orig below it creates the standins but we don't add
@@ -715,10 +716,17 @@
                 default='relpath'):
             match = oldmatch(ctx, pats, opts, globbed, default)
             m = copy.copy(match)
+
+            # revert supports recursing into subrepos, and though largefiles
+            # currently doesn't work correctly in that case, this match is
+            # called, so the lfdirstate above may not be the correct one for
+            # this invocation of match.
+            lfdirstate = lfutil.openlfdirstate(ctx._repo.ui, ctx._repo)
+
             def tostandin(f):
                 if lfutil.standin(f) in ctx:
                     return lfutil.standin(f)
-                elif lfutil.standin(f) in repo[None]:
+                elif lfutil.standin(f) in repo[None] or lfdirstate[f] == 'r':
                     return None
                 return f
             m._files = [tostandin(f) for f in m._files]
@@ -820,6 +828,14 @@
         sourcerepo, destrepo = result
         repo = destrepo.local()
 
+        # If largefiles is required for this repo, permanently enable it locally
+        if 'largefiles' in repo.requirements:
+            fp = repo.vfs('hgrc', 'a', text=True)
+            try:
+                fp.write('\n[extensions]\nlargefiles=\n')
+            finally:
+                fp.close()
+
         # Caching is implicitly limited to 'rev' option, since the dest repo was
         # truncated at that point.  The user may expect a download count with
         # this option, so attempt whether or not this is a largefile repo.
--- a/hgext/largefiles/reposetup.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/hgext/largefiles/reposetup.py	Mon Mar 02 01:20:14 2015 -0600
@@ -329,10 +329,10 @@
                             actualfiles.append(lf)
                             if not matcheddir:
                                 # There may still be normal files in the dir, so
-                                # make sure a directory is in the list, which
-                                # forces status to walk and call the match
-                                # function on the matcher.  Windows does NOT
-                                # require this.
+                                # make sure _a_ directory is in the list, which
+                                # forces status/dirstate to walk all files and
+                                # call the match function on the matcher, even
+                                # on case sensitive filesytems.
                                 actualfiles.append('.')
                                 matcheddir = True
                 # Nothing in dir, so readd it
--- a/hgext/pager.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/hgext/pager.py	Mon Mar 02 01:20:14 2015 -0600
@@ -149,6 +149,8 @@
                     usepager = True
                     break
 
+        setattr(ui, 'pageractive', usepager)
+
         if usepager:
             ui.setconfig('ui', 'formatted', ui.formatted(), 'pager')
             ui.setconfig('ui', 'interactive', False, 'pager')
@@ -157,7 +159,12 @@
             _runpager(ui, p)
         return orig(ui, options, cmd, cmdfunc)
 
-    extensions.wrapfunction(dispatch, '_runcommand', pagecmd)
+    # Wrap dispatch._runcommand after color is loaded so color can see
+    # ui.pageractive. Otherwise, if we loaded first, color's wrapped
+    # dispatch._runcommand would run without having access to ui.pageractive.
+    def afterloaded(loaded):
+        extensions.wrapfunction(dispatch, '_runcommand', pagecmd)
+    extensions.afterloaded('color', afterloaded)
 
 def extsetup(ui):
     commands.globalopts.append(
--- a/mercurial/branchmap.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/branchmap.py	Mon Mar 02 01:20:14 2015 -0600
@@ -411,9 +411,6 @@
             try:
                 if self._rbcnamescount != 0:
                     f = repo.vfs.open(_rbcnames, 'ab')
-                    # The position after open(x, 'a') is implementation defined-
-                    # see issue3543.  SEEK_END was added in 2.5
-                    f.seek(0, 2) #os.SEEK_END
                     if f.tell() == self._rbcsnameslen:
                         f.write('\0')
                     else:
@@ -438,9 +435,6 @@
             revs = min(len(repo.changelog), len(self._rbcrevs) // _rbcrecsize)
             try:
                 f = repo.vfs.open(_rbcrevs, 'ab')
-                # The position after open(x, 'a') is implementation defined-
-                # see issue3543.  SEEK_END was added in 2.5
-                f.seek(0, 2) #os.SEEK_END
                 if f.tell() != start:
                     repo.ui.debug("truncating %s to %s\n" % (_rbcrevs, start))
                     f.seek(start)
--- a/mercurial/bundle2.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/bundle2.py	Mon Mar 02 01:20:14 2015 -0600
@@ -145,6 +145,7 @@
 preserve.
 """
 
+import errno
 import sys
 import util
 import struct
@@ -312,7 +313,7 @@
     except Exception, exc:
         for part in iterparts:
             # consume the bundle content
-            part.read()
+            part.seek(0, 2)
         # Small hack to let caller code distinguish exceptions from bundle2
         # processing from processing the old format. This is mostly
         # needed to handle different return codes to unbundle according to the
@@ -364,7 +365,7 @@
             outpart.addparam('in-reply-to', str(part.id), mandatory=False)
     finally:
         # consume the part content to not corrupt the stream.
-        part.read()
+        part.seek(0, 2)
 
 
 def decodecaps(blob):
@@ -484,6 +485,8 @@
 
     def __init__(self, fp):
         self._fp = fp
+        self._seekable = (util.safehasattr(fp, 'seek') and
+                          util.safehasattr(fp, 'tell'))
 
     def _unpack(self, format):
         """unpack this struct format from the stream"""
@@ -494,6 +497,29 @@
         """read exactly <size> bytes from the stream"""
         return changegroup.readexactly(self._fp, size)
 
+    def seek(self, offset, whence=0):
+        """move the underlying file pointer"""
+        if self._seekable:
+            return self._fp.seek(offset, whence)
+        else:
+            raise NotImplementedError(_('File pointer is not seekable'))
+
+    def tell(self):
+        """return the file offset, or None if file is not seekable"""
+        if self._seekable:
+            try:
+                return self._fp.tell()
+            except IOError, e:
+                if e.errno == errno.ESPIPE:
+                    self._seekable = False
+                else:
+                    raise
+        return None
+
+    def close(self):
+        """close underlying file"""
+        if util.safehasattr(self._fp, 'close'):
+            return self._fp.close()
 
 class unbundle20(unpackermixin):
     """interpret a bundle2 stream
@@ -564,6 +590,7 @@
         while headerblock is not None:
             part = unbundlepart(self.ui, headerblock, self._fp)
             yield part
+            part.seek(0, 2)
             headerblock = self._readpartheader()
         self.ui.debug('end of bundle2 stream\n')
 
@@ -580,6 +607,8 @@
             return self._readexact(headersize)
         return None
 
+    def compressed(self):
+        return False
 
 class bundlepart(object):
     """A bundle2 part contains application level payload
@@ -801,6 +830,8 @@
         self._payloadstream = None
         self._readheader()
         self._mandatory = None
+        self._chunkindex = [] #(payload, file) position tuples for chunk starts
+        self._pos = 0
 
     def _fromheader(self, size):
         """return the next <size> byte from the header"""
@@ -826,6 +857,47 @@
         self.params.update(dict(self.advisoryparams))
         self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
 
+    def _payloadchunks(self, chunknum=0):
+        '''seek to specified chunk and start yielding data'''
+        if len(self._chunkindex) == 0:
+            assert chunknum == 0, 'Must start with chunk 0'
+            self._chunkindex.append((0, super(unbundlepart, self).tell()))
+        else:
+            assert chunknum < len(self._chunkindex), \
+                   'Unknown chunk %d' % chunknum
+            super(unbundlepart, self).seek(self._chunkindex[chunknum][1])
+
+        pos = self._chunkindex[chunknum][0]
+        payloadsize = self._unpack(_fpayloadsize)[0]
+        self.ui.debug('payload chunk size: %i\n' % payloadsize)
+        while payloadsize:
+            if payloadsize == flaginterrupt:
+                # interruption detection, the handler will now read a
+                # single part and process it.
+                interrupthandler(self.ui, self._fp)()
+            elif payloadsize < 0:
+                msg = 'negative payload chunk size: %i' %  payloadsize
+                raise error.BundleValueError(msg)
+            else:
+                result = self._readexact(payloadsize)
+                chunknum += 1
+                pos += payloadsize
+                if chunknum == len(self._chunkindex):
+                    self._chunkindex.append((pos,
+                                             super(unbundlepart, self).tell()))
+                yield result
+            payloadsize = self._unpack(_fpayloadsize)[0]
+            self.ui.debug('payload chunk size: %i\n' % payloadsize)
+
+    def _findchunk(self, pos):
+        '''for a given payload position, return a chunk number and offset'''
+        for chunk, (ppos, fpos) in enumerate(self._chunkindex):
+            if ppos == pos:
+                return chunk, 0
+            elif ppos > pos:
+                return chunk - 1, pos - self._chunkindex[chunk - 1][0]
+        raise ValueError('Unknown chunk')
+
     def _readheader(self):
         """read the header and setup the object"""
         typesize = self._unpackheader(_fparttypesize)[0]
@@ -857,22 +929,7 @@
             advparams.append((self._fromheader(key), self._fromheader(value)))
         self._initparams(manparams, advparams)
         ## part payload
-        def payloadchunks():
-            payloadsize = self._unpack(_fpayloadsize)[0]
-            self.ui.debug('payload chunk size: %i\n' % payloadsize)
-            while payloadsize:
-                if payloadsize == flaginterrupt:
-                    # interruption detection, the handler will now read a
-                    # single part and process it.
-                    interrupthandler(self.ui, self._fp)()
-                elif payloadsize < 0:
-                    msg = 'negative payload chunk size: %i' %  payloadsize
-                    raise error.BundleValueError(msg)
-                else:
-                    yield self._readexact(payloadsize)
-                payloadsize = self._unpack(_fpayloadsize)[0]
-                self.ui.debug('payload chunk size: %i\n' % payloadsize)
-        self._payloadstream = util.chunkbuffer(payloadchunks())
+        self._payloadstream = util.chunkbuffer(self._payloadchunks())
         # we read the data, tell it
         self._initialized = True
 
@@ -886,8 +943,37 @@
             data = self._payloadstream.read(size)
         if size is None or len(data) < size:
             self.consumed = True
+        self._pos += len(data)
         return data
 
+    def tell(self):
+        return self._pos
+
+    def seek(self, offset, whence=0):
+        if whence == 0:
+            newpos = offset
+        elif whence == 1:
+            newpos = self._pos + offset
+        elif whence == 2:
+            if not self.consumed:
+                self.read()
+            newpos = self._chunkindex[-1][0] - offset
+        else:
+            raise ValueError('Unknown whence value: %r' % (whence,))
+
+        if newpos > self._chunkindex[-1][0] and not self.consumed:
+            self.read()
+        if not 0 <= newpos <= self._chunkindex[-1][0]:
+            raise ValueError('Offset out of range')
+
+        if self._pos != newpos:
+            chunk, internaloffset = self._findchunk(newpos)
+            self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
+            adjust = self.read(internaloffset)
+            if len(adjust) != internaloffset:
+                raise util.Abort(_('Seek failed\n'))
+            self._pos = newpos
+
 capabilities = {'HG2Y': (),
                 'b2x:listkeys': (),
                 'b2x:pushkey': (),
--- a/mercurial/bundlerepo.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/bundlerepo.py	Mon Mar 02 01:20:14 2015 -0600
@@ -15,7 +15,7 @@
 from i18n import _
 import os, tempfile, shutil
 import changegroup, util, mdiff, discovery, cmdutil, scmutil, exchange
-import localrepo, changelog, manifest, filelog, revlog, error, phases
+import localrepo, changelog, manifest, filelog, revlog, error, phases, bundle2
 
 class bundlerevlog(revlog.revlog):
     def __init__(self, opener, indexfile, bundle, linkmapper):
@@ -177,9 +177,6 @@
     def baserevision(self, nodeorrev):
         return filelog.filelog.revision(self, nodeorrev)
 
-    def _file(self, f):
-        self._repo.file(f)
-
 class bundlepeer(localrepo.localpeer):
     def canpush(self):
         return False
@@ -219,7 +216,7 @@
 
         self.tempfile = None
         f = util.posixfile(bundlename, "rb")
-        self.bundle = exchange.readbundle(ui, f, bundlename)
+        self.bundlefile = self.bundle = exchange.readbundle(ui, f, bundlename)
         if self.bundle.compressed():
             fdtemp, temp = self.vfs.mkstemp(prefix="hg-bundle-",
                                             suffix=".hg10un")
@@ -237,7 +234,27 @@
                 fptemp.close()
 
             f = self.vfs.open(self.tempfile, mode="rb")
-            self.bundle = exchange.readbundle(ui, f, bundlename, self.vfs)
+            self.bundlefile = self.bundle = exchange.readbundle(ui, f,
+                                                                bundlename,
+                                                                self.vfs)
+
+        if isinstance(self.bundle, bundle2.unbundle20):
+            cgparts = [part for part in self.bundle.iterparts()
+                       if (part.type == 'b2x:changegroup')
+                       and (part.params.get('version', '01')
+                            in changegroup.packermap)]
+
+            if not cgparts:
+                raise util.Abort('No changegroups found')
+            version = cgparts[0].params.get('version', '01')
+            cgparts = [p for p in cgparts
+                       if p.params.get('version', '01') == version]
+            if len(cgparts) > 1:
+                raise NotImplementedError("Can't process multiple changegroups")
+            part = cgparts[0]
+
+            part.seek(0)
+            self.bundle = changegroup.packermap[version][1](part, 'UN')
 
         # dict with the mapping 'filename' -> position in the bundle
         self.bundlefilespos = {}
@@ -303,7 +320,7 @@
 
     def close(self):
         """Close assigned bundle file immediately."""
-        self.bundle.close()
+        self.bundlefile.close()
         if self.tempfile is not None:
             self.vfs.unlink(self.tempfile)
         if self._tempparent:
--- a/mercurial/changegroup.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/changegroup.py	Mon Mar 02 01:20:14 2015 -0600
@@ -659,8 +659,11 @@
         pr()
         fl = repo.file(f)
         o = len(fl)
-        if not fl.addgroup(source, revmap, trp):
-            raise util.Abort(_("received file revlog group is empty"))
+        try:
+            if not fl.addgroup(source, revmap, trp):
+                raise util.Abort(_("received file revlog group is empty"))
+        except error.CensoredBaseError, e:
+            raise util.Abort(_("received delta base is censored: %s") % e)
         revisions += len(fl) - o
         files += 1
         if f in needfiles:
--- a/mercurial/changelog.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/changelog.py	Mon Mar 02 01:20:14 2015 -0600
@@ -143,6 +143,11 @@
             if i not in self.filteredrevs:
                 return self.node(i)
 
+    def __contains__(self, rev):
+        """filtered version of revlog.__contains__"""
+        return (revlog.revlog.__contains__(self, rev)
+                and rev not in self.filteredrevs)
+
     def __iter__(self):
         """filtered version of revlog.__iter__"""
         if len(self.filteredrevs) == 0:
--- a/mercurial/cmdutil.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/cmdutil.py	Mon Mar 02 01:20:14 2015 -0600
@@ -1473,14 +1473,7 @@
     function on each context in the window in forward order.'''
 
     follow = opts.get('follow') or opts.get('follow_first')
-
-    if opts.get('rev'):
-        revs = scmutil.revrange(repo, opts.get('rev'))
-    elif follow:
-        revs = repo.revs('reverse(:.)')
-    else:
-        revs = revset.spanset(repo)
-        revs.reverse()
+    revs = _logrevs(repo, opts)
     if not revs:
         return []
     wanted = set()
@@ -1822,6 +1815,21 @@
         expr = None
     return expr, filematcher
 
+def _logrevs(repo, opts):
+    # Default --rev value depends on --follow but --follow behaviour
+    # depends on revisions resolved from --rev...
+    follow = opts.get('follow') or opts.get('follow_first')
+    if opts.get('rev'):
+        revs = scmutil.revrange(repo, opts['rev'])
+    elif follow and repo.dirstate.p1() == nullid:
+        revs = revset.baseset()
+    elif follow:
+        revs = repo.revs('reverse(:.)')
+    else:
+        revs = revset.spanset(repo)
+        revs.reverse()
+    return revs
+
 def getgraphlogrevs(repo, pats, opts):
     """Return (revs, expr, filematcher) where revs is an iterable of
     revision numbers, expr is a revset string built from log options
@@ -1830,28 +1838,14 @@
     callable taking a revision number and returning a match objects
     filtering the files to be detailed when displaying the revision.
     """
-    if not len(repo):
-        return [], None, None
     limit = loglimit(opts)
-    # Default --rev value depends on --follow but --follow behaviour
-    # depends on revisions resolved from --rev...
-    follow = opts.get('follow') or opts.get('follow_first')
-    possiblyunsorted = False # whether revs might need sorting
-    if opts.get('rev'):
-        revs = scmutil.revrange(repo, opts['rev'])
-        # Don't sort here because _makelogrevset might depend on the
-        # order of revs
-        possiblyunsorted = True
-    else:
-        if follow and len(repo) > 0:
-            revs = repo.revs('reverse(:.)')
-        else:
-            revs = revset.spanset(repo)
-            revs.reverse()
+    revs = _logrevs(repo, opts)
     if not revs:
         return revset.baseset(), None, None
     expr, filematcher = _makelogrevset(repo, pats, opts, revs)
-    if possiblyunsorted:
+    if opts.get('rev'):
+        # User-specified revs might be unsorted, but don't sort before
+        # _makelogrevset because it might depend on the order of revs
         revs.sort(reverse=True)
     if expr:
         # Revset matchers often operate faster on revisions in changelog
@@ -1882,16 +1876,7 @@
     filtering the files to be detailed when displaying the revision.
     """
     limit = loglimit(opts)
-    # Default --rev value depends on --follow but --follow behaviour
-    # depends on revisions resolved from --rev...
-    follow = opts.get('follow') or opts.get('follow_first')
-    if opts.get('rev'):
-        revs = scmutil.revrange(repo, opts['rev'])
-    elif follow:
-        revs = repo.revs('reverse(:.)')
-    else:
-        revs = revset.spanset(repo)
-        revs.reverse()
+    revs = _logrevs(repo, opts)
     if not revs:
         return revset.baseset([]), None, None
     expr, filematcher = _makelogrevset(repo, pats, opts, revs)
@@ -2798,14 +2783,14 @@
 
             _performrevert(repo, parents, ctx, actions)
 
-            # get the list of subrepos that must be reverted
-            subrepomatch = scmutil.match(ctx, pats, opts)
-            targetsubs = sorted(s for s in ctx.substate if subrepomatch(s))
-
-            if targetsubs:
-                # Revert the subrepos on the revert list
-                for sub in targetsubs:
-                    ctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
+        # get the list of subrepos that must be reverted
+        subrepomatch = scmutil.match(ctx, pats, opts)
+        targetsubs = sorted(s for s in ctx.substate if subrepomatch(s))
+
+        if targetsubs:
+            # Revert the subrepos on the revert list
+            for sub in targetsubs:
+                ctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
     finally:
         wlock.release()
 
--- a/mercurial/commands.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/commands.py	Mon Mar 02 01:20:14 2015 -0600
@@ -2885,7 +2885,7 @@
             weight, optimizedtree = revset.optimize(newtree, True)
             ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
     func = revset.match(ui, expr)
-    for c in func(repo, revset.spanset(repo)):
+    for c in func(repo):
         ui.write("%s\n" % c)
 
 @command('debugsetparents', [], _('REV1 [REV2]'))
@@ -4984,9 +4984,9 @@
     Returns 0 on success, 1 if an update had unresolved files.
     """
     source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
+    ui.status(_('pulling from %s\n') % util.hidepassword(source))
     other = hg.peer(repo, opts, source)
     try:
-        ui.status(_('pulling from %s\n') % util.hidepassword(source))
         revs, checkout = hg.addbranchrevs(repo, other, branches,
                                           opts.get('rev'))
 
@@ -5225,7 +5225,7 @@
     ('m', 'mark', None, _('mark files as resolved')),
     ('u', 'unmark', None, _('mark files as unresolved')),
     ('n', 'no-status', None, _('hide status prefix'))]
-    + mergetoolopts + walkopts,
+    + mergetoolopts + walkopts + formatteropts,
     _('[OPTION]... [FILE]...'),
     inferrepo=True)
 def resolve(ui, repo, *pats, **opts):
@@ -5277,11 +5277,25 @@
         raise util.Abort(_('no files or directories specified'),
                          hint=('use --all to remerge all files'))
 
+    if show:
+        fm = ui.formatter('resolve', opts)
+        ms = mergemod.mergestate(repo)
+        m = scmutil.match(repo[None], pats, opts)
+        for f in ms:
+            if not m(f):
+                continue
+            l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved'}[ms[f]]
+            fm.startitem()
+            fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
+            fm.write('path', '%s\n', f, label=l)
+        fm.end()
+        return 0
+
     wlock = repo.wlock()
     try:
         ms = mergemod.mergestate(repo)
 
-        if not (ms.active() or repo.dirstate.p2() != nullid) and not show:
+        if not (ms.active() or repo.dirstate.p2() != nullid):
             raise util.Abort(
                 _('resolve command not applicable when not merging'))
 
@@ -5295,14 +5309,7 @@
 
             didwork = True
 
-            if show:
-                if nostatus:
-                    ui.write("%s\n" % f)
-                else:
-                    ui.write("%s %s\n" % (ms[f].upper(), f),
-                             label='resolve.' +
-                             {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
-            elif mark:
+            if mark:
                 ms.mark(f, "r")
             elif unmark:
                 ms.mark(f, "u")
@@ -5334,10 +5341,8 @@
     finally:
         wlock.release()
 
-    # Nudge users into finishing an unfinished operation. We don't print
-    # this with the list/show operation because we want list/show to remain
-    # machine readable.
-    if not list(ms.unresolved()) and not show:
+    # Nudge users into finishing an unfinished operation
+    if not list(ms.unresolved()):
         ui.status(_('(no more unresolved files)\n'))
 
     return ret
--- a/mercurial/context.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/context.py	Mon Mar 02 01:20:14 2015 -0600
@@ -376,10 +376,6 @@
                 return
             if isinstance(changeid, long):
                 changeid = str(changeid)
-            if changeid == '.':
-                self._node = repo.dirstate.p1()
-                self._rev = repo.changelog.rev(self._node)
-                return
             if changeid == 'null':
                 self._node = nullid
                 self._rev = nullrev
@@ -388,6 +384,12 @@
                 self._node = repo.changelog.tip()
                 self._rev = repo.changelog.rev(self._node)
                 return
+            if changeid == '.' or changeid == repo.dirstate.p1():
+                # this is a hack to delay/avoid loading obsmarkers
+                # when we know that '.' won't be hidden
+                self._node = repo.dirstate.p1()
+                self._rev = repo.unfiltered().changelog.rev(self._node)
+                return
             if len(changeid) == 20:
                 try:
                     self._node = changeid
--- a/mercurial/copies.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/copies.py	Mon Mar 02 01:20:14 2015 -0600
@@ -144,6 +144,15 @@
             del c[k]
     return c
 
+def _computeforwardmissing(a, b):
+    """Computes which files are in b but not a.
+    This is its own function so extensions can easily wrap this call to see what
+    files _forwardcopies is about to process.
+    """
+    missing = set(b.manifest().iterkeys())
+    missing.difference_update(a.manifest().iterkeys())
+    return missing
+
 def _forwardcopies(a, b):
     '''find {dst@b: src@a} copy mapping where a is an ancestor of b'''
 
@@ -167,9 +176,7 @@
     # we currently don't try to find where old files went, too expensive
     # this means we can miss a case like 'hg rm b; hg cp a b'
     cm = {}
-    missing = set(b.manifest().iterkeys())
-    missing.difference_update(a.manifest().iterkeys())
-
+    missing = _computeforwardmissing(a, b)
     ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
     for f in missing:
         fctx = b[f]
@@ -208,6 +215,22 @@
         return _backwardrenames(x, y)
     return _chain(x, y, _backwardrenames(x, a), _forwardcopies(a, y))
 
+def _computenonoverlap(repo, m1, m2, ma):
+    """Computes the files exclusive to m1 and m2.
+    This is its own function so extensions can easily wrap this call to see what
+    files mergecopies is about to process.
+    """
+    u1 = _nonoverlap(m1, m2, ma)
+    u2 = _nonoverlap(m2, m1, ma)
+
+    if u1:
+        repo.ui.debug("  unmatched files in local:\n   %s\n"
+                      % "\n   ".join(u1))
+    if u2:
+        repo.ui.debug("  unmatched files in other:\n   %s\n"
+                      % "\n   ".join(u2))
+    return u1, u2
+
 def mergecopies(repo, c1, c2, ca):
     """
     Find moves and copies between context c1 and c2 that are relevant
@@ -261,15 +284,7 @@
 
     repo.ui.debug("  searching for copies back to rev %d\n" % limit)
 
-    u1 = _nonoverlap(m1, m2, ma)
-    u2 = _nonoverlap(m2, m1, ma)
-
-    if u1:
-        repo.ui.debug("  unmatched files in local:\n   %s\n"
-                      % "\n   ".join(u1))
-    if u2:
-        repo.ui.debug("  unmatched files in other:\n   %s\n"
-                      % "\n   ".join(u2))
+    u1, u2 = _computenonoverlap(repo, m1, m2, ma)
 
     for f in u1:
         checkcopies(ctx, f, m1, m2, ca, limit, diverge, copy, fullcopy)
--- a/mercurial/dispatch.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/dispatch.py	Mon Mar 02 01:20:14 2015 -0600
@@ -27,6 +27,15 @@
     "run the command in sys.argv"
     sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
 
+def _formatparse(write, inst):
+    if len(inst.args) > 1:
+        write(_("hg: parse error at %s: %s\n") %
+                         (inst.args[1], inst.args[0]))
+        if (inst.args[0][0] == ' '):
+            write(_("unexpected leading whitespace\n"))
+    else:
+        write(_("hg: parse error: %s\n") % inst.args[0])
+
 def dispatch(req):
     "run the command specified in req.args"
     if req.ferr:
@@ -55,13 +64,7 @@
             ferr.write(_("(%s)\n") % inst.hint)
         return -1
     except error.ParseError, inst:
-        if len(inst.args) > 1:
-            ferr.write(_("hg: parse error at %s: %s\n") %
-                             (inst.args[1], inst.args[0]))
-            if (inst.args[0][0] == ' '):
-                ferr.write(_("unexpected leading whitespace\n"))
-        else:
-            ferr.write(_("hg: parse error: %s\n") % inst.args[0])
+        _formatparse(ferr.write, inst)
         return -1
 
     msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
@@ -154,13 +157,7 @@
         ui.warn(_("hg: command '%s' is ambiguous:\n    %s\n") %
                 (inst.args[0], " ".join(inst.args[1])))
     except error.ParseError, inst:
-        if len(inst.args) > 1:
-            ui.warn(_("hg: parse error at %s: %s\n") %
-                             (inst.args[1], inst.args[0]))
-            if (inst.args[0][0] == ' '):
-                ui.warn(_("unexpected leading whitespace\n"))
-        else:
-            ui.warn(_("hg: parse error: %s\n") % inst.args[0])
+        _formatparse(ui.warn, inst)
         return -1
     except error.LockHeld, inst:
         if inst.errno == errno.ETIMEDOUT:
--- a/mercurial/error.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/error.py	Mon Mar 02 01:20:14 2015 -0600
@@ -22,6 +22,10 @@
 class LookupError(RevlogError, KeyError):
     def __init__(self, name, index, message):
         self.name = name
+        self.index = index
+        # this can't be called 'message' because at least some installs of
+        # Python 2.6+ complain about the 'message' property being deprecated
+        self.lookupmessage = message
         if isinstance(name, str) and len(name) == 20:
             from node import short
             name = short(name)
@@ -61,7 +65,7 @@
     """Exception raised when a remote repo reports failure"""
 
 class ParseError(Exception):
-    """Exception raised when parsing config files (msg[, pos])"""
+    """Raised when parsing config files and {rev,file}sets (msg[, pos])"""
 
 class RepoError(Exception):
     def __init__(self, *args, **kw):
@@ -139,3 +143,11 @@
     def __init__(self, filename, node):
         from node import short
         RevlogError.__init__(self, '%s:%s' % (filename, short(node)))
+
+class CensoredBaseError(RevlogError):
+    """error raised when a delta is rejected because its base is censored
+
+    A delta based on a censored revision must be formed as single patch
+    operation which replaces the entire base with new content. This ensures
+    the delta may be applied by clones which have not censored the base.
+    """
--- a/mercurial/extensions.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/extensions.py	Mon Mar 02 01:20:14 2015 -0600
@@ -10,6 +10,7 @@
 from i18n import _, gettext
 
 _extensions = {}
+_aftercallbacks = {}
 _order = []
 _ignore = ['hbisect', 'bookmarks', 'parentrevspec', 'interhg', 'inotify']
 
@@ -87,6 +88,8 @@
             mod = importh(name)
     _extensions[shortname] = mod
     _order.append(shortname)
+    for fn in _aftercallbacks.get(shortname, []):
+        fn(loaded=True)
     return mod
 
 def loadall(ui):
@@ -123,7 +126,33 @@
                     raise
                 extsetup() # old extsetup with no ui argument
 
-def wrapcommand(table, command, wrapper):
+    # Call aftercallbacks that were never met.
+    for shortname in _aftercallbacks:
+        if shortname in _extensions:
+            continue
+
+        for fn in _aftercallbacks[shortname]:
+            fn(loaded=False)
+
+def afterloaded(extension, callback):
+    '''Run the specified function after a named extension is loaded.
+
+    If the named extension is already loaded, the callback will be called
+    immediately.
+
+    If the named extension never loads, the callback will be called after
+    all extensions have been loaded.
+
+    The callback receives the named argument ``loaded``, which is a boolean
+    indicating whether the dependent extension actually loaded.
+    '''
+
+    if extension in _extensions:
+        callback(loaded=True)
+    else:
+        _aftercallbacks.setdefault(extension, []).append(callback)
+
+def wrapcommand(table, command, wrapper, synopsis=None, docstring=None):
     '''Wrap the command named `command' in table
 
     Replace command in the command table with wrapper. The wrapped command will
@@ -135,6 +164,22 @@
 
     where orig is the original (wrapped) function, and *args, **kwargs
     are the arguments passed to it.
+
+    Optionally append to the command synopsis and docstring, used for help.
+    For example, if your extension wraps the ``bookmarks`` command to add the
+    flags ``--remote`` and ``--all`` you might call this function like so:
+
+      synopsis = ' [-a] [--remote]'
+      docstring = """
+
+      The ``remotenames`` extension adds the ``--remote`` and ``--all`` (``-a``)
+      flags to the bookmarks command. Either flag will show the remote bookmarks
+      known to the repository; ``--remote`` will also supress the output of the
+      local bookmarks.
+      """
+
+      extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
+                             synopsis, docstring)
     '''
     assert callable(wrapper)
     aliases, entry = cmdutil.findcmd(command, table)
@@ -148,11 +193,17 @@
         return util.checksignature(wrapper)(
             util.checksignature(origfn), *args, **kwargs)
 
-    wrap.__doc__ = getattr(origfn, '__doc__')
     wrap.__module__ = getattr(origfn, '__module__')
 
+    doc = getattr(origfn, '__doc__')
+    if docstring is not None:
+        doc += docstring
+    wrap.__doc__ = doc
+
     newentry = list(entry)
     newentry[0] = wrap
+    if synopsis is not None:
+        newentry[2] += synopsis
     table[key] = tuple(newentry)
     return entry
 
--- a/mercurial/filelog.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/filelog.py	Mon Mar 02 01:20:14 2015 -0600
@@ -29,7 +29,7 @@
 
 def _censoredtext(text):
     m, offs = parsemeta(text)
-    return m and "censored" in m and not text[offs:]
+    return m and "censored" in m
 
 class filelog(revlog.revlog):
     def __init__(self, opener, path):
@@ -64,7 +64,7 @@
         node = self.node(rev)
         if self.renamed(node):
             return len(self.read(node))
-        if self._iscensored(rev):
+        if self.iscensored(rev):
             return 0
 
         # XXX if self.read(node).startswith("\1\n"), this returns (size+4)
@@ -85,7 +85,7 @@
             return False
 
         # censored files compare against the empty file
-        if self._iscensored(self.rev(node)):
+        if self.iscensored(self.rev(node)):
             return text != ''
 
         # renaming a file produces a different hash, even if the data
@@ -104,9 +104,6 @@
                 raise error.CensoredNodeError(self.indexfile, node)
             raise
 
-    def _file(self, f):
-        return filelog(self.opener, f)
-
-    def _iscensored(self, rev):
+    def iscensored(self, rev):
         """Check if a file revision is censored."""
         return self.flags(rev) & revlog.REVIDX_ISCENSORED
--- a/mercurial/filemerge.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/filemerge.py	Mon Mar 02 01:20:14 2015 -0600
@@ -21,6 +21,8 @@
     return ui.configlist("merge-tools", tool + "." + part, default)
 
 internals = {}
+# Merge tools to document.
+internalsdoc = {}
 
 def internaltool(name, trymerge, onfailure=None):
     '''return a decorator for populating internal merge tool table'''
@@ -29,6 +31,7 @@
         func.__doc__ = "``%s``\n" % fullname + func.__doc__.strip()
         internals[fullname] = func
         internals['internal:' + name] = func
+        internalsdoc[fullname] = func
         func.trymerge = trymerge
         func.onfailure = onfailure
         return func
--- a/mercurial/help.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/help.py	Mon Mar 02 01:20:14 2015 -0600
@@ -6,11 +6,12 @@
 # GNU General Public License version 2 or any later version.
 
 from i18n import gettext, _
-import itertools, os
+import itertools, os, textwrap
 import error
 import extensions, revset, fileset, templatekw, templatefilters, filemerge
 import encoding, util, minirst
 import cmdutil
+import hgweb.webcommands as webcommands
 
 def listexts(header, exts, indent=1, showdeprecated=False):
     '''return a text listing of the given extensions'''
@@ -171,7 +172,7 @@
 def addtopichook(topic, rewriter):
     helphooks.setdefault(topic, []).append(rewriter)
 
-def makeitemsdoc(topic, doc, marker, items):
+def makeitemsdoc(topic, doc, marker, items, dedent=False):
     """Extract docstring from the items key to function mapping, build a
     .single documentation block and use it to overwrite the marker in doc
     """
@@ -181,27 +182,35 @@
         if not text:
             continue
         text = gettext(text)
+        if dedent:
+            text = textwrap.dedent(text)
         lines = text.splitlines()
         doclines = [(lines[0])]
         for l in lines[1:]:
             # Stop once we find some Python doctest
             if l.strip().startswith('>>>'):
                 break
-            doclines.append('  ' + l.strip())
+            if dedent:
+                doclines.append(l.rstrip())
+            else:
+                doclines.append('  ' + l.strip())
         entries.append('\n'.join(doclines))
     entries = '\n\n'.join(entries)
     return doc.replace(marker, entries)
 
-def addtopicsymbols(topic, marker, symbols):
+def addtopicsymbols(topic, marker, symbols, dedent=False):
     def add(topic, doc):
-        return makeitemsdoc(topic, doc, marker, symbols)
+        return makeitemsdoc(topic, doc, marker, symbols, dedent=dedent)
     addtopichook(topic, add)
 
 addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols)
-addtopicsymbols('merge-tools', '.. internaltoolsmarker', filemerge.internals)
+addtopicsymbols('merge-tools', '.. internaltoolsmarker',
+                filemerge.internalsdoc)
 addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols)
 addtopicsymbols('templates', '.. keywordsmarker', templatekw.dockeywords)
 addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
+addtopicsymbols('hgweb', '.. webcommandsmarker', webcommands.commands,
+                dedent=True)
 
 def help_(ui, name, unknowncmd=False, full=True, **opts):
     '''
--- a/mercurial/help/hgweb.txt	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/help/hgweb.txt	Mon Mar 02 01:20:14 2015 -0600
@@ -48,3 +48,39 @@
 
 The ``collections`` section is deprecated and has been superseded by
 ``paths``.
+
+URLs and Common Arguments
+=========================
+
+URLs under each repository have the form ``/{command}[/{arguments}]``
+where ``{command}`` represents the name of a command or handler and
+``{arguments}`` represents any number of additional URL parameters
+to that command.
+
+The web server has a default style associated with it. Styles map to
+a collection of named templates. Each template is used to render a
+specific piece of data, such as a changeset or diff.
+
+The style for the current request can be overwritten two ways. First,
+if ``{command}`` contains a hyphen (``-``), the text before the hyphen
+defines the style. For example, ``/atom-log`` will render the ``log``
+command handler with the ``atom`` style. The second way to set the
+style is with the ``style`` query string argument. For example,
+``/log?style=atom``. The hyphenated URL parameter is preferred.
+
+Not all templates are available for all styles. Attempting to use
+a style that doesn't have all templates defined may result in an error
+rendering the page.
+
+Many commands take a ``{revision}`` URL parameter. This defines the
+changeset to operate on. This is commonly specified as the short,
+12 digit hexidecimal abbreviation for the full 40 character unique
+revision identifier. However, any value described by
+:hg:`help revisions` typically works.
+
+Commands and URLs
+=================
+
+The following web commands and their URLs are available:
+
+  .. webcommandsmarker
--- a/mercurial/help/subrepos.txt	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/help/subrepos.txt	Mon Mar 02 01:20:14 2015 -0600
@@ -91,7 +91,7 @@
     -S/--subrepos is specified.
 
 :cat: cat currently only handles exact file matches in subrepos.
-    Git and Subversion subrepositories are currently ignored.
+    Subversion subrepositories are currently ignored.
 
 :commit: commit creates a consistent snapshot of the state of the
     entire project and its subrepositories. If any subrepositories
--- a/mercurial/hgweb/webcommands.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/hgweb/webcommands.py	Mon Mar 02 01:20:14 2015 -0600
@@ -13,27 +13,58 @@
 from common import paritygen, staticfile, get_contact, ErrorResponse
 from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND
 from mercurial import graphmod, patch
-from mercurial import help as helpmod
 from mercurial import scmutil
 from mercurial.i18n import _
 from mercurial.error import ParseError, RepoLookupError, Abort
 from mercurial import revset
 
-# __all__ is populated with the allowed commands. Be sure to add to it if
-# you're adding a new command, or the new command won't work.
+__all__ = []
+commands = {}
+
+class webcommand(object):
+    """Decorator used to register a web command handler.
+
+    The decorator takes as its positional arguments the name/path the
+    command should be accessible under.
+
+    Usage:
+
+    @webcommand('mycommand')
+    def mycommand(web, req, tmpl):
+        pass
+    """
+
+    def __init__(self, name):
+        self.name = name
 
-__all__ = [
-   'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
-   'manifest', 'tags', 'bookmarks', 'branches', 'summary', 'filediff', 'diff',
-   'comparison', 'annotate', 'filelog', 'archive', 'static', 'graph', 'help',
-]
+    def __call__(self, func):
+        __all__.append(self.name)
+        commands[self.name] = func
+        return func
+
+@webcommand('log')
+def log(web, req, tmpl):
+    """
+    /log[/{revision}[/{path}]]
+    --------------------------
 
-def log(web, req, tmpl):
+    Show repository or file history.
+
+    For URLs of the form ``/log/{revision}``, a list of changesets starting at
+    the specified changeset identifier is shown. If ``{revision}`` is not
+    defined, the default is ``tip``. This form is equivalent to the
+    ``changelog`` handler.
+
+    For URLs of the form ``/log/{revision}/{file}``, the history for a specific
+    file will be shown. This form is equivalent to the ``filelog`` handler.
+    """
+
     if 'file' in req.form and req.form['file'][0]:
         return filelog(web, req, tmpl)
     else:
         return changelog(web, req, tmpl)
 
+@webcommand('rawfile')
 def rawfile(web, req, tmpl):
     guessmime = web.configbool('web', 'guessmime', False)
 
@@ -98,7 +129,26 @@
                 rename=webutil.renamelink(fctx),
                 permissions=fctx.manifest().flags(f))
 
+@webcommand('file')
 def file(web, req, tmpl):
+    """
+    /file/{revision}[/{path}]
+    -------------------------
+
+    Show information about a directory or file in the repository.
+
+    Info about the ``path`` given as a URL parameter will be rendered.
+
+    If ``path`` is a directory, information about the entries in that
+    directory will be rendered. This form is equivalent to the ``manifest``
+    handler.
+
+    If ``path`` is a file, information about that file will be shown via
+    the ``filerevision`` template.
+
+    If ``path`` is not defined, information about the root directory will
+    be rendered.
+    """
     path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
     if not path:
         return manifest(web, req, tmpl)
@@ -187,7 +237,7 @@
 
         mfunc = revset.match(web.repo.ui, revdef)
         try:
-            revs = mfunc(web.repo, revset.baseset(web.repo))
+            revs = mfunc(web.repo)
             return MODE_REVSET, revs
             # ParseError: wrongly placed tokens, wrongs arguments, etc
             # RepoLookupError: no such revision, e.g. in 'revision:'
@@ -267,7 +317,31 @@
                 modedesc=searchfunc[1],
                 showforcekw=showforcekw, showunforcekw=showunforcekw)
 
+@webcommand('changelog')
 def changelog(web, req, tmpl, shortlog=False):
+    """
+    /changelog[/{revision}]
+    -----------------------
+
+    Show information about multiple changesets.
+
+    If the optional ``revision`` URL argument is absent, information about
+    all changesets starting at ``tip`` will be rendered. If the ``revision``
+    argument is present, changesets will be shown starting from the specified
+    revision.
+
+    If ``revision`` is absent, the ``rev`` query string argument may be
+    defined. This will perform a search for changesets.
+
+    The argument for ``rev`` can be a single revision, a revision set,
+    or a literal keyword to search for in changeset data (equivalent to
+    :hg:`log -k`.
+
+    The ``revcount`` query string argument defines the maximum numbers of
+    changesets to render.
+
+    For non-searches, the ``changelog`` template will be rendered.
+    """
 
     query = ''
     if 'node' in req.form:
@@ -326,10 +400,36 @@
                 archives=web.archivelist("tip"), revcount=revcount,
                 morevars=morevars, lessvars=lessvars, query=query)
 
+@webcommand('shortlog')
 def shortlog(web, req, tmpl):
+    """
+    /shortlog
+    ---------
+
+    Show basic information about a set of changesets.
+
+    This accepts the same parameters as the ``changelog`` handler. The only
+    difference is the ``shortlog`` template will be rendered instead of the
+    ``changelog`` template.
+    """
     return changelog(web, req, tmpl, shortlog=True)
 
+@webcommand('changeset')
 def changeset(web, req, tmpl):
+    """
+    /changeset[/{revision}]
+    -----------------------
+
+    Show information about a single changeset.
+
+    A URL path argument is the changeset identifier to show. See ``hg help
+    revisions`` for possible values. If not defined, the ``tip`` changeset
+    will be shown.
+
+    The ``changeset`` template is rendered. Contents of the ``changesettag``,
+    ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
+    templates related to diffs may all be used to produce the output.
+    """
     ctx = webutil.changectx(web.repo, req)
     basectx = webutil.basechangectx(web.repo, req)
     if basectx is None:
@@ -382,7 +482,7 @@
                 inbranch=webutil.nodeinbranch(web.repo, ctx),
                 branches=webutil.nodebranchdict(web.repo, ctx))
 
-rev = changeset
+rev = webcommand('rev')(changeset)
 
 def decodepath(path):
     """Hook for mapping a path in the repository to a path in the
@@ -392,7 +492,23 @@
     the virtual file system presented by the manifest command below."""
     return path
 
+@webcommand('manifest')
 def manifest(web, req, tmpl):
+    """
+    /manifest[/{revision}[/{path}]]
+    -------------------------------
+
+    Show information about a directory.
+
+    If the URL path arguments are defined, information about the root
+    directory for the ``tip`` changeset will be shown.
+
+    Because this handler can only show information for directories, it
+    is recommended to use the ``file`` handler instead, as it can handle both
+    directories and files.
+
+    The ``manifest`` template will be rendered for this handler.
+    """
     ctx = webutil.changectx(web.repo, req)
     path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
     mf = ctx.manifest()
@@ -474,7 +590,18 @@
                 inbranch=webutil.nodeinbranch(web.repo, ctx),
                 branches=webutil.nodebranchdict(web.repo, ctx))
 
+@webcommand('tags')
 def tags(web, req, tmpl):
+    """
+    /tags
+    -----
+
+    Show information about tags.
+
+    No arguments are accepted.
+
+    The ``tags`` template is rendered.
+    """
     i = list(reversed(web.repo.tagslist()))
     parity = paritygen(web.stripecount)
 
@@ -496,7 +623,18 @@
                 entriesnotip=lambda **x: entries(True, False, **x),
                 latestentry=lambda **x: entries(True, True, **x))
 
+@webcommand('bookmarks')
 def bookmarks(web, req, tmpl):
+    """
+    /bookmarks
+    ----------
+
+    Show information about bookmarks.
+
+    No arguments are accepted.
+
+    The ``bookmarks`` template is rendered.
+    """
     i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
     parity = paritygen(web.stripecount)
 
@@ -516,7 +654,20 @@
                 entries=lambda **x: entries(latestonly=False, **x),
                 latestentry=lambda **x: entries(latestonly=True, **x))
 
+@webcommand('branches')
 def branches(web, req, tmpl):
+    """
+    /branches
+    ---------
+
+    Show information about branches.
+
+    All known branches are contained in the output, even closed branches.
+
+    No arguments are accepted.
+
+    The ``branches`` template is rendered.
+    """
     tips = []
     heads = web.repo.heads()
     parity = paritygen(web.stripecount)
@@ -547,7 +698,19 @@
                 entries=lambda **x: entries(0, **x),
                 latestentry=lambda **x: entries(1, **x))
 
+@webcommand('summary')
 def summary(web, req, tmpl):
+    """
+    /summary
+    --------
+
+    Show a summary of repository state.
+
+    Information about the latest changesets, bookmarks, tags, and branches
+    is captured by this handler.
+
+    The ``summary`` template is rendered.
+    """
     i = reversed(web.repo.tagslist())
 
     def tagentries(**map):
@@ -632,7 +795,19 @@
                 node=tip.hex(),
                 archives=web.archivelist("tip"))
 
+@webcommand('filediff')
 def filediff(web, req, tmpl):
+    """
+    /diff/{revision}/{path}
+    -----------------------
+
+    Show how a file changed in a particular commit.
+
+    The ``filediff`` template is rendered.
+
+    This hander is registered under both the ``/diff`` and ``/filediff``
+    paths. ``/diff`` is used in modern code.
+    """
     fctx, ctx = None, None
     try:
         fctx = webutil.filectx(web.repo, req)
@@ -672,9 +847,25 @@
                 child=webutil.children(ctx),
                 diff=diffs)
 
-diff = filediff
+diff = webcommand('diff')(filediff)
+
+@webcommand('comparison')
+def comparison(web, req, tmpl):
+    """
+    /comparison/{revision}/{path}
+    -----------------------------
 
-def comparison(web, req, tmpl):
+    Show a comparison between the old and new versions of a file from changes
+    made on a particular revision.
+
+    This is similar to the ``diff`` handler. However, this form features
+    a split or side-by-side diff rather than a unified diff.
+
+    The ``context`` query string argument can be used to control the lines of
+    context in the diff.
+
+    The ``filecomparison`` template is rendered.
+    """
     ctx = webutil.changectx(web.repo, req)
     if 'file' not in req.form:
         raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
@@ -732,7 +923,16 @@
                 rightnode=hex(rightnode),
                 comparison=comparison)
 
+@webcommand('annotate')
 def annotate(web, req, tmpl):
+    """
+    /annotate/{revision}/{path}
+    ---------------------------
+
+    Show changeset information for each line in a file.
+
+    The ``fileannotate`` template is rendered.
+    """
     fctx = webutil.filectx(web.repo, req)
     f = fctx.path()
     parity = paritygen(web.stripecount)
@@ -784,7 +984,19 @@
                 child=webutil.children(fctx),
                 permissions=fctx.manifest().flags(f))
 
+@webcommand('filelog')
 def filelog(web, req, tmpl):
+    """
+    /filelog/{revision}/{path}
+    --------------------------
+
+    Show information about the history of a file in the repository.
+
+    The ``revcount`` query string argument can be defined to control the
+    maximum number of entries to show.
+
+    The ``filelog`` template will be rendered.
+    """
 
     try:
         fctx = webutil.filectx(web.repo, req)
@@ -862,7 +1074,27 @@
                 latestentry=latestentry,
                 revcount=revcount, morevars=morevars, lessvars=lessvars)
 
+@webcommand('archive')
 def archive(web, req, tmpl):
+    """
+    /archive/{revision}.{format}[/{path}]
+    -------------------------------------
+
+    Obtain an archive of repository content.
+
+    The content and type of the archive is defined by a URL path parameter.
+    ``format`` is the file extension of the archive type to be generated. e.g.
+    ``zip`` or ``tar.bz2``. Not all archive types may be allowed by your
+    server configuration.
+
+    The optional ``path`` URL parameter controls content to include in the
+    archive. If omitted, every file in the specified revision is present in the
+    archive. If included, only the specified file or contents of the specified
+    directory will be included in the archive.
+
+    No template is used for this handler. Raw, binary content is generated.
+    """
+
     type_ = req.form.get('type', [None])[0]
     allowed = web.configlist("web", "allow_archive")
     key = req.form['node'][0]
@@ -911,6 +1143,7 @@
     return []
 
 
+@webcommand('static')
 def static(web, req, tmpl):
     fname = req.form['file'][0]
     # a repo owner may set web.static in .hg/hgrc to get any file
@@ -924,7 +1157,24 @@
     staticfile(static, fname, req)
     return []
 
+@webcommand('graph')
 def graph(web, req, tmpl):
+    """
+    /graph[/{revision}]
+    -------------------
+
+    Show information about the graphical topology of the repository.
+
+    Information rendered by this handler can be used to create visual
+    representations of repository topology.
+
+    The ``revision`` URL parameter controls the starting changeset.
+
+    The ``revcount`` query string argument can define the number of changesets
+    to show information for.
+
+    This handler will render the ``graph`` template.
+    """
 
     ctx = webutil.changectx(web.repo, req)
     rev = ctx.rev()
@@ -1047,8 +1297,23 @@
         doc = _('(no help text available)')
     return doc
 
+@webcommand('help')
 def help(web, req, tmpl):
+    """
+    /help[/{topic}]
+    ---------------
+
+    Render help documentation.
+
+    This web command is roughly equivalent to :hg:`help`. If a ``topic``
+    is defined, that help topic will be rendered. If not, an index of
+    available help topics will be rendered.
+
+    The ``help`` template will be rendered when requesting help for a topic.
+    ``helptopics`` will be rendered for the index of help topics.
+    """
     from mercurial import commands # avoid cycle
+    from mercurial import help as helpmod # avoid cycle
 
     topicname = req.form.get('node', [None])[0]
     if not topicname:
--- a/mercurial/hgweb/webutil.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/hgweb/webutil.py	Mon Mar 02 01:20:14 2015 -0600
@@ -138,9 +138,10 @@
         yield d
 
 def parents(ctx, hide=None):
-    if (isinstance(ctx, context.basefilectx) and
-        ctx.changectx().rev() != ctx.linkrev()):
-        return _siblings([ctx._repo[ctx.linkrev()]], hide)
+    if isinstance(ctx, context.basefilectx):
+        introrev = ctx.introrev()
+        if ctx.changectx().rev() != introrev:
+            return _siblings([ctx._repo[introrev]], hide)
     return _siblings(ctx.parents(), hide)
 
 def children(ctx, hide=None):
--- a/mercurial/localrepo.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/localrepo.py	Mon Mar 02 01:20:14 2015 -0600
@@ -323,6 +323,9 @@
         maxchainlen = self.ui.configint('format', 'maxchainlen')
         if maxchainlen is not None:
             self.svfs.options['maxchainlen'] = maxchainlen
+        manifestcachesize = self.ui.configint('format', 'manifestcachesize')
+        if manifestcachesize is not None:
+            self.svfs.options['manifestcachesize'] = manifestcachesize
 
     def _writerequirements(self):
         reqfile = self.vfs("requires", "w")
@@ -479,7 +482,7 @@
         '''Return a list of revisions matching the given revset'''
         expr = revset.formatspec(expr, *args)
         m = revset.match(None, expr)
-        return m(self, revset.spanset(self))
+        return m(self)
 
     def set(self, expr, *args):
         '''
@@ -501,7 +504,6 @@
         """
         return hook.hook(self.ui, self, name, throw, **args)
 
-    @unfilteredmethod
     def _tag(self, names, node, message, local, user, date, extra={},
              editor=False):
         if isinstance(names, str):
--- a/mercurial/manifest.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/manifest.py	Mon Mar 02 01:20:14 2015 -0600
@@ -10,13 +10,8 @@
 import array, struct
 
 class manifestdict(dict):
-    def __init__(self, mapping=None, flags=None):
-        if mapping is None:
-            mapping = {}
-        if flags is None:
-            flags = {}
-        dict.__init__(self, mapping)
-        self._flags = flags
+    def __init__(self):
+        self._flags = {}
     def __setitem__(self, k, v):
         assert v is not None
         dict.__setitem__(self, k, v)
@@ -26,7 +21,10 @@
         """Set the flags (symlink, executable) for path f."""
         self._flags[f] = flags
     def copy(self):
-        return manifestdict(self, dict.copy(self._flags))
+        copy = manifestdict()
+        dict.__init__(copy, self)
+        copy._flags = dict.copy(self._flags)
+        return copy
     def intersectfiles(self, files):
         '''make a new manifestdict with the intersection of self with files
 
@@ -50,11 +48,11 @@
             (not match.anypats() and util.all(fn in self for fn in files))):
             return self.intersectfiles(files)
 
-        mf = self.copy()
-        for fn in mf.keys():
+        m = self.copy()
+        for fn in m.keys():
             if not match(fn):
-                del mf[fn]
-        return mf
+                del m[fn]
+        return m
 
     def diff(self, m2, clean=False):
         '''Finds changes between the current manifest and m2.
@@ -220,9 +218,14 @@
 
 class manifest(revlog.revlog):
     def __init__(self, opener):
-        # we expect to deal with not more than four revs at a time,
-        # during a commit --amend
-        self._mancache = util.lrucachedict(4)
+        # During normal operations, we expect to deal with not more than four
+        # revs at a time (such as during commit --amend). When rebasing large
+        # stacks of commits, the number can go up, hence the config knob below.
+        cachesize = 4
+        opts = getattr(opener, 'options', None)
+        if opts is not None:
+            cachesize = opts.get('manifestcachesize', cachesize)
+        self._mancache = util.lrucachedict(cachesize)
         revlog.revlog.__init__(self, opener, "00manifest.i")
 
     def readdelta(self, node):
@@ -244,16 +247,16 @@
             return self._mancache[node][0]
         text = self.revision(node)
         arraytext = array.array('c', text)
-        mapping = _parse(text)
-        self._mancache[node] = (mapping, arraytext)
-        return mapping
+        m = _parse(text)
+        self._mancache[node] = (m, arraytext)
+        return m
 
     def find(self, node, f):
         '''look up entry for a single file efficiently.
         return (node, flags) pair if found, (None, None) if not.'''
         if node in self._mancache:
-            mapping = self._mancache[node][0]
-            return mapping.get(f), mapping.flags(f)
+            m = self._mancache[node][0]
+            return m.get(f), m.flags(f)
         text = self.revision(node)
         start, end = _msearch(text, f)
         if start == end:
@@ -262,7 +265,7 @@
         f, n = l.split('\0')
         return revlog.bin(n[:40]), n[40:-1]
 
-    def add(self, map, transaction, link, p1, p2, added, removed):
+    def add(self, m, transaction, link, p1, p2, added, removed):
         if p1 in self._mancache:
             # If our first parent is in the manifest cache, we can
             # compute a delta here using properties we know about the
@@ -277,7 +280,7 @@
             # since the lists are already sorted
             work.sort()
 
-            arraytext, deltatext = map.fastdelta(self._mancache[p1][1], work)
+            arraytext, deltatext = m.fastdelta(self._mancache[p1][1], work)
             cachedelta = self.rev(p1), deltatext
             text = util.buffer(arraytext)
         else:
@@ -285,11 +288,11 @@
             # just encode a fulltext of the manifest and pass that
             # through to the revlog layer, and let it handle the delta
             # process.
-            text = map.text()
+            text = m.text()
             arraytext = array.array('c', text)
             cachedelta = None
 
         n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
-        self._mancache[n] = (map, arraytext)
+        self._mancache[n] = (m, arraytext)
 
         return n
--- a/mercurial/mdiff.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/mdiff.py	Mon Mar 02 01:20:14 2015 -0600
@@ -367,6 +367,9 @@
 def trivialdiffheader(length):
     return struct.pack(">lll", 0, 0, length)
 
+def replacediffheader(oldlen, newlen):
+    return struct.pack(">lll", 0, oldlen, newlen)
+
 patches = mpatch.patches
 patchedsize = mpatch.patchedsize
 textdiff = bdiff.bdiff
--- a/mercurial/obsolete.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/obsolete.py	Mon Mar 02 01:20:14 2015 -0600
@@ -68,15 +68,14 @@
 
 """
 import struct
-import util, base85, node
+import util, base85, node, parsers
 import phases
 from i18n import _
 
 _pack = struct.pack
 _unpack = struct.unpack
 _calcsize = struct.calcsize
-
-_SEEK_END = 2 # os.SEEK_END was introduced in Python 2.5
+propertycache = util.propertycache
 
 # the obsolete feature is not mature enough to be enabled by default.
 # you have to rely on third party extension extension to enable this.
@@ -146,7 +145,7 @@
 _fm0fsize = _calcsize(_fm0fixed)
 _fm0fnodesize = _calcsize(_fm0node)
 
-def _fm0readmarkers(data, off=0):
+def _fm0readmarkers(data, off):
     # Loop on markers
     l = len(data)
     while off + _fm0fsize <= l:
@@ -285,7 +284,7 @@
 _fm1metapair = 'BB'
 _fm1metapairsize = _calcsize('BB')
 
-def _fm1readmarkers(data, off=0):
+def _fm1purereadmarkers(data, off):
     # make some global constants local for performance
     noneflag = _fm1parentnone
     sha2flag = usingsha256
@@ -301,6 +300,7 @@
     # Loop on markers
     stop = len(data) - _fm1fsize
     ufixed = util.unpacker(_fm1fixed)
+
     while off <= stop:
         # read fixed part
         o1 = off + fsize
@@ -395,6 +395,13 @@
         data.append(value)
     return ''.join(data)
 
+def _fm1readmarkers(data, off):
+    native = getattr(parsers, 'fm1readmarkers', None)
+    if not native:
+        return _fm1purereadmarkers(data, off)
+    stop = len(data) - _fm1fsize
+    return native(data, off, stop)
+
 # mapping to read/write various marker formats
 # <version> -> (decoder, encoder)
 formats = {_fm0version: (_fm0readmarkers, _fm0encodeonemarker),
@@ -462,15 +469,35 @@
         """The flags field of the marker"""
         return self._data[2]
 
-def _checkinvalidmarkers(obsstore):
+@util.nogc
+def _addsuccessors(successors, markers):
+    for mark in markers:
+        successors.setdefault(mark[0], set()).add(mark)
+
+@util.nogc
+def _addprecursors(precursors, markers):
+    for mark in markers:
+        for suc in mark[1]:
+            precursors.setdefault(suc, set()).add(mark)
+
+@util.nogc
+def _addchildren(children, markers):
+    for mark in markers:
+        parents = mark[5]
+        if parents is not None:
+            for p in parents:
+                children.setdefault(p, set()).add(mark)
+
+def _checkinvalidmarkers(markers):
     """search for marker with invalid data and raise error if needed
 
     Exist as a separated function to allow the evolve extension for a more
     subtle handling.
     """
-    if node.nullid in obsstore.precursors:
-        raise util.Abort(_('bad obsolescence marker detected: '
-                           'invalid successors nullid'))
+    for mark in markers:
+        if node.nullid in mark[1]:
+            raise util.Abort(_('bad obsolescence marker detected: '
+                               'invalid successors nullid'))
 
 class obsstore(object):
     """Store obsolete markers
@@ -494,16 +521,13 @@
         # caches for various obsolescence related cache
         self.caches = {}
         self._all = []
-        self.precursors = {}
-        self.successors = {}
-        self.children = {}
         self.sopener = sopener
         data = sopener.tryread('obsstore')
         self._version = defaultformat
         self._readonly = readonly
         if data:
             self._version, markers = _readmarkers(data)
-            self._load(markers)
+            self._addmarkers(markers)
 
     def __iter__(self):
         return iter(self._all)
@@ -566,12 +590,6 @@
         if new:
             f = self.sopener('obsstore', 'ab')
             try:
-                # Whether the file's current position is at the begin or at
-                # the end after opening a file for appending is implementation
-                # defined. So we must seek to the end before calling tell(),
-                # or we may get a zero offset for non-zero sized files on
-                # some platforms (issue3543).
-                f.seek(0, _SEEK_END)
                 offset = f.tell()
                 transaction.add('obsstore', offset)
                 # offset == 0: new file - add the version header
@@ -581,7 +599,7 @@
                 # XXX: f.close() == filecache invalidation == obsstore rebuilt.
                 # call 'filecacheentry.refresh()'  here
                 f.close()
-            self._load(new)
+            self._addmarkers(new)
             # new marker *may* have changed several set. invalidate the cache.
             self.caches.clear()
         # records the number of new markers for the transaction hooks
@@ -596,19 +614,37 @@
         version, markers = _readmarkers(data)
         return self.add(transaction, markers)
 
-    @util.nogc
-    def _load(self, markers):
-        for mark in markers:
-            self._all.append(mark)
-            pre, sucs = mark[:2]
-            self.successors.setdefault(pre, set()).add(mark)
-            for suc in sucs:
-                self.precursors.setdefault(suc, set()).add(mark)
-            parents = mark[5]
-            if parents is not None:
-                for p in parents:
-                    self.children.setdefault(p, set()).add(mark)
-        _checkinvalidmarkers(self)
+    @propertycache
+    def successors(self):
+        successors = {}
+        _addsuccessors(successors, self._all)
+        return successors
+
+    @propertycache
+    def precursors(self):
+        precursors = {}
+        _addprecursors(precursors, self._all)
+        return precursors
+
+    @propertycache
+    def children(self):
+        children = {}
+        _addchildren(children, self._all)
+        return children
+
+    def _cached(self, attr):
+        return attr in self.__dict__
+
+    def _addmarkers(self, markers):
+        markers = list(markers) # to allow repeated iteration
+        self._all.extend(markers)
+        if self._cached('successors'):
+            _addsuccessors(self.successors, markers)
+        if self._cached('precursors'):
+            _addprecursors(self.precursors, markers)
+        if self._cached('children'):
+            _addchildren(self.children, markers)
+        _checkinvalidmarkers(markers)
 
     def relevantmarkers(self, nodes):
         """return a set of all obsolescence markers relevant to a set of nodes.
--- a/mercurial/parsers.c	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/parsers.c	Mon Mar 02 01:20:14 2015 -0600
@@ -1676,108 +1676,6 @@
 }
 
 /*
- * Given a (possibly overlapping) set of revs, return the greatest
- * common ancestors: those with the longest path to the root.
- */
-static PyObject *index_ancestors(indexObject *self, PyObject *args)
-{
-	PyObject *ret = NULL, *gca = NULL;
-	Py_ssize_t argcount, i, len;
-	bitmask repeat = 0;
-	int revcount = 0;
-	int *revs;
-
-	argcount = PySequence_Length(args);
-	revs = malloc(argcount * sizeof(*revs));
-	if (argcount > 0 && revs == NULL)
-		return PyErr_NoMemory();
-	len = index_length(self) - 1;
-
-	for (i = 0; i < argcount; i++) {
-		static const int capacity = 24;
-		PyObject *obj = PySequence_GetItem(args, i);
-		bitmask x;
-		long val;
-
-		if (!PyInt_Check(obj)) {
-			PyErr_SetString(PyExc_TypeError,
-					"arguments must all be ints");
-			Py_DECREF(obj);
-			goto bail;
-		}
-		val = PyInt_AsLong(obj);
-		Py_DECREF(obj);
-		if (val == -1) {
-			ret = PyList_New(0);
-			goto done;
-		}
-		if (val < 0 || val >= len) {
-			PyErr_SetString(PyExc_IndexError,
-					"index out of range");
-			goto bail;
-		}
-		/* this cheesy bloom filter lets us avoid some more
-		 * expensive duplicate checks in the common set-is-disjoint
-		 * case */
-		x = 1ull << (val & 0x3f);
-		if (repeat & x) {
-			int k;
-			for (k = 0; k < revcount; k++) {
-				if (val == revs[k])
-					goto duplicate;
-			}
-		}
-		else repeat |= x;
-		if (revcount >= capacity) {
-			PyErr_Format(PyExc_OverflowError,
-				     "bitset size (%d) > capacity (%d)",
-				     revcount, capacity);
-			goto bail;
-		}
-		revs[revcount++] = (int)val;
-	duplicate:;
-	}
-
-	if (revcount == 0) {
-		ret = PyList_New(0);
-		goto done;
-	}
-	if (revcount == 1) {
-		PyObject *obj;
-		ret = PyList_New(1);
-		if (ret == NULL)
-			goto bail;
-		obj = PyInt_FromLong(revs[0]);
-		if (obj == NULL)
-			goto bail;
-		PyList_SET_ITEM(ret, 0, obj);
-		goto done;
-	}
-
-	gca = find_gca_candidates(self, revs, revcount);
-	if (gca == NULL)
-		goto bail;
-
-	if (PyList_GET_SIZE(gca) <= 1) {
-		ret = gca;
-		Py_INCREF(gca);
-	}
-	else ret = find_deepest(self, gca);
-
-done:
-	free(revs);
-	Py_XDECREF(gca);
-
-	return ret;
-
-bail:
-	free(revs);
-	Py_XDECREF(gca);
-	Py_XDECREF(ret);
-	return NULL;
-}
-
-/*
  * Given a (possibly overlapping) set of revs, return all the
  * common ancestors heads: heads(::args[0] and ::a[1] and ...)
  */
@@ -1871,6 +1769,24 @@
 }
 
 /*
+ * Given a (possibly overlapping) set of revs, return the greatest
+ * common ancestors: those with the longest path to the root.
+ */
+static PyObject *index_ancestors(indexObject *self, PyObject *args)
+{
+	PyObject *gca = index_commonancestorsheads(self, args);
+	if (gca == NULL)
+		return NULL;
+
+	if (PyList_GET_SIZE(gca) <= 1) {
+		Py_INCREF(gca);
+		return gca;
+	}
+
+	return find_deepest(self, gca);
+}
+
+/*
  * Invalidate any trie entries introduced by added revs.
  */
 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
@@ -2230,6 +2146,157 @@
 	return NULL;
 }
 
+#define BUMPED_FIX 1
+#define USING_SHA_256 2
+
+static PyObject *readshas(
+	const char *source, unsigned char num, Py_ssize_t hashwidth)
+{
+	int i;
+	PyObject *list = PyTuple_New(num);
+	if (list == NULL) {
+		return NULL;
+	}
+	for (i = 0; i < num; i++) {
+		PyObject *hash = PyString_FromStringAndSize(source, hashwidth);
+		if (hash == NULL) {
+			Py_DECREF(list);
+			return NULL;
+		}
+		PyTuple_SetItem(list, i, hash);
+		source += hashwidth;
+	}
+	return list;
+}
+
+static PyObject *fm1readmarker(const char *data, uint32_t *msize)
+{
+	const char *meta;
+
+	double mtime;
+	int16_t tz;
+	uint16_t flags;
+	unsigned char nsuccs, nparents, nmetadata;
+	Py_ssize_t hashwidth = 20;
+
+	PyObject *prec = NULL, *parents = NULL, *succs = NULL;
+	PyObject *metadata = NULL, *ret = NULL;
+	int i;
+
+	*msize = getbe32(data);
+	data += 4;
+	mtime = getbefloat64(data);
+	data += 8;
+	tz = getbeint16(data);
+	data += 2;
+	flags = getbeuint16(data);
+	data += 2;
+
+	if (flags & USING_SHA_256) {
+		hashwidth = 32;
+	}
+
+	nsuccs = (unsigned char)(*data++);
+	nparents = (unsigned char)(*data++);
+	nmetadata = (unsigned char)(*data++);
+
+	prec = PyString_FromStringAndSize(data, hashwidth);
+	data += hashwidth;
+	if (prec == NULL) {
+		goto bail;
+	}
+
+	succs = readshas(data, nsuccs, hashwidth);
+	if (succs == NULL) {
+		goto bail;
+	}
+	data += nsuccs * hashwidth;
+
+	if (nparents == 1 || nparents == 2) {
+		parents = readshas(data, nparents, hashwidth);
+		if (parents == NULL) {
+			goto bail;
+		}
+		data += nparents * hashwidth;
+	} else {
+		parents = Py_None;
+	}
+
+	meta = data + (2 * nmetadata);
+	metadata = PyTuple_New(nmetadata);
+	if (metadata == NULL) {
+		goto bail;
+	}
+	for (i = 0; i < nmetadata; i++) {
+		PyObject *tmp, *left = NULL, *right = NULL;
+		Py_ssize_t metasize = (unsigned char)(*data++);
+		left = PyString_FromStringAndSize(meta, metasize);
+		meta += metasize;
+		metasize = (unsigned char)(*data++);
+		right = PyString_FromStringAndSize(meta, metasize);
+		meta += metasize;
+		if (!left || !right) {
+			Py_XDECREF(left);
+			Py_XDECREF(right);
+			goto bail;
+		}
+		tmp = PyTuple_Pack(2, left, right);
+		Py_DECREF(left);
+		Py_DECREF(right);
+		if (!tmp) {
+			goto bail;
+		}
+		PyTuple_SetItem(metadata, i, tmp);
+	}
+	ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags,
+			    metadata, mtime, (int)tz * 60, parents);
+bail:
+	Py_XDECREF(prec);
+	Py_XDECREF(succs);
+	Py_XDECREF(metadata);
+	if (parents != Py_None)
+		Py_XDECREF(parents);
+	return ret;
+}
+
+
+static PyObject *fm1readmarkers(PyObject *self, PyObject *args) {
+	const char *data;
+	Py_ssize_t datalen;
+	/* only unsigned long because python 2.4, should be Py_ssize_t */
+	unsigned long offset, stop;
+	PyObject *markers = NULL;
+
+	/* replace kk with nn when we drop Python 2.4 */
+	if (!PyArg_ParseTuple(args, "s#kk", &data, &datalen, &offset, &stop)) {
+		return NULL;
+	}
+	data += offset;
+	markers = PyList_New(0);
+	if (!markers) {
+		return NULL;
+	}
+	while (offset < stop) {
+		uint32_t msize;
+		int error;
+		PyObject *record = fm1readmarker(data, &msize);
+		if (!record) {
+			goto bail;
+		}
+		error = PyList_Append(markers, record);
+		Py_DECREF(record);
+		if (error) {
+			goto bail;
+		}
+		data += msize;
+		offset += msize;
+	}
+	return markers;
+bail:
+	Py_DECREF(markers);
+	return NULL;
+}
+
 static char parsers_doc[] = "Efficient content parsing.";
 
 PyObject *encodedir(PyObject *self, PyObject *args);
@@ -2245,6 +2312,8 @@
 	{"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
 	{"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
 	{"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
+	{"fm1readmarkers", fm1readmarkers, METH_VARARGS,
+			"parse v1 obsolete markers\n"},
 	{NULL, NULL}
 };
 
--- a/mercurial/patch.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/patch.py	Mon Mar 02 01:20:14 2015 -0600
@@ -1736,20 +1736,47 @@
     '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
     return difflabel(diff, *args, **kw)
 
+def _filepairs(ctx1, modified, added, removed, copy, opts):
+    '''generates tuples (f1, f2, copyop), where f1 is the name of the file
+    before and f2 is the the name after. For added files, f1 will be None,
+    and for removed files, f2 will be None. copyop may be set to None, 'copy'
+    or 'rename' (the latter two only if opts.git is set).'''
+    gone = set()
+
+    copyto = dict([(v, k) for k, v in copy.items()])
+
+    addedset, removedset = set(added), set(removed)
+    # Fix up  added, since merged-in additions appear as
+    # modifications during merges
+    for f in modified:
+        if f not in ctx1:
+            addedset.add(f)
+
+    for f in sorted(modified + added + removed):
+        copyop = None
+        f1, f2 = f, f
+        if f in addedset:
+            f1 = None
+            if f in copy:
+                if opts.git:
+                    f1 = copy[f]
+                    if f1 in removedset and f1 not in gone:
+                        copyop = 'rename'
+                        gone.add(f1)
+                    else:
+                        copyop = 'copy'
+        elif f in removedset:
+            f2 = None
+            if opts.git:
+                # have we already reported a copy above?
+                if (f in copyto and copyto[f] in addedset
+                    and copy[copyto[f]] == f):
+                    continue
+        yield f1, f2, copyop
+
 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
             copy, getfilectx, opts, losedatafn, prefix):
 
-    def join(f):
-        return posixpath.join(prefix, f)
-
-    def addmodehdr(header, omode, nmode):
-        if omode != nmode:
-            header.append('old mode %s\n' % omode)
-            header.append('new mode %s\n' % nmode)
-
-    def addindexmeta(meta, oindex, nindex):
-        meta.append('index %s..%s\n' % (oindex, nindex))
-
     def gitindex(text):
         if not text:
             text = ""
@@ -1764,120 +1791,79 @@
         aprefix = 'a/'
         bprefix = 'b/'
 
-    def diffline(a, b, revs):
-        if opts.git:
-            line = 'diff --git %s%s %s%s\n' % (aprefix, a, bprefix, b)
-        elif not repo.ui.quiet:
-            if revs:
-                revinfo = ' '.join(["-r %s" % rev for rev in revs])
-                line = 'diff %s %s\n' % (revinfo, a)
-            else:
-                line = 'diff %s\n' % a
-        else:
-            line = ''
-        return line
+    def diffline(f, revs):
+        revinfo = ' '.join(["-r %s" % rev for rev in revs])
+        return 'diff %s %s' % (revinfo, f)
 
     date1 = util.datestr(ctx1.date())
     date2 = util.datestr(ctx2.date())
 
-    gone = set()
     gitmode = {'l': '120000', 'x': '100755', '': '100644'}
 
-    copyto = dict([(v, k) for k, v in copy.items()])
-
-    if opts.git:
-        revs = None
-
-    modifiedset, addedset, removedset = set(modified), set(added), set(removed)
-    # Fix up modified and added, since merged-in additions appear as
-    # modifications during merges
-    for f in modifiedset.copy():
-        if f not in ctx1:
-            addedset.add(f)
-            modifiedset.remove(f)
-    for f in sorted(modified + added + removed):
-        to = None
-        tn = None
-        binarydiff = False
-        header = []
-        if f not in addedset:
-            to = getfilectx(f, ctx1).data()
-        if f not in removedset:
-            tn = getfilectx(f, ctx2).data()
-        a, b = f, f
+    for f1, f2, copyop in _filepairs(
+            ctx1, modified, added, removed, copy, opts):
+        content1 = None
+        content2 = None
+        flag1 = None
+        flag2 = None
+        if f1:
+            content1 = getfilectx(f1, ctx1).data()
+            if opts.git or losedatafn:
+                flag1 = ctx1.flags(f1)
+        if f2:
+            content2 = getfilectx(f2, ctx2).data()
+            if opts.git or losedatafn:
+                flag2 = ctx2.flags(f2)
+        binary = False
         if opts.git or losedatafn:
-            if f in addedset:
-                mode = gitmode[ctx2.flags(f)]
-                if f in copy or f in copyto:
-                    if opts.git:
-                        if f in copy:
-                            a = copy[f]
-                        else:
-                            a = copyto[f]
-                        omode = gitmode[ctx1.flags(a)]
-                        addmodehdr(header, omode, mode)
-                        if a in removedset and a not in gone:
-                            op = 'rename'
-                            gone.add(a)
-                        else:
-                            op = 'copy'
-                        header.append('%s from %s\n' % (op, join(a)))
-                        header.append('%s to %s\n' % (op, join(f)))
-                        to = getfilectx(a, ctx1).data()
-                    else:
-                        losedatafn(f)
-                else:
-                    if opts.git:
-                        header.append('new file mode %s\n' % mode)
-                    elif ctx2.flags(f):
-                        losedatafn(f)
-                if util.binary(to) or util.binary(tn):
-                    if opts.git:
-                        binarydiff = True
-                    else:
-                        losedatafn(f)
-                if not opts.git and not tn:
-                    # regular diffs cannot represent new empty file
-                    losedatafn(f)
-            elif f in removedset:
-                if opts.git:
-                    # have we already reported a copy above?
-                    if ((f in copy and copy[f] in addedset
-                         and copyto[copy[f]] == f) or
-                        (f in copyto and copyto[f] in addedset
-                         and copy[copyto[f]] == f)):
-                        continue
-                    else:
-                        header.append('deleted file mode %s\n' %
-                                      gitmode[ctx1.flags(f)])
-                        if util.binary(to):
-                            binarydiff = True
-                elif not to or util.binary(to):
-                    # regular diffs cannot represent empty file deletion
-                    losedatafn(f)
-            else:
-                oflag = ctx1.flags(f)
-                nflag = ctx2.flags(f)
-                binary = util.binary(to) or util.binary(tn)
-                if opts.git:
-                    addmodehdr(header, gitmode[oflag], gitmode[nflag])
-                    if binary:
-                        binarydiff = True
-                elif binary or nflag != oflag:
-                    losedatafn(f)
+            binary = util.binary(content1) or util.binary(content2)
+
+        if losedatafn and not opts.git:
+            if (binary or
+                # copy/rename
+                f2 in copy or
+                # empty file creation
+                (not f1 and not content2) or
+                # empty file deletion
+                (not content1 and not f2) or
+                # create with flags
+                (not f1 and flag2) or
+                # change flags
+                (f1 and f2 and flag1 != flag2)):
+                losedatafn(f2 or f1)
 
-        if opts.git or revs:
-            header.insert(0, diffline(join(a), join(b), revs))
-        if binarydiff and not opts.nobinary:
-            text = mdiff.b85diff(to, tn)
-            if text and opts.git:
-                addindexmeta(header, gitindex(to), gitindex(tn))
+        path1 = posixpath.join(prefix, f1 or f2)
+        path2 = posixpath.join(prefix, f2 or f1)
+        header = []
+        if opts.git:
+            header.append('diff --git %s%s %s%s' %
+                          (aprefix, path1, bprefix, path2))
+            if not f1: # added
+                header.append('new file mode %s' % gitmode[flag2])
+            elif not f2: # removed
+                header.append('deleted file mode %s' % gitmode[flag1])
+            else:  # modified/copied/renamed
+                mode1, mode2 = gitmode[flag1], gitmode[flag2]
+                if mode1 != mode2:
+                    header.append('old mode %s' % mode1)
+                    header.append('new mode %s' % mode2)
+                if copyop is not None:
+                    header.append('%s from %s' % (copyop, path1))
+                    header.append('%s to %s' % (copyop, path2))
+        elif revs and not repo.ui.quiet:
+            header.append(diffline(path1, revs))
+
+        if binary and opts.git and not opts.nobinary:
+            text = mdiff.b85diff(content1, content2)
+            if text:
+                header.append('index %s..%s' %
+                              (gitindex(content1), gitindex(content2)))
         else:
-            text = mdiff.unidiff(to, date1,
-                                 tn, date2,
-                                 join(a), join(b), opts=opts)
+            text = mdiff.unidiff(content1, date1,
+                                 content2, date2,
+                                 path1, path2, opts=opts)
         if header and (text or len(header) > 1):
-            yield ''.join(header)
+            yield '\n'.join(header) + '\n'
         if text:
             yield text
 
--- a/mercurial/revlog.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/revlog.py	Mon Mar 02 01:20:14 2015 -0600
@@ -277,6 +277,8 @@
 
     def tip(self):
         return self.node(len(self.index) - 2)
+    def __contains__(self, rev):
+        return 0 <= rev < len(self)
     def __len__(self):
         return len(self.index) - 1
     def __iter__(self):
@@ -1231,8 +1233,18 @@
             if dfh:
                 dfh.flush()
             ifh.flush()
-            basetext = self.revision(self.node(cachedelta[0]))
-            btext[0] = mdiff.patch(basetext, cachedelta[1])
+            baserev = cachedelta[0]
+            delta = cachedelta[1]
+            # special case deltas which replace entire base; no need to decode
+            # base revision. this neatly avoids censored bases, which throw when
+            # they're decoded.
+            hlen = struct.calcsize(">lll")
+            if delta[:hlen] == mdiff.replacediffheader(self.rawsize(baserev),
+                                                       len(delta) - hlen):
+                btext[0] = delta[hlen:]
+            else:
+                basetext = self.revision(self.node(baserev))
+                btext[0] = mdiff.patch(basetext, delta)
             try:
                 self.checkhash(btext[0], p1, p2, node)
                 if flags & REVIDX_ISCENSORED:
@@ -1249,8 +1261,14 @@
                 delta = cachedelta[1]
             else:
                 t = buildtext()
-                ptext = self.revision(self.node(rev))
-                delta = mdiff.textdiff(ptext, t)
+                if self.iscensored(rev):
+                    # deltas based on a censored revision must replace the
+                    # full content in one patch, so delta works everywhere
+                    header = mdiff.replacediffheader(self.rawsize(rev), len(t))
+                    delta = header + t
+                else:
+                    ptext = self.revision(self.node(rev))
+                    delta = mdiff.textdiff(ptext, t)
             data = self.compress(delta)
             l = len(data[1]) + len(data[0])
             if basecache[0] == rev:
@@ -1401,6 +1419,17 @@
                                       _('unknown delta base'))
 
                 baserev = self.rev(deltabase)
+
+                if baserev != nullrev and self.iscensored(baserev):
+                    # if base is censored, delta must be full replacement in a
+                    # single patch operation
+                    hlen = struct.calcsize(">lll")
+                    oldlen = self.rawsize(baserev)
+                    newlen = len(delta) - hlen
+                    if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
+                        raise error.CensoredBaseError(self.indexfile,
+                                                      self.node(baserev))
+
                 chain = self._addrevision(node, None, transaction, link,
                                           p1, p2, REVIDX_DEFAULT_FLAGS,
                                           (baserev, delta), ifh, dfh)
@@ -1417,6 +1446,10 @@
 
         return content
 
+    def iscensored(self, rev):
+        """Check if a file revision is censored."""
+        return False
+
     def getstrippoint(self, minlink):
         """find the minimum rev that must be stripped to strip the linkrev
 
--- a/mercurial/revset.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/revset.py	Mon Mar 02 01:20:14 2015 -0600
@@ -349,7 +349,7 @@
     return r & subset
 
 def dagrange(repo, subset, x, y):
-    r = spanset(repo)
+    r = fullreposet(repo)
     xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
     return xs & subset
 
@@ -396,7 +396,7 @@
     """
     # i18n: "ancestor" is a keyword
     l = getlist(x)
-    rl = spanset(repo)
+    rl = fullreposet(repo)
     anc = None
 
     # (getset(repo, rl, i) for i in l) generates a list of lists
@@ -412,7 +412,7 @@
     return baseset()
 
 def _ancestors(repo, subset, x, followfirst=False):
-    heads = getset(repo, spanset(repo), x)
+    heads = getset(repo, fullreposet(repo), x)
     if not heads:
         return baseset()
     s = _revancestors(repo, heads, followfirst)
@@ -544,7 +544,7 @@
         else:
             return subset.filter(lambda r: matcher(getbi(ucl, r)[0]))
 
-    s = getset(repo, spanset(repo), x)
+    s = getset(repo, fullreposet(repo), x)
     b = set()
     for r in s:
         b.add(getbi(ucl, r)[0])
@@ -708,7 +708,7 @@
     return subset.filter(matches)
 
 def _descendants(repo, subset, x, followfirst=False):
-    roots = getset(repo, spanset(repo), x)
+    roots = getset(repo, fullreposet(repo), x)
     if not roots:
         return baseset()
     s = _revdescendants(repo, roots, followfirst)
@@ -744,9 +744,9 @@
     is the same as passing all().
     """
     if x is not None:
-        sources = getset(repo, spanset(repo), x)
+        sources = getset(repo, fullreposet(repo), x)
     else:
-        sources = getall(repo, spanset(repo), x)
+        sources = getall(repo, fullreposet(repo), x)
 
     dests = set()
 
@@ -1145,7 +1145,7 @@
         # i18n: "limit" is a keyword
         raise error.ParseError(_("limit expects a number"))
     ss = subset
-    os = getset(repo, spanset(repo), l[0])
+    os = getset(repo, fullreposet(repo), l[0])
     result = []
     it = iter(os)
     for x in xrange(lim):
@@ -1172,7 +1172,7 @@
         # i18n: "last" is a keyword
         raise error.ParseError(_("last expects a number"))
     ss = subset
-    os = getset(repo, spanset(repo), l[0])
+    os = getset(repo, fullreposet(repo), l[0])
     os.reverse()
     result = []
     it = iter(os)
@@ -1189,7 +1189,7 @@
     """``max(set)``
     Changeset with highest revision number in set.
     """
-    os = getset(repo, spanset(repo), x)
+    os = getset(repo, fullreposet(repo), x)
     if os:
         m = os.max()
         if m in subset:
@@ -1226,7 +1226,7 @@
     """``min(set)``
     Changeset with lowest revision number in set.
     """
-    os = getset(repo, spanset(repo), x)
+    os = getset(repo, fullreposet(repo), x)
     if os:
         m = os.min()
         if m in subset:
@@ -1322,7 +1322,7 @@
     cl = repo.changelog
     # i18n: "only" is a keyword
     args = getargs(x, 1, 2, _('only takes one or two arguments'))
-    include = getset(repo, spanset(repo), args[0])
+    include = getset(repo, fullreposet(repo), args[0])
     if len(args) == 1:
         if not include:
             return baseset()
@@ -1331,7 +1331,7 @@
         exclude = [rev for rev in cl.headrevs()
             if not rev in descendants and not rev in include]
     else:
-        exclude = getset(repo, spanset(repo), args[1])
+        exclude = getset(repo, fullreposet(repo), args[1])
 
     results = set(cl.findmissingrevs(common=exclude, heads=include))
     return subset & results
@@ -1345,9 +1345,9 @@
     for the first operation is selected.
     """
     if x is not None:
-        dests = getset(repo, spanset(repo), x)
+        dests = getset(repo, fullreposet(repo), x)
     else:
-        dests = getall(repo, spanset(repo), x)
+        dests = getall(repo, fullreposet(repo), x)
 
     def _firstsrc(rev):
         src = _getrevsource(repo, rev)
@@ -1400,7 +1400,7 @@
 
     ps = set()
     cl = repo.changelog
-    for r in getset(repo, spanset(repo), x):
+    for r in getset(repo, fullreposet(repo), x):
         ps.add(cl.parentrevs(r)[0])
     ps -= set([node.nullrev])
     return subset & ps
@@ -1421,7 +1421,7 @@
 
     ps = set()
     cl = repo.changelog
-    for r in getset(repo, spanset(repo), x):
+    for r in getset(repo, fullreposet(repo), x):
         ps.add(cl.parentrevs(r)[1])
     ps -= set([node.nullrev])
     return subset & ps
@@ -1435,7 +1435,7 @@
     else:
         ps = set()
         cl = repo.changelog
-        for r in getset(repo, spanset(repo), x):
+        for r in getset(repo, fullreposet(repo), x):
             ps.update(cl.parentrevs(r))
     ps -= set([node.nullrev])
     return subset & ps
@@ -1548,7 +1548,7 @@
     except (TypeError, ValueError):
         # i18n: "rev" is a keyword
         raise error.ParseError(_("rev expects a number"))
-    if l not in fullreposet(repo) and l != node.nullrev:
+    if l not in repo.changelog and l != node.nullrev:
         return baseset()
     return subset & baseset([l])
 
@@ -1676,7 +1676,7 @@
     """``roots(set)``
     Changesets in set with no parent changeset in set.
     """
-    s = getset(repo, spanset(repo), x)
+    s = getset(repo, fullreposet(repo), x)
     subset = baseset([r for r in s if r in subset])
     cs = _children(repo, subset, s)
     return subset - cs
@@ -2243,6 +2243,71 @@
     except error.ParseError, inst:
         return (decl, None, None, parseerrordetail(inst))
 
+def _parsealiasdefn(defn, args):
+    """Parse alias definition ``defn``
+
+    This function also replaces alias argument references in the
+    specified definition by ``_aliasarg(ARGNAME)``.
+
+    ``args`` is a list of alias argument names, or None if the alias
+    is declared as a symbol.
+
+    This returns "tree" as parsing result.
+
+    >>> args = ['$1', '$2', 'foo']
+    >>> print prettyformat(_parsealiasdefn('$1 or foo', args))
+    (or
+      (func
+        ('symbol', '_aliasarg')
+        ('string', '$1'))
+      (func
+        ('symbol', '_aliasarg')
+        ('string', 'foo')))
+    >>> try:
+    ...     _parsealiasdefn('$1 or $bar', args)
+    ... except error.ParseError, inst:
+    ...     print parseerrordetail(inst)
+    at 6: '$' not for alias arguments
+    >>> args = ['$1', '$10', 'foo']
+    >>> print prettyformat(_parsealiasdefn('$10 or foobar', args))
+    (or
+      (func
+        ('symbol', '_aliasarg')
+        ('string', '$10'))
+      ('symbol', 'foobar'))
+    >>> print prettyformat(_parsealiasdefn('"$1" or "foo"', args))
+    (or
+      ('string', '$1')
+      ('string', 'foo'))
+    """
+    def tokenizedefn(program, lookup=None):
+        if args:
+            argset = set(args)
+        else:
+            argset = set()
+
+        for t, value, pos in _tokenizealias(program, lookup=lookup):
+            if t == 'symbol':
+                if value in argset:
+                    # emulate tokenization of "_aliasarg('ARGNAME')":
+                    # "_aliasarg()" is an unknown symbol only used separate
+                    # alias argument placeholders from regular strings.
+                    yield ('symbol', '_aliasarg', pos)
+                    yield ('(', None, pos)
+                    yield ('string', value, pos)
+                    yield (')', None, pos)
+                    continue
+                elif value.startswith('$'):
+                    raise error.ParseError(_("'$' not for alias arguments"),
+                                           pos)
+            yield (t, value, pos)
+
+    p = parser.parser(tokenizedefn, elements)
+    tree, pos = p.parse(defn)
+    if pos != len(defn):
+        raise error.ParseError(_('invalid token'), pos)
+    return tree
+
 class revsetalias(object):
     # whether own `error` information is already shown or not.
     # this avoids showing same warning multiple times at each `findaliases`.
@@ -2260,16 +2325,8 @@
                            ' "%s": %s') % (self.name, self.error)
             return
 
-        if self.args:
-            for arg in self.args:
-                # _aliasarg() is an unknown symbol only used separate
-                # alias argument placeholders from regular strings.
-                value = value.replace(arg, '_aliasarg(%r)' % (arg,))
-
         try:
-            self.replacement, pos = parse(value)
-            if pos != len(value):
-                raise error.ParseError(_('invalid token'), pos)
+            self.replacement = _parsealiasdefn(value, self.args)
             # Check for placeholder injection
             _checkaliasarg(self.replacement, self.args)
         except error.ParseError, inst:
@@ -2392,7 +2449,9 @@
         tree = findaliases(ui, tree, showwarning=ui.warn)
     tree = foldconcat(tree)
     weight, tree = optimize(tree, True)
-    def mfunc(repo, subset):
+    def mfunc(repo, subset=None):
+        if subset is None:
+            subset = fullreposet(repo)
         if util.safehasattr(subset, 'isascending'):
             result = getset(repo, subset, tree)
         else:
@@ -3146,18 +3205,7 @@
             return it().next()
         return None
 
-def spanset(repo, start=None, end=None):
-    """factory function to dispatch between fullreposet and actual spanset
-
-    Feel free to update all spanset call sites and kill this function at some
-    point.
-    """
-    if start is None and end is None:
-        return fullreposet(repo)
-    return _spanset(repo, start, end)
-
-
-class _spanset(abstractsmartset):
+class spanset(abstractsmartset):
     """Duck type for baseset class which represents a range of revisions and
     can work lazily and without having all the range in memory
 
@@ -3261,7 +3309,7 @@
             return x
         return None
 
-class fullreposet(_spanset):
+class fullreposet(spanset):
     """a set containing all revisions in the repo
 
     This class exists to host special optimization.
--- a/mercurial/scmutil.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/scmutil.py	Mon Mar 02 01:20:14 2015 -0600
@@ -672,11 +672,11 @@
         # fall through to new-style queries if old-style fails
         m = revset.match(repo.ui, spec, repo)
         if seen or l:
-            dl = [r for r in m(repo, revset.spanset(repo)) if r not in seen]
+            dl = [r for r in m(repo) if r not in seen]
             l = l + revset.baseset(dl)
             seen.update(dl)
         else:
-            l = m(repo, revset.spanset(repo))
+            l = m(repo)
 
     return l
 
--- a/mercurial/subrepo.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/subrepo.py	Mon Mar 02 01:20:14 2015 -0600
@@ -626,6 +626,7 @@
                            os.path.join(prefix, self._path), explicitonly,
                            **opts)
 
+    @annotatesubrepoerror
     def addremove(self, m, prefix, opts, dry_run, similarity):
         # In the same way as sub directories are processed, once in a subrepo,
         # always entry any of its subrepos.  Don't corrupt the options that will
@@ -877,13 +878,11 @@
             opts['date'] = None
             opts['rev'] = substate[1]
 
-            pats = []
-            if not opts.get('all'):
-                pats = ['set:modified()']
             self.filerevert(*pats, **opts)
 
         # Update the repo to the revision specified in the given substate
-        self.get(substate, overwrite=True)
+        if not opts.get('dry_run'):
+            self.get(substate, overwrite=True)
 
     def filerevert(self, *pats, **opts):
         ctx = self._repo[opts['rev']]
@@ -1577,6 +1576,25 @@
 
 
     @annotatesubrepoerror
+    def cat(self, match, prefix, **opts):
+        rev = self._state[1]
+        if match.anypats():
+            return 1 #No support for include/exclude yet
+
+        if not match.files():
+            return 1
+
+        for f in match.files():
+            output = self._gitcommand(["show", "%s:%s" % (rev, f)])
+            fp = cmdutil.makefileobj(self._subparent, opts.get('output'),
+                                     self._ctx.node(),
+                                     pathname=os.path.join(prefix, f))
+            fp.write(output)
+            fp.close()
+        return 0
+
+
+    @annotatesubrepoerror
     def status(self, rev2, **opts):
         rev1 = self._state[1]
         if self._gitmissing() or not rev1:
@@ -1673,7 +1691,8 @@
                 util.rename(os.path.join(self._abspath, name),
                             os.path.join(self._abspath, bakname))
 
-        self.get(substate, overwrite=True)
+        if not opts.get('dry_run'):
+            self.get(substate, overwrite=True)
         return []
 
     def shortid(self, revid):
--- a/mercurial/tags.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/tags.py	Mon Mar 02 01:20:14 2015 -0600
@@ -337,7 +337,7 @@
     # them local encoding on input, we would lose info writing them to
     # the cache.
     cachefile.write('\n')
-    for (name, (node, hist)) in cachetags.iteritems():
+    for (name, (node, hist)) in sorted(cachetags.iteritems()):
         for n in hist:
             cachefile.write("%s %s\n" % (hex(n), name))
         cachefile.write("%s %s\n" % (hex(node), name))
--- a/mercurial/templater.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templater.py	Mon Mar 02 01:20:14 2015 -0600
@@ -393,7 +393,7 @@
 
     def query(expr):
         m = revsetmod.match(repo.ui, expr)
-        return m(repo, revsetmod.spanset(repo))
+        return m(repo)
 
     if len(args) > 1:
         formatargs = list([a[0](context, mapping, a[1]) for a in args[1:]])
--- a/mercurial/templates/gitweb/map	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/gitweb/map	Mon Mar 02 01:20:14 2015 -0600
@@ -140,7 +140,7 @@
   <tr>
     <td>parent {rev}</td>
     <td style="font-family:monospace">
-      {changesetlink} {ifeq(node, basenode, '(current diff)', \'({difffrom})\')}
+      {changesetlink} {ifeq(node, basenode, '(current diff)', '({difffrom})')}
     </td>
   </tr>'
 difffrom = '<a href="{url|urlescape}rev/{node|short}:{originalnode|short}{sessionvars%urlparameter}">diff</a>'
--- a/mercurial/templates/monoblue/bookmarks.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/monoblue/bookmarks.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -26,7 +26,7 @@
             <li class="current">bookmarks</li>
             <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
             <li><a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a></li>
-	    <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
+            <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
         </ul>
     </div>
 
--- a/mercurial/templates/monoblue/branches.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/monoblue/branches.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -26,7 +26,7 @@
             <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
             <li class="current">branches</li>
             <li><a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a></li>
-	    <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
+            <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
         </ul>
     </div>
 
--- a/mercurial/templates/monoblue/changelog.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/monoblue/changelog.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -27,7 +27,7 @@
             <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
             <li><a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a></li>
             {archives%archiveentry}
-	    <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
+            <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
         </ul>
     </div>
 
--- a/mercurial/templates/monoblue/graph.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/monoblue/graph.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -27,7 +27,7 @@
             <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
             <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
             <li><a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a></li>
-	    <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
+            <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
         </ul>
     </div>
 
--- a/mercurial/templates/monoblue/help.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/monoblue/help.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -26,7 +26,7 @@
             <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
             <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
             <li><a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a></li>
-	    <li class="current">help</li>
+            <li class="current">help</li>
         </ul>
     </div>
 
--- a/mercurial/templates/monoblue/helptopics.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/monoblue/helptopics.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -26,7 +26,7 @@
             <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
             <li><a href="{url|urlescape}help{sessionvars%urlparameter}">branches</a></li>
             <li><a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a></li>
-	    <li class="current">help</li>
+            <li class="current">help</li>
         </ul>
     </div>
 
--- a/mercurial/templates/monoblue/manifest.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/monoblue/manifest.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -26,7 +26,7 @@
             <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
             <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
             <li class="current">files</li>
-	    <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
+            <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
         </ul>
     </div>
 
--- a/mercurial/templates/monoblue/map	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/monoblue/map	Mon Mar 02 01:20:14 2015 -0600
@@ -93,7 +93,7 @@
   <tr class="parity{parity}">
     <td class="linenr">
       <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}"
-	 title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>
+         title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>
     </td>
     <td class="lineno">
       <a href="#{lineid}" id="{lineid}">{linenumber}</a>
@@ -129,7 +129,7 @@
   <dd>{changesetlink}</dd>'
 changesetparentdiff = '
   <dt>parent {rev}</dt>
-  <dd>{changesetlink} {ifeq(node, basenode, '(current diff)', \'({difffrom})\')}</dd>'
+  <dd>{changesetlink} {ifeq(node, basenode, '(current diff)', '({difffrom})')}</dd>'
 difffrom = '<a href="{url|urlescape}rev/{node|short}:{originalnode|short}{sessionvars%urlparameter}">diff</a>'
 filerevbranch = '<dt>branch</dt><dd>{name|escape}</dd>'
 filerevparent = '
--- a/mercurial/templates/monoblue/shortlog.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/monoblue/shortlog.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -26,8 +26,8 @@
             <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
             <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
             <li><a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a></li>
-	    {archives%archiveentry}
-	    <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
+            {archives%archiveentry}
+            <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
         </ul>
     </div>
 
--- a/mercurial/templates/monoblue/summary.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/monoblue/summary.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -26,7 +26,7 @@
             <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
             <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
             <li><a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a></li>
-	    <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
+            <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
         </ul>
     </div>
 
--- a/mercurial/templates/monoblue/tags.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/monoblue/tags.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -26,7 +26,7 @@
             <li><a href="{url|urlescape}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
             <li><a href="{url|urlescape}branches{sessionvars%urlparameter}">branches</a></li>
             <li><a href="{url|urlescape}file/{node|short}{sessionvars%urlparameter}">files</a></li>
-	    <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
+            <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
         </ul>
     </div>
 
--- a/mercurial/templates/paper/bookmarks.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/paper/bookmarks.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -23,7 +23,6 @@
 <ul>
 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
 </ul>
-<p></p>
 <div class="atom-logo">
 <a href="{url|urlescape}atom-bookmarks" title="subscribe to atom feed">
 <img class="atom-logo" src="{staticurl|urlescape}feed-icon-14x14.png" alt="atom feed" />
@@ -42,10 +41,12 @@
 </form>
 
 <table class="bigtable">
+<thead>
 <tr>
  <th>bookmark</th>
  <th>node</th>
 </tr>
+</thead>
 <tbody class="stripes2">
 {entries%bookmarkentry}
 </tbody>
--- a/mercurial/templates/paper/branches.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/paper/branches.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -23,7 +23,6 @@
 <ul>
  <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
 </ul>
-<p></p>
 <div class="atom-logo">
 <a href="{url|urlescape}atom-branches" title="subscribe to atom feed">
 <img class="atom-logo" src="{staticurl|urlescape}feed-icon-14x14.png" alt="atom feed" />
@@ -42,10 +41,12 @@
 </form>
 
 <table class="bigtable">
+<thead>
 <tr>
  <th>branch</th>
  <th>node</th>
 </tr>
+</thead>
 <tbody class="stripes2">
 {entries % branchentry}
 </tbody>
--- a/mercurial/templates/paper/changeset.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/paper/changeset.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -48,7 +48,8 @@
 </tr>
 <tr>
  <th class="date">date</th>
- <td class="date age">{date|rfc822date}</td></tr>
+ <td class="date age">{date|rfc822date}</td>
+</tr>
 <tr>
  <th class="author">parents</th>
  <td class="author">{ifeq(count(parent), '2', parent%changesetparentdiff, parent%changesetparent)}</td>
@@ -68,8 +69,7 @@
     <a id="diffstatexpand" href="javascript:toggleDiffstat()">[<tt>+</tt>]</a>
     <div id="diffstatdetails" style="display:none;">
       <a href="javascript:toggleDiffstat()">[<tt>-</tt>]</a>
-      <p></p>
-      <table class="stripes2">{diffstat}</table>
+      <table class="diffstat-table stripes2">{diffstat}</table>
     </div>
   </td>
 </tr>
--- a/mercurial/templates/paper/fileannotate.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/paper/fileannotate.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -68,10 +68,12 @@
 
 <div class="overflow">
 <table class="bigtable">
+<thead>
 <tr>
  <th class="annotate">rev</th>
  <th class="line">&nbsp;&nbsp;line source</th>
 </tr>
+</thead>
 <tbody class="stripes2">
   {annotate%annotateline}
 </tbody>
--- a/mercurial/templates/paper/filelog.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/paper/filelog.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -35,7 +35,6 @@
 <ul>
 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
 </ul>
-<p></p>
 <div class="atom-logo">
 <a href="{url|urlescape}atom-log/{node|short}/{file|urlescape}" title="subscribe to atom feed">
 <img class="atom-logo" src="{staticurl|urlescape}feed-icon-14x14.png" alt="atom feed" />
@@ -59,11 +58,13 @@
 | {nav%filenav}</div>
 
 <table class="bigtable">
+<thead>
  <tr>
   <th class="age">age</th>
   <th class="author">author</th>
   <th class="description">description</th>
  </tr>
+</thead>
 <tbody class="stripes2">
 {entries%filelogentry}
 </tbody>
--- a/mercurial/templates/paper/graph.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/paper/graph.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -28,7 +28,6 @@
 <ul>
  <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
 </ul>
-<p></p>
 <div class="atom-logo">
 <a href="{url|urlescape}atom-log" title="subscribe to atom feed">
 <img class="atom-logo" src="{staticurl|urlescape}feed-icon-14x14.png" alt="atom feed" />
--- a/mercurial/templates/paper/index.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/paper/index.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -12,6 +12,7 @@
 <h2 class="breadcrumb"><a href="/">Mercurial</a> {pathdef%breadcrumb}</h2>
 
 <table class="bigtable">
+    <thead>
     <tr>
         <th><a href="?sort={sort_name}">Name</a></th>
         <th><a href="?sort={sort_description}">Description</a></th>
@@ -20,6 +21,7 @@
         <th>&nbsp;</th>
         <th>&nbsp;</th>
     </tr>
+    </thead>
     <tbody class="stripes2">
     {entries%indexentry}
     </tbody>
--- a/mercurial/templates/paper/manifest.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/paper/manifest.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -39,11 +39,13 @@
 </form>
 
 <table class="bigtable">
+<thead>
 <tr>
   <th class="name">name</th>
   <th class="size">size</th>
   <th class="permissions">permissions</th>
 </tr>
+</thead>
 <tbody class="stripes2">
 <tr class="fileline">
   <td class="name"><a href="{url|urlescape}file/{node|short}{up|urlescape}{sessionvars%urlparameter}">[up]</a></td>
--- a/mercurial/templates/paper/search.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/paper/search.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -43,11 +43,13 @@
 </div>
 
 <table class="bigtable">
+<thead>
  <tr>
   <th class="age">age</th>
   <th class="author">author</th>
   <th class="description">description</th>
  </tr>
+</thead>
 <tbody class="stripes2">
 {entries}
 </tbody>
--- a/mercurial/templates/paper/shortlog.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/paper/shortlog.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -30,7 +30,6 @@
 <ul>
  <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
 </ul>
-<p></p>
 <div class="atom-logo">
 <a href="{url|urlescape}atom-log" title="subscribe to atom feed">
 <img class="atom-logo" src="{staticurl|urlescape}feed-icon-14x14.png" alt="atom feed" />
@@ -55,11 +54,13 @@
 </div>
 
 <table class="bigtable">
+<thead>
  <tr>
   <th class="age">age</th>
   <th class="author">author</th>
   <th class="description">description</th>
  </tr>
+</thead>
 <tbody class="stripes2">
 {entries%shortlogentry}
 </tbody>
--- a/mercurial/templates/paper/tags.tmpl	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/paper/tags.tmpl	Mon Mar 02 01:20:14 2015 -0600
@@ -23,7 +23,6 @@
 <ul>
 <li><a href="{url|urlescape}help{sessionvars%urlparameter}">help</a></li>
 </ul>
-<p></p>
 <div class="atom-logo">
 <a href="{url|urlescape}atom-tags" title="subscribe to atom feed">
 <img class="atom-logo" src="{staticurl|urlescape}feed-icon-14x14.png" alt="atom feed" />
@@ -42,10 +41,12 @@
 </form>
 
 <table class="bigtable">
+<thead>
 <tr>
  <th>tag</th>
  <th>node</th>
 </tr>
+</thead>
 <tbody class="stripes2">
 {entries%tagentry}
 </tbody>
--- a/mercurial/templates/static/style-paper.css	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/templates/static/style-paper.css	Mon Mar 02 01:20:14 2015 -0600
@@ -60,6 +60,10 @@
   border: 0;
 }
 
+div.atom-logo {
+  margin-top: 10px;
+}
+
 .atom-logo img{
   width: 14px;
   height: 14px;
@@ -104,6 +108,9 @@
 .minusline { color: #dc143c; } /* crimson */
 .atline { color: purple; }
 
+.diffstat-table {
+  margin-top: 1em;
+}
 .diffstat-file {
   white-space: nowrap;
   font-size: 90%;
--- a/mercurial/unionrepo.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/unionrepo.py	Mon Mar 02 01:20:14 2015 -0600
@@ -160,8 +160,11 @@
     def baserevdiff(self, rev1, rev2):
         return filelog.filelog.revdiff(self, rev1, rev2)
 
-    def _file(self, f):
-        self._repo.file(f)
+    def iscensored(self, rev):
+        """Check if a revision is censored."""
+        if rev <= self.repotiprev:
+            return filelog.filelog.iscensored(self, rev)
+        return self.revlog2.iscensored(rev)
 
 class unionpeer(localrepo.localpeer):
     def canpush(self):
--- a/mercurial/util.h	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/util.h	Mon Mar 02 01:20:14 2015 -0600
@@ -172,6 +172,22 @@
 		(d[3]));
 }
 
+static inline int16_t getbeint16(const char *c)
+{
+	const unsigned char *d = (const unsigned char *)c;
+
+	return ((d[0] << 8) |
+		(d[1]));
+}
+
+static inline uint16_t getbeuint16(const char *c)
+{
+	const unsigned char *d = (const unsigned char *)c;
+
+	return ((d[0] << 8) |
+		(d[1]));
+}
+
 static inline void putbe32(uint32_t x, char *c)
 {
 	c[0] = (x >> 24) & 0xff;
@@ -180,4 +196,17 @@
 	c[3] = (x) & 0xff;
 }
 
+static inline double getbefloat64(const char *c)
+{
+	const unsigned char *d = (const unsigned char *)c;
+	double ret;
+	int i;
+	uint64_t t = 0;
+	for (i = 0; i < 8; i++) {
+		t = (t<<8) + d[i];
+	}
+	memcpy(&ret, &t, sizeof(t));
+	return ret;
+}
+
 #endif /* _HG_UTIL_H_ */
--- a/mercurial/windows.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/mercurial/windows.py	Mon Mar 02 01:20:14 2015 -0600
@@ -26,14 +26,22 @@
 unlink = win32.unlink
 
 umask = 0022
+_SEEK_END = 2 # os.SEEK_END was introduced in Python 2.5
 
-# wrap osutil.posixfile to provide friendlier exceptions
 def posixfile(name, mode='r', buffering=-1):
+    '''Open a file with even more POSIX-like semantics'''
     try:
-        return osutil.posixfile(name, mode, buffering)
+        fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
+
+        # The position when opening in append mode is implementation defined, so
+        # make it consistent with other platforms, which position at EOF.
+        if 'a' in mode:
+            fp.seek(0, _SEEK_END)
+
+        return fp
     except WindowsError, err:
+        # convert to a friendlier exception
         raise IOError(err.errno, '%s: %s' % (name, err.strerror))
-posixfile.__doc__ = osutil.posixfile.__doc__
 
 class winstdout(object):
     '''stdout on windows misbehaves if sent through a pipe'''
--- a/tests/run-tests.py	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/run-tests.py	Mon Mar 02 01:20:14 2015 -0600
@@ -1863,6 +1863,17 @@
                   'prefix': self._installdir, 'libdir': self._pythondir,
                   'bindir': self._bindir,
                   'nohome': nohome, 'logfile': installerrs})
+
+        # setuptools requires install directories to exist.
+        def makedirs(p):
+            try:
+                os.makedirs(p)
+            except OSError, e:
+                if e.errno != errno.EEXIST:
+                    raise
+        makedirs(self._pythondir)
+        makedirs(self._bindir)
+
         vlog("# Running", cmd)
         if os.system(cmd) == 0:
             if not self.options.verbose:
@@ -1870,7 +1881,7 @@
         else:
             f = open(installerrs, 'rb')
             for line in f:
-                print line
+                sys.stdout.write(line)
             f.close()
             sys.exit(1)
         os.chdir(self._testdir)
--- a/tests/test-bundle-type.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-bundle-type.t	Mon Mar 02 01:20:14 2015 -0600
@@ -87,6 +87,7 @@
   $ hg init tgarbage
   $ cd tgarbage
   $ hg pull ../bgarbage
+  pulling from ../bgarbage
   abort: ../bgarbage: not a Mercurial bundle
   [255]
   $ cd ..
--- a/tests/test-churn.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-churn.t	Mon Mar 02 01:20:14 2015 -0600
@@ -171,4 +171,27 @@
   El Ni\xc3\xb1o         1 *************** (esc)
   with space      1 ***************
 
+Test --template argument, with backwards compatiblity
+
+  $ hg churn -t '{author|user}'
+  user1      4 ***************************************************************
+  user3      3 ***********************************************
+  user2      2 ********************************
+  nino       1 ****************
+  with       1 ****************
+             0 
+  user4      0 
+  $ hg churn -T '{author|user}'
+  user1      4 ***************************************************************
+  user3      3 ***********************************************
+  user2      2 ********************************
+  nino       1 ****************
+  with       1 ****************
+             0 
+  user4      0 
+  $ hg churn -t 'alltogether'
+  alltogether     11 *********************************************************
+  $ hg churn -T 'alltogether'
+  alltogether     11 *********************************************************
+
   $ cd ..
--- a/tests/test-completion.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-completion.t	Mon Mar 02 01:20:14 2015 -0600
@@ -278,7 +278,7 @@
   phase: public, draft, secret, force, rev
   recover: 
   rename: after, force, include, exclude, dry-run
-  resolve: all, list, mark, unmark, no-status, tool, include, exclude
+  resolve: all, list, mark, unmark, no-status, tool, include, exclude, template
   revert: all, date, rev, no-backup, include, exclude, dry-run
   rollback: dry-run, force
   root: 
--- a/tests/test-extension.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-extension.t	Mon Mar 02 01:20:14 2015 -0600
@@ -1140,3 +1140,27 @@
   C sub3/3
 
   $ cd ..
+
+Test synopsis and docstring extending
+
+  $ hg init exthelp
+  $ cat > exthelp.py <<EOF
+  > from mercurial import commands, extensions
+  > def exbookmarks(orig, *args, **opts):
+  >     return orig(*args, **opts)
+  > def uisetup(ui):
+  >     synopsis = ' GREPME [--foo] [-x]'
+  >     docstring = '''
+  >     GREPME make sure that this is in the help!
+  >     '''
+  >     extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks,
+  >                            synopsis, docstring)
+  > EOF
+  $ abspath=`pwd`/exthelp.py
+  $ echo '[extensions]' >> $HGRCPATH
+  $ echo "exthelp = $abspath" >> $HGRCPATH
+  $ cd exthelp
+  $ hg help bookmarks | grep GREPME
+  hg bookmarks [OPTIONS]... [NAME]... GREPME [--foo] [-x]
+      GREPME make sure that this is in the help!
+
--- a/tests/test-gendoc.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-gendoc.t	Mon Mar 02 01:20:14 2015 -0600
@@ -1,4 +1,5 @@
 #require docutils
+#require gettext
 
 Test document extraction
 
--- a/tests/test-glog.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-glog.t	Mon Mar 02 01:20:14 2015 -0600
@@ -1541,6 +1541,9 @@
   $ testlog --follow
   []
   []
+  $ testlog -rnull
+  ['null']
+  []
   $ echo a > a
   $ echo aa > aa
   $ echo f > f
@@ -1764,6 +1767,13 @@
   nodetag 1
   nodetag 0
 
+Test --follow null parent
+
+  $ hg up -q null
+  $ testlog -f
+  []
+  []
+
 Test --follow-first
 
   $ hg up -q 3
--- a/tests/test-grep.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-grep.t	Mon Mar 02 01:20:14 2015 -0600
@@ -82,6 +82,10 @@
   port:1:2:+:eggs:export
   port:0:1:+:spam:import
 
+  $ hg up -q null
+  $ hg grep -f port
+  [1]
+
   $ cd ..
   $ hg init t2
   $ cd t2
--- a/tests/test-help.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-help.t	Mon Mar 02 01:20:14 2015 -0600
@@ -1101,6 +1101,125 @@
   abort: help section not found
   [255]
 
+Test dynamic list of merge tools only shows up once
+  $ hg help merge-tools
+  Merge Tools
+  """""""""""
+  
+      To merge files Mercurial uses merge tools.
+  
+      A merge tool combines two different versions of a file into a merged file.
+      Merge tools are given the two files and the greatest common ancestor of
+      the two file versions, so they can determine the changes made on both
+      branches.
+  
+      Merge tools are used both for "hg resolve", "hg merge", "hg update", "hg
+      backout" and in several extensions.
+  
+      Usually, the merge tool tries to automatically reconcile the files by
+      combining all non-overlapping changes that occurred separately in the two
+      different evolutions of the same initial base file. Furthermore, some
+      interactive merge programs make it easier to manually resolve conflicting
+      merges, either in a graphical way, or by inserting some conflict markers.
+      Mercurial does not include any interactive merge programs but relies on
+      external tools for that.
+  
+      Available merge tools
+      =====================
+  
+      External merge tools and their properties are configured in the merge-
+      tools configuration section - see hgrc(5) - but they can often just be
+      named by their executable.
+  
+      A merge tool is generally usable if its executable can be found on the
+      system and if it can handle the merge. The executable is found if it is an
+      absolute or relative executable path or the name of an application in the
+      executable search path. The tool is assumed to be able to handle the merge
+      if it can handle symlinks if the file is a symlink, if it can handle
+      binary files if the file is binary, and if a GUI is available if the tool
+      requires a GUI.
+  
+      There are some internal merge tools which can be used. The internal merge
+      tools are:
+  
+      ":dump"
+        Creates three versions of the files to merge, containing the contents of
+        local, other and base. These files can then be used to perform a merge
+        manually. If the file to be merged is named "a.txt", these files will
+        accordingly be named "a.txt.local", "a.txt.other" and "a.txt.base" and
+        they will be placed in the same directory as "a.txt".
+  
+      ":fail"
+        Rather than attempting to merge files that were modified on both
+        branches, it marks them as unresolved. The resolve command must be used
+        to resolve these conflicts.
+  
+      ":local"
+        Uses the local version of files as the merged version.
+  
+      ":merge"
+        Uses the internal non-interactive simple merge algorithm for merging
+        files. It will fail if there are any conflicts and leave markers in the
+        partially merged file. Markers will have two sections, one for each side
+        of merge.
+  
+      ":merge3"
+        Uses the internal non-interactive simple merge algorithm for merging
+        files. It will fail if there are any conflicts and leave markers in the
+        partially merged file. Marker will have three sections, one from each
+        side of the merge and one for the base content.
+  
+      ":other"
+        Uses the other version of files as the merged version.
+  
+      ":prompt"
+        Asks the user which of the local or the other version to keep as the
+        merged version.
+  
+      ":tagmerge"
+        Uses the internal tag merge algorithm (experimental).
+  
+      Internal tools are always available and do not require a GUI but will by
+      default not handle symlinks or binary files.
+  
+      Choosing a merge tool
+      =====================
+  
+      Mercurial uses these rules when deciding which merge tool to use:
+  
+      1. If a tool has been specified with the --tool option to merge or
+         resolve, it is used.  If it is the name of a tool in the merge-tools
+         configuration, its configuration is used. Otherwise the specified tool
+         must be executable by the shell.
+      2. If the "HGMERGE" environment variable is present, its value is used and
+         must be executable by the shell.
+      3. If the filename of the file to be merged matches any of the patterns in
+         the merge-patterns configuration section, the first usable merge tool
+         corresponding to a matching pattern is used. Here, binary capabilities
+         of the merge tool are not considered.
+      4. If ui.merge is set it will be considered next. If the value is not the
+         name of a configured tool, the specified value is used and must be
+         executable by the shell. Otherwise the named tool is used if it is
+         usable.
+      5. If any usable merge tools are present in the merge-tools configuration
+         section, the one with the highest priority is used.
+      6. If a program named "hgmerge" can be found on the system, it is used -
+         but it will by default not be used for symlinks and binary files.
+      7. If the file to be merged is not binary and is not a symlink, then
+         internal ":merge" is used.
+      8. The merge of the file fails and must be resolved before commit.
+  
+      Note:
+         After selecting a merge program, Mercurial will by default attempt to
+         merge the files using a simple merge algorithm first. Only if it
+         doesn't succeed because of conflicting changes Mercurial will actually
+         execute the merge program. Whether to use the simple merge algorithm
+         first can be controlled by the premerge setting of the merge tool.
+         Premerge is enabled by default unless the file is binary or a symlink.
+  
+      See the merge-tools and ui sections of hgrc(5) for details on the
+      configuration of merge tools.
+
 Test usage of section marks in help documents
 
   $ cd "$TESTDIR"/../doc
--- a/tests/test-hgweb-commands.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-hgweb-commands.t	Mon Mar 02 01:20:14 2015 -0600
@@ -726,7 +726,6 @@
   <ul>
    <li><a href="/help">help</a></li>
   </ul>
-  <p></p>
   <div class="atom-logo">
   <a href="/atom-log" title="subscribe to atom feed">
   <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
@@ -752,11 +751,13 @@
   </div>
   
   <table class="bigtable">
+  <thead>
    <tr>
     <th class="age">age</th>
     <th class="author">author</th>
     <th class="description">description</th>
    </tr>
+  </thead>
   <tbody class="stripes2">
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
@@ -873,7 +874,8 @@
   </tr>
   <tr>
    <th class="date">date</th>
-   <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td></tr>
+   <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
+  </tr>
   <tr>
    <th class="author">parents</th>
    <td class="author"></td>
@@ -894,8 +896,7 @@
       <a id="diffstatexpand" href="javascript:toggleDiffstat()">[<tt>+</tt>]</a>
       <div id="diffstatdetails" style="display:none;">
         <a href="javascript:toggleDiffstat()">[<tt>-</tt>]</a>
-        <p></p>
-        <table class="stripes2">  <tr>
+        <table class="diffstat-table stripes2">  <tr>
       <td class="diffstat-file"><a href="#l1.1">da/foo</a></td>
       <td class="diffstat-total" align="right">1</td>
       <td class="diffstat-graph">
@@ -1012,11 +1013,13 @@
   </div>
   
   <table class="bigtable">
+  <thead>
    <tr>
     <th class="age">age</th>
     <th class="author">author</th>
     <th class="description">description</th>
    </tr>
+  </thead>
   <tbody class="stripes2">
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
--- a/tests/test-hgweb-descend-empties.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-hgweb-descend-empties.t	Mon Mar 02 01:20:14 2015 -0600
@@ -81,11 +81,13 @@
   </form>
   
   <table class="bigtable">
+  <thead>
   <tr>
     <th class="name">name</th>
     <th class="size">size</th>
     <th class="permissions">permissions</th>
   </tr>
+  </thead>
   <tbody class="stripes2">
   <tr class="fileline">
     <td class="name"><a href="/file/9087c84a0f5d/">[up]</a></td>
--- a/tests/test-hgweb-diffs.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-hgweb-diffs.t	Mon Mar 02 01:20:14 2015 -0600
@@ -97,7 +97,8 @@
   </tr>
   <tr>
    <th class="date">date</th>
-   <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td></tr>
+   <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
+  </tr>
   <tr>
    <th class="author">parents</th>
    <td class="author"></td>
@@ -118,8 +119,7 @@
       <a id="diffstatexpand" href="javascript:toggleDiffstat()">[<tt>+</tt>]</a>
       <div id="diffstatdetails" style="display:none;">
         <a href="javascript:toggleDiffstat()">[<tt>-</tt>]</a>
-        <p></p>
-        <table class="stripes2">  <tr>
+        <table class="diffstat-table stripes2">  <tr>
       <td class="diffstat-file"><a href="#l1.1">a</a></td>
       <td class="diffstat-total" align="right">1</td>
       <td class="diffstat-graph">
@@ -369,7 +369,8 @@
   </tr>
   <tr>
    <th class="date">date</th>
-   <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td></tr>
+   <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
+  </tr>
   <tr>
    <th class="author">parents</th>
    <td class="author"></td>
@@ -390,8 +391,7 @@
       <a id="diffstatexpand" href="javascript:toggleDiffstat()">[<tt>+</tt>]</a>
       <div id="diffstatdetails" style="display:none;">
         <a href="javascript:toggleDiffstat()">[<tt>-</tt>]</a>
-        <p></p>
-        <table class="stripes2">  <tr>
+        <table class="diffstat-table stripes2">  <tr>
       <td class="diffstat-file"><a href="#l1.1">a</a></td>
       <td class="diffstat-total" align="right">1</td>
       <td class="diffstat-graph">
--- a/tests/test-hgweb-empty.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-hgweb-empty.t	Mon Mar 02 01:20:14 2015 -0600
@@ -48,7 +48,6 @@
   <ul>
    <li><a href="/help">help</a></li>
   </ul>
-  <p></p>
   <div class="atom-logo">
   <a href="/atom-log" title="subscribe to atom feed">
   <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
@@ -74,11 +73,13 @@
   </div>
   
   <table class="bigtable">
+  <thead>
    <tr>
     <th class="age">age</th>
     <th class="author">author</th>
     <th class="description">description</th>
    </tr>
+  </thead>
   <tbody class="stripes2">
   
   </tbody>
@@ -158,7 +159,6 @@
   <ul>
    <li><a href="/help">help</a></li>
   </ul>
-  <p></p>
   <div class="atom-logo">
   <a href="/atom-log" title="subscribe to atom feed">
   <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
@@ -184,11 +184,13 @@
   </div>
   
   <table class="bigtable">
+  <thead>
    <tr>
     <th class="age">age</th>
     <th class="author">author</th>
     <th class="description">description</th>
    </tr>
+  </thead>
   <tbody class="stripes2">
   
   </tbody>
@@ -264,7 +266,6 @@
   <ul>
    <li><a href="/help">help</a></li>
   </ul>
-  <p></p>
   <div class="atom-logo">
   <a href="/atom-log" title="subscribe to atom feed">
   <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
@@ -431,11 +432,13 @@
   </form>
   
   <table class="bigtable">
+  <thead>
   <tr>
     <th class="name">name</th>
     <th class="size">size</th>
     <th class="permissions">permissions</th>
   </tr>
+  </thead>
   <tbody class="stripes2">
   <tr class="fileline">
     <td class="name"><a href="/file/000000000000/">[up]</a></td>
--- a/tests/test-hgweb-filelog.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-hgweb-filelog.t	Mon Mar 02 01:20:14 2015 -0600
@@ -156,7 +156,6 @@
   <ul>
   <li><a href="/help">help</a></li>
   </ul>
-  <p></p>
   <div class="atom-logo">
   <a href="/atom-log/01de2d66a28d/a" title="subscribe to atom feed">
   <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
@@ -181,11 +180,13 @@
   | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
   
   <table class="bigtable">
+  <thead>
    <tr>
     <th class="age">age</th>
     <th class="author">author</th>
     <th class="description">description</th>
    </tr>
+  </thead>
   <tbody class="stripes2">
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
@@ -266,7 +267,6 @@
   <ul>
   <li><a href="/help">help</a></li>
   </ul>
-  <p></p>
   <div class="atom-logo">
   <a href="/atom-log/01de2d66a28d/a" title="subscribe to atom feed">
   <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
@@ -291,11 +291,13 @@
   | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
   
   <table class="bigtable">
+  <thead>
    <tr>
     <th class="age">age</th>
     <th class="author">author</th>
     <th class="description">description</th>
    </tr>
+  </thead>
   <tbody class="stripes2">
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
@@ -376,7 +378,6 @@
   <ul>
   <li><a href="/help">help</a></li>
   </ul>
-  <p></p>
   <div class="atom-logo">
   <a href="/atom-log/5ed941583260/a" title="subscribe to atom feed">
   <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
@@ -401,11 +402,13 @@
   | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
   
   <table class="bigtable">
+  <thead>
    <tr>
     <th class="age">age</th>
     <th class="author">author</th>
     <th class="description">description</th>
    </tr>
+  </thead>
   <tbody class="stripes2">
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
@@ -481,7 +484,6 @@
   <ul>
   <li><a href="/help">help</a></li>
   </ul>
-  <p></p>
   <div class="atom-logo">
   <a href="/atom-log/5ed941583260/a" title="subscribe to atom feed">
   <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
@@ -506,11 +508,13 @@
   | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
   
   <table class="bigtable">
+  <thead>
    <tr>
     <th class="age">age</th>
     <th class="author">author</th>
     <th class="description">description</th>
    </tr>
+  </thead>
   <tbody class="stripes2">
    <tr>
     <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
--- a/tests/test-hgweb-removed.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-hgweb-removed.t	Mon Mar 02 01:20:14 2015 -0600
@@ -78,7 +78,8 @@
   </tr>
   <tr>
    <th class="date">date</th>
-   <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td></tr>
+   <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
+  </tr>
   <tr>
    <th class="author">parents</th>
    <td class="author"><a href="/rev/cb9a9f314b8b">cb9a9f314b8b</a> </td>
@@ -99,8 +100,7 @@
       <a id="diffstatexpand" href="javascript:toggleDiffstat()">[<tt>+</tt>]</a>
       <div id="diffstatdetails" style="display:none;">
         <a href="javascript:toggleDiffstat()">[<tt>-</tt>]</a>
-        <p></p>
-        <table class="stripes2">  <tr>
+        <table class="diffstat-table stripes2">  <tr>
       <td class="diffstat-file"><a href="#l1.1">a</a></td>
       <td class="diffstat-total" align="right">1</td>
       <td class="diffstat-graph">
--- a/tests/test-hgweb.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-hgweb.t	Mon Mar 02 01:20:14 2015 -0600
@@ -272,11 +272,13 @@
   </form>
   
   <table class="bigtable">
+  <thead>
   <tr>
     <th class="name">name</th>
     <th class="size">size</th>
     <th class="permissions">permissions</th>
   </tr>
+  </thead>
   <tbody class="stripes2">
   <tr class="fileline">
     <td class="name"><a href="/file/2ef0ac749a14/">[up]</a></td>
--- a/tests/test-hgwebdir.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-hgwebdir.t	Mon Mar 02 01:20:14 2015 -0600
@@ -201,6 +201,7 @@
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   
   <table class="bigtable">
+      <thead>
       <tr>
           <th><a href="?sort=name">Name</a></th>
           <th><a href="?sort=description">Description</a></th>
@@ -209,6 +210,7 @@
           <th>&nbsp;</th>
           <th>&nbsp;</th>
       </tr>
+      </thead>
       <tbody class="stripes2">
       
   <tr>
@@ -699,6 +701,7 @@
   <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/t">t</a> </h2>
   
   <table class="bigtable">
+      <thead>
       <tr>
           <th><a href="?sort=name">Name</a></th>
           <th><a href="?sort=description">Description</a></th>
@@ -707,6 +710,7 @@
           <th>&nbsp;</th>
           <th>&nbsp;</th>
       </tr>
+      </thead>
       <tbody class="stripes2">
       
   <tr>
@@ -1128,6 +1132,7 @@
   <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
   
   <table class="bigtable">
+      <thead>
       <tr>
           <th><a href="?sort=name">Name</a></th>
           <th><a href="?sort=description">Description</a></th>
@@ -1136,6 +1141,7 @@
           <th>&nbsp;</th>
           <th>&nbsp;</th>
       </tr>
+      </thead>
       <tbody class="stripes2">
       
       </tbody>
--- a/tests/test-highlight.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-highlight.t	Mon Mar 02 01:20:14 2015 -0600
@@ -268,10 +268,12 @@
   
   <div class="overflow">
   <table class="bigtable">
+  <thead>
   <tr>
    <th class="annotate">rev</th>
    <th class="line">&nbsp;&nbsp;line source</th>
   </tr>
+  </thead>
   <tbody class="stripes2">
     
   <tr id="l1">
--- a/tests/test-histedit-arguments.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-histedit-arguments.t	Mon Mar 02 01:20:14 2015 -0600
@@ -103,6 +103,15 @@
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg up --quiet
 
+Test config specified default
+-----------------------------
+
+  $ HGEDITOR=cat hg histedit --config "histedit.defaultrev=only(.) - ::eb57da33312f" --commands - << EOF
+  > pick c8e68270e35a 3 four
+  > pick 08d98a8350f3 4 five
+  > EOF
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
 Run on a revision not descendants of the initial parent
 --------------------------------------------------------------------
 
--- a/tests/test-histedit-drop.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-histedit-drop.t	Mon Mar 02 01:20:14 2015 -0600
@@ -96,7 +96,6 @@
 Check histedit_source
 
   $ hg log --debug --rev f518305ce889
-  invalid branchheads cache (visible): tip differs
   changeset:   4:f518305ce889c07cb5bd05522176d75590ef3324
   tag:         tip
   phase:       draft
--- a/tests/test-histedit-edit.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-histedit-edit.t	Mon Mar 02 01:20:14 2015 -0600
@@ -3,13 +3,14 @@
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
   > histedit=
+  > strip=
   > EOF
 
   $ initrepo ()
   > {
   >     hg init r
   >     cd r
-  >     for x in a b c d e f ; do
+  >     for x in a b c d e f g; do
   >         echo $x > $x
   >         hg add $x
   >         hg ci -m $x
@@ -20,10 +21,15 @@
 
 log before edit
   $ hg log --graph
-  @  changeset:   5:652413bf663e
+  @  changeset:   6:3c6a8ed2ebe8
   |  tag:         tip
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     g
+  |
+  o  changeset:   5:652413bf663e
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     f
   |
   o  changeset:   4:e860deea161a
@@ -58,11 +64,19 @@
   > pick 055a42cdd887 d
   > edit e860deea161a e
   > pick 652413bf663e f
+  > pick 3c6a8ed2ebe8 g
   > EOF
-  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
   Make changes as needed, you may commit or record as needed now.
   When you are finished, run hg histedit --continue to resume.
 
+edit the plan
+  $ hg histedit --edit-plan --commands - 2>&1 << EOF
+  > edit e860deea161a e
+  > pick 652413bf663e f
+  > drop 3c6a8ed2ebe8 g
+  > EOF
+
 Go at a random point and try to continue
 
   $ hg id -n
@@ -72,6 +86,11 @@
   (use 'hg histedit --continue' or 'hg histedit --abort')
   [255]
 
+Try to delete necessary commit
+  $ hg strip -r 652413bf663e
+  abort: unable to strip 652413bf663ef2a641cab26574e46d5f5a64a55a. Nodes are used by history edit in progress.
+  [255]
+
 commit, then edit the revision
   $ hg ci -m 'wat'
   created new head
--- a/tests/test-https.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-https.t	Mon Mar 02 01:20:14 2015 -0600
@@ -156,8 +156,8 @@
   $ echo '[hooks]' >> .hg/hgrc
   $ echo "changegroup = python \"$TESTDIR/printenv.py\" changegroup" >> .hg/hgrc
   $ hg pull $DISABLEOSXDUMMYCERT
+  pulling from https://localhost:$HGPORT/
   warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
-  pulling from https://localhost:$HGPORT/
   searching for changes
   adding changesets
   adding manifests
@@ -188,28 +188,30 @@
   searching for changes
   no changes found
   $ P=`pwd` hg -R copy-pull pull --insecure
+  pulling from https://localhost:$HGPORT/
   warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
-  pulling from https://localhost:$HGPORT/
   searching for changes
   no changes found
 
 cacert mismatch
 
   $ hg -R copy-pull pull --config web.cacerts=pub.pem https://127.0.0.1:$HGPORT/
+  pulling from https://127.0.0.1:$HGPORT/
   abort: 127.0.0.1 certificate error: certificate is for localhost
   (configure hostfingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca or use --insecure to connect insecurely)
   [255]
   $ hg -R copy-pull pull --config web.cacerts=pub.pem https://127.0.0.1:$HGPORT/ --insecure
+  pulling from https://127.0.0.1:$HGPORT/
   warning: 127.0.0.1 certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
-  pulling from https://127.0.0.1:$HGPORT/
   searching for changes
   no changes found
   $ hg -R copy-pull pull --config web.cacerts=pub-other.pem
+  pulling from https://localhost:$HGPORT/
   abort: error: *certificate verify failed* (glob)
   [255]
   $ hg -R copy-pull pull --config web.cacerts=pub-other.pem --insecure
+  pulling from https://localhost:$HGPORT/
   warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
-  pulling from https://localhost:$HGPORT/
   searching for changes
   no changes found
 
@@ -218,6 +220,7 @@
   $ hg -R test serve -p $HGPORT1 -d --pid-file=hg1.pid --certificate=server-not-yet.pem
   $ cat hg1.pid >> $DAEMON_PIDS
   $ hg -R copy-pull pull --config web.cacerts=pub-not-yet.pem https://localhost:$HGPORT1/
+  pulling from https://localhost:$HGPORT1/
   abort: error: *certificate verify failed* (glob)
   [255]
 
@@ -226,6 +229,7 @@
   $ hg -R test serve -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
   $ cat hg2.pid >> $DAEMON_PIDS
   $ hg -R copy-pull pull --config web.cacerts=pub-expired.pem https://localhost:$HGPORT2/
+  pulling from https://localhost:$HGPORT2/
   abort: error: *certificate verify failed* (glob)
   [255]
 
@@ -267,8 +271,8 @@
 Test unvalidated https through proxy
 
   $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure --traceback
+  pulling from https://localhost:$HGPORT/
   warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
-  pulling from https://localhost:$HGPORT/
   searching for changes
   no changes found
 
@@ -286,8 +290,10 @@
 Test https with cert problems through proxy
 
   $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub-other.pem
+  pulling from https://localhost:$HGPORT/
   abort: error: *certificate verify failed* (glob)
   [255]
   $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub-expired.pem https://localhost:$HGPORT2/
+  pulling from https://localhost:$HGPORT2/
   abort: error: *certificate verify failed* (glob)
   [255]
--- a/tests/test-largefiles-misc.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-largefiles-misc.t	Mon Mar 02 01:20:14 2015 -0600
@@ -362,6 +362,14 @@
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg status -S
 
+  $ hg forget -v subrepo/large.txt
+  removing subrepo/large.txt (glob)
+
+Test reverting a forgotten file
+  $ hg revert -R subrepo subrepo/large.txt
+  $ hg status -SA subrepo/large.txt
+  C subrepo/large.txt
+
   $ hg rm -v subrepo/large.txt
   removing subrepo/large.txt (glob)
   $ hg revert -R subrepo subrepo/large.txt
--- a/tests/test-largefiles.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-largefiles.t	Mon Mar 02 01:20:14 2015 -0600
@@ -1183,12 +1183,12 @@
   adding manifests
   adding file changes
   added 1 changesets with 2 changes to 2 files (+1 heads)
-  0 largefiles cached
   rebasing 8:f574fb32bb45 "modify normal file largefile in repo d"
   Invoking status precommit hook
   M sub/normal4
   M sub2/large6
   saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-dd1d9f80-backup.hg (glob)
+  0 largefiles cached
   $ [ -f .hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 ]
   $ hg log --template '{rev}:{node|short}  {desc|firstline}\n'
   9:598410d3eb9a  modify normal file largefile in repo d
--- a/tests/test-log.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-log.t	Mon Mar 02 01:20:14 2015 -0600
@@ -672,10 +672,17 @@
   
 
 
+log -f with null parent
+
+  $ hg up -C null
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg log -f
+
+
 log -r .  with two parents
 
   $ hg up -C 3
-  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg merge tip
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
--- a/tests/test-merge-tools.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-merge-tools.t	Mon Mar 02 01:20:14 2015 -0600
@@ -603,7 +603,8 @@
   true.priority=1
   true.executable=cat
   # hg update -C 1
-  $ hg debugsetparent 0
+  $ hg update -q 0
+  $ hg revert -q -r 1 .
   $ hg update -r 2
   merging f
   revision 1
@@ -628,7 +629,8 @@
   true.priority=1
   true.executable=cat
   # hg update -C 1
-  $ hg debugsetparent 0
+  $ hg update -q 0
+  $ hg revert -q -r 1 .
   $ hg update -r 2 --tool false
   merging f
   merging f failed!
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-obsolete-tag-cache.t	Mon Mar 02 01:20:14 2015 -0600
@@ -0,0 +1,70 @@
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > rebase=
+  > 
+  > [experimental]
+  > evolution = createmarkers
+  > EOF
+
+Create a repo with some tags
+
+  $ hg init repo
+  $ cd repo
+  $ echo initial > foo
+  $ hg -q commit -A -m initial
+  $ hg tag -m 'test tag' test1
+  $ echo first > first
+  $ hg -q commit -A -m first
+  $ hg tag -m 'test2 tag' test2
+  $ hg -q up -r 0
+  $ echo newhead > newhead
+  $ hg commit -A -m newhead
+  adding newhead
+  created new head
+
+Trigger tags cache population by doing something that accesses tags info
+
+  $ hg log -G -T '{rev}:{node|short} {tags} {desc}\n'
+  @  4:042eb6bfcc49 tip newhead
+  |
+  | o  3:c3cb30f2d2cd  test2 tag
+  | |
+  | o  2:d75775ffbc6b test2 first
+  | |
+  | o  1:5f97d42da03f  test tag
+  |/
+  o  0:55482a6fb4b1 test1 initial
+  
+
+  $ cat .hg/cache/tags
+  4 042eb6bfcc4909bad84a1cbf6eb1ddf0ab587d41
+  3 c3cb30f2d2cd0aae008cc91a07876e3c5131fd22 b3bce87817fe7ac9dca2834366c1d7534c095cf1
+  
+  55482a6fb4b1881fa8f746fd52cf6f096bb21c89 test1
+  d75775ffbc6bca1794d300f5571272879bd280da test2
+
+Create some hidden changesets via a rebase and trigger tags cache
+repopulation
+
+  $ hg -q rebase -s 1 -d 4
+  $ hg log -G -T '{rev}:{node|short} {tags} {desc}\n'
+  o  7:eb610439e10e tip test2 tag
+  |
+  o  6:7b4af00c3c83  first
+  |
+  o  5:43ac2a539b3c  test tag
+  |
+  @  4:042eb6bfcc49  newhead
+  |
+  o  0:55482a6fb4b1 test1 initial
+  
+
+.hgtags filenodes for hidden heads should be visible (issue4550)
+(currently broken)
+
+  $ cat .hg/cache/tags
+  7 eb610439e10e0c6b296f97b59624c2e24fc59e30 b3bce87817fe7ac9dca2834366c1d7534c095cf1
+  
+  55482a6fb4b1881fa8f746fd52cf6f096bb21c89 test1
+  d75775ffbc6bca1794d300f5571272879bd280da test2
+
--- a/tests/test-obsolete.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-obsolete.t	Mon Mar 02 01:20:14 2015 -0600
@@ -11,7 +11,7 @@
   >    hg ci -m "add $1"
   > }
   $ getid() {
-  >    hg id --debug --hidden -ir "desc('$1')"
+  >    hg log -T "{node}\n" --hidden -r "desc('$1')"
   > }
 
   $ cat > debugkeys.py <<EOF
@@ -621,7 +621,7 @@
 
 check filelog view
 
-  $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'log/'`hg id --debug --id`/'babar'
+  $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'log/'`hg log -r . -T "{node}"`/'babar'
   200 Script output follows
 
   $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/68'
@@ -753,3 +753,84 @@
   $ hg tags
   visible                            0:193e9254ce7e
   tip                                0:193e9254ce7e
+
+#if serve
+
+Test issue 4506
+
+  $ cd ..
+  $ hg init repo-issue4506
+  $ cd repo-issue4506
+  $ echo "0" > foo
+  $ hg add foo
+  $ hg ci -m "content-0"
+
+  $ hg up null
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo "1" > bar
+  $ hg add bar
+  $ hg ci -m "content-1"
+  created new head
+  $ hg up 0
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg graft 1
+  grafting 1:1c9eddb02162 "content-1" (tip)
+
+  $ hg debugobsolete `hg log -r1 -T'{node}'` `hg log -r2 -T'{node}'`
+
+  $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
+  $ cat hg.pid >> $DAEMON_PIDS
+
+  $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'rev/1'
+  404 Not Found
+  [1]
+  $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'file/tip/bar'
+  200 Script output follows
+  $ "$TESTDIR/get-with-headers.py" --headeronly localhost:$HGPORT 'annotate/tip/bar'
+  200 Script output follows
+
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
+
+#endif
+
+  $ hg init a
+  $ cd a
+  $ touch foo
+  $ hg add foo
+  $ hg ci -mfoo
+  $ touch bar
+  $ hg add bar
+  $ hg ci -mbar
+  $ hg up 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ touch quux
+  $ hg add quux
+  $ hg ci -m quux
+  created new head
+  $ hg up 1
+  1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg tag 1.0
+
+  $ hg up 2
+  1 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg log -G
+  o  3:bc47fc7e1c1d (draft) [tip ] Added tag 1.0 for changeset 50c889141114
+  |
+  | @  2:3d7f255a0081 (draft) [ ] quux
+  | |
+  o |  1:50c889141114 (draft) [1.0 ] bar
+  |/
+  o  0:1f7b0de80e11 (draft) [ ] foo
+  
+  $ hg debugobsolete `getid bar`
+  $ hg debugobsolete `getid 1.0`
+  $ hg tag 1.0
+  $ hg log -G
+  @  4:f9f2ab71ffd5 (draft) [tip ] Added tag 1.0 for changeset 3d7f255a0081
+  |
+  o  2:3d7f255a0081 (draft) [1.0 ] quux
+  |
+  o  0:1f7b0de80e11 (draft) [ ] foo
+  
+  $ cat .hgtags
+  3d7f255a008103380aeb2a7d581fe257f40969e7 1.0
--- a/tests/test-rename.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-rename.t	Mon Mar 02 01:20:14 2015 -0600
@@ -620,10 +620,16 @@
   $ hg rename d1/d11/a1 .hg
   abort: path contains illegal component: .hg/a1 (glob)
   [255]
+  $ hg --config extensions.largefiles= rename d1/d11/a1 .hg
+  abort: path contains illegal component: .hg/a1 (glob)
+  [255]
   $ hg status -C
   $ hg rename d1/d11/a1 ..
   abort: ../a1 not under root '$TESTTMP' (glob)
   [255]
+  $ hg --config extensions.largefiles= rename d1/d11/a1 ..
+  abort: ../a1 not under root '$TESTTMP' (glob)
+  [255]
   $ hg status -C
 
   $ mv d1/d11/a1 .hg
--- a/tests/test-resolve.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-resolve.t	Mon Mar 02 01:20:14 2015 -0600
@@ -43,10 +43,15 @@
   U file1
   U file2
 
-resolving an unknown path should emit a warning
+  $ hg resolve -l --no-status
+  file1
+  file2
+
+resolving an unknown path should emit a warning, but not for -l
 
   $ hg resolve -m does-not-exist
   arguments do not match paths that need resolving
+  $ hg resolve -l does-not-exist
 
 resolve the failure
 
@@ -59,6 +64,18 @@
   R file1
   U file2
 
+  $ hg resolve -l -Tjson
+  [
+   {
+    "path": "file1",
+    "status": "R"
+   },
+   {
+    "path": "file2",
+    "status": "U"
+   }
+  ]
+
 resolve -m without paths should mark all resolved
 
   $ hg resolve -m
@@ -69,6 +86,10 @@
 
   $ hg resolve -l
 
+  $ hg resolve -l -Tjson
+  [
+  ]
+
 resolve --all should abort when no merge in progress
 
   $ hg resolve --all
--- a/tests/test-revset.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-revset.t	Mon Mar 02 01:20:14 2015 -0600
@@ -1029,6 +1029,19 @@
   ('symbol', 'tip')
   warning: failed to parse the declaration of revset alias "bad name": at 4: invalid token
   9
+  $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
+  $ try 'strictreplacing("foo", tip)'
+  (func
+    ('symbol', 'strictreplacing')
+    (list
+      ('string', 'foo')
+      ('symbol', 'tip')))
+  (or
+    ('symbol', 'tip')
+    (func
+      ('symbol', 'desc')
+      ('string', '$1')))
+  9
 
   $ try 'd(2:5)'
   (func
--- a/tests/test-ssh.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-ssh.t	Mon Mar 02 01:20:14 2015 -0600
@@ -116,6 +116,14 @@
   searching for changes
   no changes found
 
+pull from wrong ssh URL
+
+  $ hg pull -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist
+  pulling from ssh://user@dummy/doesnotexist
+  remote: abort: there is no Mercurial repository here (.hg not found)!
+  abort: no suitable response from remote hg!
+  [255]
+
 local change
 
   $ echo bleah > foo
@@ -446,6 +454,7 @@
   Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio
   Got arguments 1:user@dummy 2:hg -R remote serve --stdio
   Got arguments 1:user@dummy 2:hg -R remote serve --stdio
+  Got arguments 1:user@dummy 2:hg -R doesnotexist serve --stdio
   Got arguments 1:user@dummy 2:hg -R remote serve --stdio
   Got arguments 1:user@dummy 2:hg -R local serve --stdio
   Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
--- a/tests/test-status-color.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-status-color.t	Mon Mar 02 01:20:14 2015 -0600
@@ -338,8 +338,8 @@
 hg resolve with one unresolved, one resolved:
 
   $ hg resolve --color=always -l
-  \x1b[0;31;1mU a\x1b[0m (esc)
-  \x1b[0;32;1mR b\x1b[0m (esc)
+  \x1b[0;31;1mU \x1b[0m\x1b[0;31;1ma\x1b[0m (esc)
+  \x1b[0;32;1mR \x1b[0m\x1b[0;32;1mb\x1b[0m (esc)
 
 color coding of error message with current availability of curses
 
--- a/tests/test-strip.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-strip.t	Mon Mar 02 01:20:14 2015 -0600
@@ -220,8 +220,69 @@
   Stream params: {}
   b2x:changegroup -- "{'version': '02'}"
       264128213d290d868c54642d13aeaa3675551a78
+  $ hg incoming .hg/strip-backup/*
+  comparing with .hg/strip-backup/264128213d29-0b39d6bf-backup.hg
+  searching for changes
+  changeset:   4:264128213d29
+  tag:         tip
+  parent:      1:ef3a871183d7
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     c
+  
   $ restore
-
+  $ hg up -C 4
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg --config experimental.bundle2-exp=True --config experimental.strip-bundle2-version=02 --traceback strip 4
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob)
+  $ hg parents
+  changeset:   1:ef3a871183d7
+  user:        test
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     b
+  
+  $ hg debugbundle .hg/strip-backup/*
+  Stream params: {}
+  b2x:changegroup -- "{'version': '02'}"
+      264128213d290d868c54642d13aeaa3675551a78
+  $ hg pull .hg/strip-backup/*
+  pulling from .hg/strip-backup/264128213d29-0b39d6bf-backup.hg
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 0 changes to 0 files (+1 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ rm .hg/strip-backup/*
+  $ hg log --graph
+  o  changeset:   4:264128213d29
+  |  tag:         tip
+  |  parent:      1:ef3a871183d7
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     c
+  |
+  | o  changeset:   3:443431ffac4f
+  | |  user:        test
+  | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | |  summary:     e
+  | |
+  | o  changeset:   2:65bd5f99a4a3
+  |/   user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    summary:     d
+  |
+  @  changeset:   1:ef3a871183d7
+  |  user:        test
+  |  date:        Thu Jan 01 00:00:00 1970 +0000
+  |  summary:     b
+  |
+  o  changeset:   0:9ab35a2d17cb
+     user:        test
+     date:        Thu Jan 01 00:00:00 1970 +0000
+     summary:     a
+  
   $ hg up -C 2
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg merge 4
--- a/tests/test-subrepo-deep-nested-change.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-subrepo-deep-nested-change.t	Mon Mar 02 01:20:14 2015 -0600
@@ -46,12 +46,29 @@
 
 Clone main
 
-  $ hg clone main cloned
+  $ hg --config extensions.largefiles= clone main cloned
   updating to branch default
   cloning subrepo sub1 from $TESTTMP/sub1
   cloning subrepo sub1/sub2 from $TESTTMP/sub2 (glob)
   3 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
+Largefiles is NOT enabled in the clone if the source repo doesn't require it
+  $ cat cloned/.hg/hgrc
+  # example repository config (see "hg help config" for more info)
+  [paths]
+  default = $TESTTMP/main (glob)
+  
+  # path aliases to other clones of this repo in URLs or filesystem paths
+  # (see "hg help config.paths" for more info)
+  #
+  # default-push = ssh://jdoe@example.net/hg/jdoes-fork
+  # my-fork      = ssh://jdoe@example.net/hg/jdoes-fork
+  # my-clone     = /home/jdoe/jdoes-clone
+  
+  [ui]
+  # name and email (local to this repository, optional), e.g.
+  # username = Jane Doe <jdoe@example.com>
+
 Checking cloned repo ids
 
   $ printf "cloned " ; hg id -R cloned
@@ -319,6 +336,31 @@
   ../archive_lf/sub1/sub2/large.bin
   $ rm -rf ../archive_lf
 
+The local repo enables largefiles if a largefiles repo is cloned
+  $ hg showconfig extensions
+  abort: repository requires features unknown to this Mercurial: largefiles!
+  (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
+  [255]
+  $ hg --config extensions.largefiles= clone -qU . ../lfclone
+  $ cat ../lfclone/.hg/hgrc
+  # example repository config (see "hg help config" for more info)
+  [paths]
+  default = $TESTTMP/cloned (glob)
+  
+  # path aliases to other clones of this repo in URLs or filesystem paths
+  # (see "hg help config.paths" for more info)
+  #
+  # default-push = ssh://jdoe@example.net/hg/jdoes-fork
+  # my-fork      = ssh://jdoe@example.net/hg/jdoes-fork
+  # my-clone     = /home/jdoe/jdoes-clone
+  
+  [ui]
+  # name and email (local to this repository, optional), e.g.
+  # username = Jane Doe <jdoe@example.com>
+  
+  [extensions]
+  largefiles=
+
 Find an exact match to a standin (should archive nothing)
   $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
   $ find ../archive_lf 2> /dev/null | sort
--- a/tests/test-subrepo-git.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-subrepo-git.t	Mon Mar 02 01:20:14 2015 -0600
@@ -802,4 +802,52 @@
   $ hg status --subrepos
   ? s/barfoo
 
+show file at specific revision
+  $ cat > s/foobar << EOF
+  > woop    woop
+  > fooo bar
+  > EOF
+  $ hg commit --subrepos -m "updated foobar"
+  committing subrepository s
+  $ cat > s/foobar << EOF
+  > current foobar
+  > (should not be visible using hg cat)
+  > EOF
+
+  $ hg cat -r . s/foobar
+  woop    woop
+  fooo bar (no-eol)
+  $ hg cat -r "parents(.)" s/foobar > catparents
+
+  $ mkdir -p tmp/s
+
+  $ hg cat -r "parents(.)" --output tmp/%% s/foobar
+  $ diff tmp/% catparents
+
+  $ hg cat -r "parents(.)" --output tmp/%s s/foobar
+  $ diff tmp/foobar catparents
+
+  $ hg cat -r "parents(.)" --output tmp/%d/otherfoobar s/foobar
+  $ diff tmp/s/otherfoobar catparents
+
+  $ hg cat -r "parents(.)" --output tmp/%p s/foobar
+  $ diff tmp/s/foobar catparents
+
+  $ hg cat -r "parents(.)" --output tmp/%H s/foobar
+  $ diff tmp/255ee8cf690ec86e99b1e80147ea93ece117cd9d catparents
+
+  $ hg cat -r "parents(.)" --output tmp/%R s/foobar
+  $ diff tmp/10 catparents
+
+  $ hg cat -r "parents(.)" --output tmp/%h s/foobar
+  $ diff tmp/255ee8cf690e catparents
+
+  $ rm tmp/10
+  $ hg cat -r "parents(.)" --output tmp/%r s/foobar
+  $ diff tmp/10 catparents
+
+  $ mkdir tmp/tc
+  $ hg cat -r "parents(.)" --output tmp/%b/foobar s/foobar
+  $ diff tmp/tc/foobar catparents
+
   $ cd ..
--- a/tests/test-subrepo.t	Mon Mar 02 01:06:31 2015 -0600
+++ b/tests/test-subrepo.t	Mon Mar 02 01:20:14 2015 -0600
@@ -50,9 +50,16 @@
 Revert subrepo and test subrepo fileset keyword:
 
   $ echo b > s/a
+  $ hg revert --dry-run "set:subrepo('glob:s*')"
+  reverting subrepo s
+  reverting s/a (glob)
+  $ cat s/a
+  b
   $ hg revert "set:subrepo('glob:s*')"
   reverting subrepo s
   reverting s/a (glob)
+  $ cat s/a
+  a
   $ rm s/a.orig
 
 Revert subrepo with no backup. The "reverting s/a" line is gone since