changeset 22102:fff8e1cec90f

merge with stable
author Matt Mackall <mpm@selenic.com>
date Mon, 11 Aug 2014 11:24:05 -0500
parents 6fa40bd78bc8 (diff) f72b71ba756b (current diff)
children 70bdf59d27b6
files mercurial/commands.py
diffstat 161 files changed, 1932 insertions(+), 1080 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/check-code.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/contrib/check-code.py	Mon Aug 11 11:24:05 2014 -0500
@@ -94,7 +94,7 @@
     (r'sed.*-i', "don't use 'sed -i', use a temporary file"),
     (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"),
     (r'echo -n', "don't use 'echo -n', use printf"),
-    (r'(^| )wc[^|]*$\n(?!.*\(re\))', "filter wc output"),
+    (r'(^| )\bwc\b[^|]*$\n(?!.*\(re\))', "filter wc output"),
     (r'head -c', "don't use 'head -c', use 'dd'"),
     (r'tail -n', "don't use the '-n' option to tail, just use '-<num>'"),
     (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
@@ -179,12 +179,14 @@
 ]
 
 for i in [0, 1]:
-    for p, m in testpats[i]:
+    for tp in testpats[i]:
+        p = tp[0]
+        m = tp[1]
         if p.startswith(r'^'):
             p = r"^  [$>] (%s)" % p[1:]
         else:
             p = r"^  [$>] .*(%s)" % p
-        utestpats[i].append((p, m))
+        utestpats[i].append((p, m) + tp[2:])
 
 utestfilters = [
     (r"<<(\S+)((.|\n)*?\n  > \1)", rephere),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/check-commit	Mon Aug 11 11:24:05 2014 -0500
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+#
+# Copyright 2014 Matt Mackall <mpm@selenic.com>
+#
+# A tool/hook to run basic sanity checks on commits/patches for
+# submission to Mercurial. Install by adding the following to your
+# .hg/hgrc:
+#
+# [hooks]
+# pretxncommit = contrib/check-commit
+#
+# The hook can be temporarily bypassed with:
+#
+# $ BYPASS= hg commit
+#
+# See also: http://mercurial.selenic.com/wiki/ContributingChanges
+
+import re, sys, os
+
+errors = [
+    (r"[(]bc[)]", "(BC) needs to be uppercase"),
+    (r"[(]issue \d\d\d", "no space allowed between issue and number"),
+    (r"[(]bug", "use (issueDDDD) instead of bug"),
+    (r"^# User [^@\n]+$", "username is not an email address"),
+    (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.*\.\s+$", "don't add trailing period on summary line"),
+    (r"^# .*\n.{78,}", "summary line too long"),
+    (r"^\+\n \n", "adds double empty line"),
+    (r"\+\s+def [a-z]+_[a-z]", "adds a function with foo_bar naming"),
+]
+
+node = os.environ.get("HG_NODE")
+
+if node:
+    commit = os.popen("hg export %s" % node).read()
+else:
+    commit = sys.stdin.read()
+
+exitcode = 0
+for exp, msg in errors:
+    m = re.search(exp, commit, re.MULTILINE)
+    if m:
+        pos = 0
+        for n, l in enumerate(commit.splitlines(True)):
+            pos += len(l)
+            if pos >= m.end():
+                print "%d: %s" % (n, msg)
+                print " %s" % l[:-1]
+                if "BYPASS" not in os.environ:
+                    exitcode = 1
+                break
+
+sys.exit(exitcode)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/hg-test-mode.el	Mon Aug 11 11:24:05 2014 -0500
@@ -0,0 +1,56 @@
+;; hg-test-mode.el - Major mode for editing Mercurial tests
+;;
+;; Copyright 2014 Matt Mackall <mpm@selenic.com>
+;; "I have no idea what I'm doing"
+;;
+;; This software may be used and distributed according to the terms of the
+;; GNU General Public License version 2 or any later version.
+;;
+;; To enable, add something like the following to your .emacs:
+;;
+;; (if (file-exists-p "~/hg/contrib/hg-test-mode.el")
+;;    (load "~/hg/contrib/hg-test-mode.el"))
+
+(defvar hg-test-mode-hook nil)
+
+(defvar hg-test-mode-map
+  (let ((map (make-keymap)))
+    (define-key map "\C-j" 'newline-and-indent)
+    map)
+  "Keymap for hg test major mode")
+
+(add-to-list 'auto-mode-alist '("\\.t\\'" . hg-test-mode))
+
+(defconst hg-test-font-lock-keywords-1
+  (list
+   '("^  \\(\\$\\|>>>\\) " 1 font-lock-builtin-face)
+   '("^  \\(>\\|\\.\\.\\.\\) " 1 font-lock-constant-face)
+   '("\\$?\\(HG\\|TEST\\)\\w+=?" . font-lock-variable-name-face)
+   '("^  \\([[][0-9]+[]]\\)$" 1 font-lock-warning-face)
+   '("^  \\(.*?\\)\\(\\( [(][-a-z]+[)]\\)+\\)$" 2 font-lock-type-face)
+   '("^  \\(.*?\\)\\(\\( [(][-a-z]+[)]\\)*\\)$" 1 font-lock-string-face)
+   '("^#.*" . font-lock-preprocessor-face)
+   '("^\\([^ ].*\\)$" 1 font-lock-comment-face)
+   )
+  "Minimal highlighting expressions for hg-test mode")
+
+(defvar hg-test-font-lock-keywords hg-test-font-lock-keywords-1
+  "Default highlighting expressions for hg-test mode")
+
+(defvar hg-test-mode-syntax-table
+  (let ((st (make-syntax-table)))
+    (modify-syntax-entry ?\" "w" st) ;; disable standard quoting
+    st)
+"Syntax table for hg-test mode")
+
+(defun hg-test-mode ()
+  (interactive)
+  (kill-all-local-variables)
+  (use-local-map hg-test-mode-map)
+  (set-syntax-table hg-test-mode-syntax-table)
+  (set (make-local-variable 'font-lock-defaults) '(hg-test-font-lock-keywords))
+  (setq major-mode 'hg-test-mode)
+  (setq mode-name "hg-test")
+  (run-hooks 'hg-test-mode-hook))
+
+(provide 'hg-test-mode)
--- a/contrib/revsetbenchmarks.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/contrib/revsetbenchmarks.py	Mon Aug 11 11:24:05 2014 -0500
@@ -19,8 +19,6 @@
 # cannot use argparse, python 2.7 only
 from optparse import OptionParser
 
-
-
 def check_output(*args, **kwargs):
     kwargs.setdefault('stderr', PIPE)
     kwargs.setdefault('stdout', PIPE)
--- a/contrib/simplemerge	Sun Aug 10 23:09:23 2014 -0500
+++ b/contrib/simplemerge	Mon Aug 11 11:24:05 2014 -0500
@@ -11,8 +11,7 @@
            ('a', 'text', None, _('treat all files as text')),
            ('p', 'print', None,
             _('print results instead of overwriting LOCAL')),
-           ('', 'no-minimal', None,
-            _('do not try to minimize conflict regions')),
+           ('', 'no-minimal', None, _('no effect (DEPRECATED)')),
            ('h', 'help', None, _('display help and exit')),
            ('q', 'quiet', None, _('suppress output'))]
 
--- a/hgext/fetch.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/hgext/fetch.py	Mon Aug 11 11:24:05 2014 -0500
@@ -143,8 +143,8 @@
                        ('Automated merge with %s' %
                         util.removeauth(other.url())))
             editopt = opts.get('edit') or opts.get('force_editor')
-            n = repo.commit(message, opts['user'], opts['date'],
-                            editor=cmdutil.getcommiteditor(edit=editopt))
+            editor = cmdutil.getcommiteditor(edit=editopt, editform='fetch')
+            n = repo.commit(message, opts['user'], opts['date'], editor=editor)
             ui.status(_('new changeset %d:%s merges remote changes '
                         'with local\n') % (repo.changelog.rev(n),
                                            short(n)))
--- a/hgext/gpg.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/hgext/gpg.py	Mon Aug 11 11:24:05 2014 -0500
@@ -277,8 +277,9 @@
                              % hgnode.short(n)
                              for n in nodes])
     try:
+        editor = cmdutil.getcommiteditor(editform='gpg.sign', **opts)
         repo.commit(message, opts['user'], opts['date'], match=msigs,
-                    editor=cmdutil.getcommiteditor(**opts))
+                    editor=editor)
     except ValueError, inst:
         raise util.Abort(str(inst))
 
--- a/hgext/histedit.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/hgext/histedit.py	Mon Aug 11 11:24:05 2014 -0500
@@ -208,8 +208,6 @@
             repo.ui.restoreconfig(phasebackup)
     return commitfunc
 
-
-
 def applychanges(ui, repo, ctx, opts):
     """Merge changeset from ctx (only) in the current working directory"""
     wcpar = repo.dirstate.parents()[0]
@@ -293,6 +291,7 @@
     extra = commitopts.get('extra')
 
     parents = (first.p1().node(), first.p2().node())
+    editor = cmdutil.getcommiteditor(edit=True, editform='histedit.fold')
     new = context.memctx(repo,
                          parents=parents,
                          text=message,
@@ -301,7 +300,7 @@
                          user=user,
                          date=date,
                          extra=extra,
-                         editor=cmdutil.getcommiteditor(edit=True))
+                         editor=editor)
     return repo.commitctx(new)
 
 def pick(ui, repo, ctx, ha, opts):
@@ -405,9 +404,10 @@
             _('Fix up the change and run hg histedit --continue'))
     message = oldctx.description()
     commit = commitfuncfor(repo, oldctx)
+    editor = cmdutil.getcommiteditor(edit=True, editform='histedit.mess')
     new = commit(text=message, user=oldctx.user(), date=oldctx.date(),
                  extra=oldctx.extra(),
-                 editor=cmdutil.getcommiteditor(edit=True))
+                 editor=editor)
     newctx = repo[new]
     if oldctx.node() != newctx.node():
         return newctx, [(oldctx.node(), (new,))]
@@ -684,7 +684,9 @@
         else:
             message = ctx.description()
         editopt = action in ('e', 'edit', 'm', 'mess')
-        editor = cmdutil.getcommiteditor(edit=editopt)
+        canonaction = {'e': 'edit', 'm': 'mess', 'p': 'pick'}
+        editform = 'histedit.%s' % canonaction.get(action, action)
+        editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
         commit = commitfuncfor(repo, ctx)
         new = commit(text=message, user=ctx.user(),
                      date=ctx.date(), extra=ctx.extra(),
--- a/hgext/keyword.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/hgext/keyword.py	Mon Aug 11 11:24:05 2014 -0500
@@ -1,6 +1,6 @@
 # keyword.py - $Keyword$ expansion for Mercurial
 #
-# Copyright 2007-2012 Christian Ebert <blacktrash@gmx.net>
+# Copyright 2007-2014 Christian Ebert <blacktrash@gmx.net>
 #
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
@@ -87,7 +87,7 @@
 from mercurial import scmutil, pathutil
 from mercurial.hgweb import webcommands
 from mercurial.i18n import _
-import os, re, shutil, tempfile
+import os, re, tempfile
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
@@ -450,7 +450,12 @@
     repo.commit(text=msg)
     ui.status(_('\n\tkeywords expanded\n'))
     ui.write(repo.wread(fn))
-    shutil.rmtree(tmpdir, ignore_errors=True)
+    for root, dirs, files in os.walk(tmpdir, topdown=False):
+        for f in files:
+            util.unlink(os.path.join(root, f))
+        for d in dirs:
+            os.rmdir(os.path.join(root, d))
+    os.rmdir(tmpdir)
 
 @command('kwexpand',
     commands.walkopts,
--- a/hgext/largefiles/lfcommands.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/hgext/largefiles/lfcommands.py	Mon Aug 11 11:24:05 2014 -0500
@@ -510,26 +510,7 @@
 
             updated += update1
 
-            standin = lfutil.standin(lfile)
-            if standin in repo.dirstate:
-                stat = repo.dirstate._map[standin]
-                state, mtime = stat[0], stat[3]
-            else:
-                state, mtime = '?', -1
-            if state == 'n':
-                if normallookup or mtime < 0:
-                    # state 'n' doesn't ensure 'clean' in this case
-                    lfdirstate.normallookup(lfile)
-                else:
-                    lfdirstate.normal(lfile)
-            elif state == 'm':
-                lfdirstate.normallookup(lfile)
-            elif state == 'r':
-                lfdirstate.remove(lfile)
-            elif state == 'a':
-                lfdirstate.add(lfile)
-            elif state == '?':
-                lfdirstate.drop(lfile)
+            lfutil.synclfdirstate(repo, lfdirstate, lfile, normallookup)
 
         lfdirstate.write()
         if printmessage and lfiles:
--- a/hgext/largefiles/lfutil.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/hgext/largefiles/lfutil.py	Mon Aug 11 11:24:05 2014 -0500
@@ -363,6 +363,28 @@
         standins.append((lfile, hash))
     return standins
 
+def synclfdirstate(repo, lfdirstate, lfile, normallookup):
+    lfstandin = standin(lfile)
+    if lfstandin in repo.dirstate:
+        stat = repo.dirstate._map[lfstandin]
+        state, mtime = stat[0], stat[3]
+    else:
+        state, mtime = '?', -1
+    if state == 'n':
+        if normallookup or mtime < 0:
+            # state 'n' doesn't ensure 'clean' in this case
+            lfdirstate.normallookup(lfile)
+        else:
+            lfdirstate.normal(lfile)
+    elif state == 'm':
+        lfdirstate.normallookup(lfile)
+    elif state == 'r':
+        lfdirstate.remove(lfile)
+    elif state == 'a':
+        lfdirstate.add(lfile)
+    elif state == '?':
+        lfdirstate.drop(lfile)
+
 def getlfilestoupdate(oldstandins, newstandins):
     changedstandins = set(oldstandins).symmetric_difference(set(newstandins))
     filelist = []
--- a/hgext/largefiles/overrides.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/hgext/largefiles/overrides.py	Mon Aug 11 11:24:05 2014 -0500
@@ -1140,19 +1140,20 @@
     repo.status = oldstatus
 
 def overriderollback(orig, ui, repo, **opts):
-    result = orig(ui, repo, **opts)
-    merge.update(repo, node=None, branchmerge=False, force=True,
-        partial=lfutil.isstandin)
     wlock = repo.wlock()
     try:
+        result = orig(ui, repo, **opts)
+        merge.update(repo, node=None, branchmerge=False, force=True,
+                     partial=lfutil.isstandin)
+
         lfdirstate = lfutil.openlfdirstate(ui, repo)
+        orphans = set(lfdirstate)
         lfiles = lfutil.listlfiles(repo)
-        oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
         for file in lfiles:
-            if file in oldlfiles:
-                lfdirstate.normallookup(file)
-            else:
-                lfdirstate.add(file)
+            lfutil.synclfdirstate(repo, lfdirstate, file, True)
+            orphans.discard(file)
+        for lfile in orphans:
+            lfdirstate.drop(lfile)
         lfdirstate.write()
     finally:
         wlock.release()
--- a/hgext/largefiles/reposetup.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/hgext/largefiles/reposetup.py	Mon Aug 11 11:24:05 2014 -0500
@@ -285,6 +285,16 @@
                                             printmessage=False)
                     result = orig(text=text, user=user, date=date, match=match,
                                     force=force, editor=editor, extra=extra)
+
+                    if result:
+                        lfdirstate = lfutil.openlfdirstate(ui, self)
+                        for f in self[result].files():
+                            if lfutil.isstandin(f):
+                                lfile = lfutil.splitstandin(f)
+                                lfutil.synclfdirstate(self, lfdirstate, lfile,
+                                                      False)
+                        lfdirstate.write()
+
                     return result
                 # Case 1: user calls commit with no specific files or
                 # include/exclude patterns: refresh and commit all files that
--- a/hgext/mq.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/hgext/mq.py	Mon Aug 11 11:24:05 2014 -0500
@@ -621,7 +621,7 @@
 
         # apply failed, strip away that rev and merge.
         hg.clean(repo, head)
-        strip(self.ui, repo, [n], update=False, backup='strip')
+        strip(self.ui, repo, [n], update=False, backup=False)
 
         ctx = repo[rev]
         ret = hg.merge(repo, rev)
@@ -930,7 +930,12 @@
             oldqbase = repo[qfinished[0]]
             tphase = repo.ui.config('phases', 'new-commit', phases.draft)
             if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
-                phases.advanceboundary(repo, tphase, qfinished)
+                tr = repo.transaction('qfinish')
+                try:
+                    phases.advanceboundary(repo, tr, tphase, qfinished)
+                    tr.close()
+                finally:
+                    tr.release()
 
     def delete(self, repo, patches, opts):
         if not patches and not opts.get('rev'):
@@ -1025,6 +1030,7 @@
         """
         msg = opts.get('msg')
         edit = opts.get('edit')
+        editform = opts.get('editform', 'mq.qnew')
         user = opts.get('user')
         date = opts.get('date')
         if date:
@@ -1079,7 +1085,7 @@
                         p.write("# Date %s %s\n\n" % date)
 
                 defaultmsg = "[mq]: %s" % patchfn
-                editor = cmdutil.getcommiteditor()
+                editor = cmdutil.getcommiteditor(editform=editform)
                 if edit:
                     def finishdesc(desc):
                         if desc.rstrip():
@@ -1089,7 +1095,8 @@
                     # i18n: this message is shown in editor with "HG: " prefix
                     extramsg = _('Leave message empty to use default message.')
                     editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
-                                                     extramsg=extramsg)
+                                                     extramsg=extramsg,
+                                                     editform=editform)
                     commitmsg = msg
                 else:
                     commitmsg = msg or defaultmsg
@@ -1456,7 +1463,7 @@
             for patch in reversed(self.applied[start:end]):
                 self.ui.status(_("popping %s\n") % patch.name)
             del self.applied[start:end]
-            strip(self.ui, repo, [rev], update=False, backup='strip')
+            strip(self.ui, repo, [rev], update=False, backup=False)
             for s, state in repo['.'].substate.items():
                 repo['.'].sub(s).get(state)
             if self.applied:
@@ -1485,6 +1492,7 @@
             return 1
         msg = opts.get('msg', '').rstrip()
         edit = opts.get('edit')
+        editform = opts.get('editform', 'mq.qrefresh')
         newuser = opts.get('user')
         newdate = opts.get('date')
         if newdate:
@@ -1645,7 +1653,7 @@
                 repo.setparents(*cparents)
                 self.applied.pop()
                 self.applieddirty = True
-                strip(self.ui, repo, [top], update=False, backup='strip')
+                strip(self.ui, repo, [top], update=False, backup=False)
             except: # re-raises
                 repo.dirstate.invalidate()
                 raise
@@ -1654,7 +1662,7 @@
                 # might be nice to attempt to roll back strip after this
 
                 defaultmsg = "[mq]: %s" % patchfn
-                editor = cmdutil.getcommiteditor()
+                editor = cmdutil.getcommiteditor(editform=editform)
                 if edit:
                     def finishdesc(desc):
                         if desc.rstrip():
@@ -1664,7 +1672,8 @@
                     # i18n: this message is shown in editor with "HG: " prefix
                     extramsg = _('Leave message empty to use default message.')
                     editor = cmdutil.getcommiteditor(finishdesc=finishdesc,
-                                                     extramsg=extramsg)
+                                                     extramsg=extramsg,
+                                                     editform=editform)
                     message = msg or "\n".join(ph.message)
                 elif not msg:
                     if not ph.message:
@@ -1842,7 +1851,7 @@
                     update = True
                 else:
                     update = False
-                strip(self.ui, repo, [rev], update=update, backup='strip')
+                strip(self.ui, repo, [rev], update=update, backup=False)
         if qpp:
             self.ui.warn(_("saved queue repository parents: %s %s\n") %
                          (short(qpp[0]), short(qpp[1])))
@@ -1966,41 +1975,49 @@
                 lastparent = None
 
             diffopts = self.diffopts({'git': git})
-            for r in rev:
-                if not repo[r].mutable():
-                    raise util.Abort(_('revision %d is not mutable') % r,
-                                     hint=_('see "hg help phases" for details'))
-                p1, p2 = repo.changelog.parentrevs(r)
-                n = repo.changelog.node(r)
-                if p2 != nullrev:
-                    raise util.Abort(_('cannot import merge revision %d') % r)
-                if lastparent and lastparent != r:
-                    raise util.Abort(_('revision %d is not the parent of %d')
-                                     % (r, lastparent))
-                lastparent = p1
-
-                if not patchname:
-                    patchname = normname('%d.diff' % r)
-                checkseries(patchname)
-                self.checkpatchname(patchname, force)
-                self.fullseries.insert(0, patchname)
-
-                patchf = self.opener(patchname, "w")
-                cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
-                patchf.close()
-
-                se = statusentry(n, patchname)
-                self.applied.insert(0, se)
-
-                self.added.append(patchname)
-                imported.append(patchname)
-                patchname = None
-                if rev and repo.ui.configbool('mq', 'secret', False):
-                    # if we added anything with --rev, move the secret root
-                    phases.retractboundary(repo, phases.secret, [n])
-                self.parseseries()
-                self.applieddirty = True
-                self.seriesdirty = True
+            tr = repo.transaction('qimport')
+            try:
+                for r in rev:
+                    if not repo[r].mutable():
+                        raise util.Abort(_('revision %d is not mutable') % r,
+                                         hint=_('see "hg help phases" '
+                                                'for details'))
+                    p1, p2 = repo.changelog.parentrevs(r)
+                    n = repo.changelog.node(r)
+                    if p2 != nullrev:
+                        raise util.Abort(_('cannot import merge revision %d')
+                                         % r)
+                    if lastparent and lastparent != r:
+                        raise util.Abort(_('revision %d is not the parent of '
+                                           '%d')
+                                         % (r, lastparent))
+                    lastparent = p1
+
+                    if not patchname:
+                        patchname = normname('%d.diff' % r)
+                    checkseries(patchname)
+                    self.checkpatchname(patchname, force)
+                    self.fullseries.insert(0, patchname)
+
+                    patchf = self.opener(patchname, "w")
+                    cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
+                    patchf.close()
+
+                    se = statusentry(n, patchname)
+                    self.applied.insert(0, se)
+
+                    self.added.append(patchname)
+                    imported.append(patchname)
+                    patchname = None
+                    if rev and repo.ui.configbool('mq', 'secret', False):
+                        # if we added anything with --rev, move the secret root
+                        phases.retractboundary(repo, tr, phases.secret, [n])
+                    self.parseseries()
+                    self.applieddirty = True
+                    self.seriesdirty = True
+                tr.close()
+            finally:
+                tr.release()
 
         for i, filename in enumerate(files):
             if existing:
@@ -2585,7 +2602,8 @@
     diffopts = q.patchopts(q.diffopts(), *patches)
     wlock = repo.wlock()
     try:
-        q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'))
+        q.refresh(repo, msg=message, git=diffopts.git, edit=opts.get('edit'),
+                  editform='mq.qfold')
         q.delete(repo, patches, opts)
         q.savedirty()
     finally:
--- a/hgext/purge.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/hgext/purge.py	Mon Aug 11 11:24:05 2014 -0500
@@ -26,7 +26,7 @@
 
 from mercurial import util, commands, cmdutil, scmutil
 from mercurial.i18n import _
-import os, stat
+import os
 
 cmdtable = {}
 command = cmdutil.command(cmdtable)
@@ -95,17 +95,6 @@
         else:
             ui.write('%s%s' % (name, eol))
 
-    def removefile(path):
-        try:
-            os.remove(path)
-        except OSError:
-            # read-only files cannot be unlinked under Windows
-            s = os.stat(path)
-            if (s.st_mode & stat.S_IWRITE) != 0:
-                raise
-            os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
-            os.remove(path)
-
     directories = []
     match = scmutil.match(repo[None], dirs, opts)
     match.explicitdir = match.traversedir = directories.append
@@ -115,7 +104,7 @@
         for f in sorted(status[4] + status[5]):
             if act:
                 ui.note(_('removing file %s\n') % f)
-            remove(removefile, f)
+            remove(util.unlink, f)
 
     if removedirs:
         for f in sorted(directories, reverse=True):
--- a/hgext/rebase.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/hgext/rebase.py	Mon Aug 11 11:24:05 2014 -0500
@@ -138,7 +138,7 @@
     skipped = set()
     targetancestors = set()
 
-    editor = cmdutil.getcommiteditor(**opts)
+    editor = cmdutil.getcommiteditor(editform='rebase.normal', **opts)
 
     lock = wlock = None
     try:
@@ -383,7 +383,8 @@
                 for rebased in state:
                     if rebased not in skipped and state[rebased] > nullmerge:
                         commitmsg += '\n* %s' % repo[rebased].description()
-                editor = cmdutil.getcommiteditor(edit=True)
+                editform = 'rebase.collapse'
+                editor = cmdutil.getcommiteditor(edit=True, editform=editform)
             newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
                                   extrafn=extrafn, editor=editor)
             for oldrev in state.iterkeys():
@@ -468,15 +469,18 @@
         extra = {'rebase_source': ctx.hex()}
         if extrafn:
             extrafn(ctx, extra)
-        # Commit might fail if unresolved files exist
-        newrev = repo.commit(text=commitmsg, user=ctx.user(),
-                             date=ctx.date(), extra=extra, editor=editor)
+
+        backup = repo.ui.backupconfig('phases', 'new-commit')
+        try:
+            targetphase = max(ctx.phase(), phases.draft)
+            repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase')
+            # Commit might fail if unresolved files exist
+            newrev = repo.commit(text=commitmsg, user=ctx.user(),
+                                 date=ctx.date(), extra=extra, editor=editor)
+        finally:
+            repo.ui.restoreconfig(backup)
+
         repo.dirstate.setbranch(repo[newrev].branch())
-        targetphase = max(ctx.phase(), phases.draft)
-        # retractboundary doesn't overwrite upper phase inherited from parent
-        newnode = repo[newrev].node()
-        if newnode:
-            phases.retractboundary(repo, targetphase, [newnode])
         return newrev
     except util.Abort:
         # Invalidate the previous setparents
--- a/hgext/shelve.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/hgext/shelve.py	Mon Aug 11 11:24:05 2014 -0500
@@ -73,7 +73,8 @@
         try:
             gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
             changegroup.addchangegroup(self.repo, gen, 'unshelve',
-                                       'bundle:' + self.vfs.join(self.fname))
+                                       'bundle:' + self.vfs.join(self.fname),
+                                       targetphase=phases.secret)
         finally:
             fp.close()
 
@@ -177,10 +178,14 @@
         hasmq = util.safehasattr(repo, 'mq')
         if hasmq:
             saved, repo.mq.checkapplied = repo.mq.checkapplied, False
+        backup = repo.ui.backupconfig('phases', 'new-commit')
         try:
+            repo.ui. setconfig('phases', 'new-commit', phases.secret)
+            editor = cmdutil.getcommiteditor(editform='shelve.shelve', **opts)
             return repo.commit(message, user, opts.get('date'), match,
-                               editor=cmdutil.getcommiteditor(**opts))
+                               editor=editor)
         finally:
+            repo.ui.restoreconfig(backup)
             if hasmq:
                 repo.mq.checkapplied = saved
 
@@ -234,8 +239,6 @@
                 ui.status(_("nothing changed\n"))
             return 1
 
-        phases.retractboundary(repo, phases.secret, [node])
-
         fp = shelvedfile(repo, name, 'files').opener('wb')
         fp.write('\0'.join(shelvedfiles))
 
@@ -388,7 +391,7 @@
 
         mergefiles(ui, repo, state.wctx, state.pendingctx)
 
-        repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
+        repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
         shelvedstate.clear(repo)
         ui.warn(_("unshelve of '%s' aborted\n") % state.name)
     finally:
@@ -457,7 +460,7 @@
         mergefiles(ui, repo, state.wctx, shelvectx)
 
         state.stripnodes.append(shelvectx.node())
-        repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
+        repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
         shelvedstate.clear(repo)
         unshelvecleanup(ui, repo, state.name, opts)
         ui.status(_("unshelve of '%s' complete\n") % state.name)
@@ -558,10 +561,13 @@
                 if hasmq:
                     saved, repo.mq.checkapplied = repo.mq.checkapplied, False
 
+                backup = repo.ui.backupconfig('phases', 'new-commit')
                 try:
+                    repo.ui. setconfig('phases', 'new-commit', phases.secret)
                     return repo.commit(message, 'shelve@localhost',
                                        opts.get('date'), match)
                 finally:
+                    repo.ui.restoreconfig(backup)
                     if hasmq:
                         repo.mq.checkapplied = saved
 
@@ -574,8 +580,6 @@
 
         ui.quiet = True
         shelvedfile(repo, basename, 'hg').applybundle()
-        nodes = [ctx.node() for ctx in repo.set('%d:', oldtiprev)]
-        phases.retractboundary(repo, phases.secret, nodes)
 
         ui.quiet = oldquiet
 
--- a/hgext/strip.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/hgext/strip.py	Mon Aug 11 11:24:05 2014 -0500
@@ -42,7 +42,7 @@
             raise util.Abort(_("local changed subrepos found" + excsuffix))
     return m, a, r, d
 
-def strip(ui, repo, revs, update=True, backup="all", force=None, bookmark=None):
+def strip(ui, repo, revs, update=True, backup=True, force=None, bookmark=None):
     wlock = lock = None
     try:
         wlock = repo.wlock()
@@ -114,11 +114,9 @@
 
     Return 0 on success.
     """
-    backup = 'all'
-    if opts.get('backup'):
-        backup = 'strip'
-    elif opts.get('no_backup') or opts.get('nobackup'):
-        backup = 'none'
+    backup = True
+    if opts.get('no_backup') or opts.get('nobackup'):
+        backup = False
 
     cl = repo.changelog
     revs = list(revs) + opts.get('rev')
--- a/hgext/transplant.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/hgext/transplant.py	Mon Aug 11 11:24:05 2014 -0500
@@ -86,7 +86,7 @@
         self.opener = scmutil.opener(self.path)
         self.transplants = transplants(self.path, 'transplants',
                                        opener=self.opener)
-        self.editor = cmdutil.getcommiteditor(**opts)
+        self.editor = cmdutil.getcommiteditor(editform='transplant', **opts)
 
     def applied(self, repo, node, parent):
         '''returns True if a node is already an ancestor of parent
--- a/mercurial/branchmap.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/branchmap.py	Mon Aug 11 11:24:05 2014 -0500
@@ -62,8 +62,6 @@
         partial = None
     return partial
 
-
-
 ### Nearest subset relation
 # Nearest subset of filter X is a filter Y so that:
 # * Y is included in X,
--- a/mercurial/changegroup.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/changegroup.py	Mon Aug 11 11:24:05 2014 -0500
@@ -569,7 +569,8 @@
 
     return revisions, files
 
-def addchangegroup(repo, source, srctype, url, emptyok=False):
+def addchangegroup(repo, source, srctype, url, emptyok=False,
+                   targetphase=phases.draft):
     """Add the changegroup returned by source.read() to this repo.
     srctype is a string like 'push', 'pull', or 'unbundle'.  url is
     the URL of the repo where this changegroup is coming from.
@@ -699,15 +700,18 @@
             # We should not use added here but the list of all change in
             # the bundle
             if publishing:
-                phases.advanceboundary(repo, phases.public, srccontent)
+                phases.advanceboundary(repo, tr, phases.public, srccontent)
             else:
-                phases.advanceboundary(repo, phases.draft, srccontent)
-                phases.retractboundary(repo, phases.draft, added)
+                # Those changesets have been pushed from the outside, their
+                # phases are going to be pushed alongside. Therefor
+                # `targetphase` is ignored.
+                phases.advanceboundary(repo, tr, phases.draft, srccontent)
+                phases.retractboundary(repo, tr, phases.draft, added)
         elif srctype != 'strip':
             # publishing only alter behavior during push
             #
             # strip should not touch boundary at all
-            phases.retractboundary(repo, phases.draft, added)
+            phases.retractboundary(repo, tr, targetphase, added)
 
         # make changelog see real files again
         cl.finalize(trp)
--- a/mercurial/cmdutil.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/cmdutil.py	Mon Aug 11 11:24:05 2014 -0500
@@ -109,7 +109,8 @@
                              (logfile, inst.strerror))
     return message
 
-def getcommiteditor(edit=False, finishdesc=None, extramsg=None, **opts):
+def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
+                    editform='', **opts):
     """get appropriate commit message editor according to '--edit' option
 
     'finishdesc' is a function to be called with edited commit message
@@ -122,6 +123,9 @@
     'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
     is automatically added.
 
+    'editform' is a dot-separated list of names, to distinguish
+    the purpose of commit text editing.
+
     'getcommiteditor' returns 'commitforceeditor' regardless of
     'edit', if one of 'finishdesc' or 'extramsg' is specified, because
     they are specific for usage in MQ.
@@ -129,7 +133,10 @@
     if edit or finishdesc or extramsg:
         return lambda r, c, s: commitforceeditor(r, c, s,
                                                  finishdesc=finishdesc,
-                                                 extramsg=extramsg)
+                                                 extramsg=extramsg,
+                                                 editform=editform)
+    elif editform:
+        return lambda r, c, s: commiteditor(r, c, s, editform=editform)
     else:
         return commiteditor
 
@@ -586,7 +593,7 @@
     tmpname, message, user, date, branch, nodeid, p1, p2 = \
         patch.extract(ui, hunk)
 
-    editor = getcommiteditor(**opts)
+    editor = getcommiteditor(editform='import.normal', **opts)
     update = not opts.get('bypass')
     strip = opts["strip"]
     sim = float(opts.get('similarity') or 0)
@@ -680,12 +687,13 @@
                                     files, eolmode=None)
                 except patch.PatchError, e:
                     raise util.Abort(str(e))
+                editor = getcommiteditor(editform='import.bypass')
                 memctx = context.makememctx(repo, (p1.node(), p2.node()),
                                             message,
                                             opts.get('user') or user,
                                             opts.get('date') or date,
                                             branch, files, store,
-                                            editor=getcommiteditor())
+                                            editor=editor)
                 n = memctx.commit()
             finally:
                 store.close()
@@ -1570,8 +1578,14 @@
     if not slowpath:
         for f in match.files():
             if follow and f not in pctx:
-                raise util.Abort(_('cannot follow file not in parent '
-                                   'revision: "%s"') % f)
+                # If the file exists, it may be a directory, so let it
+                # take the slow path.
+                if os.path.exists(repo.wjoin(f)):
+                    slowpath = True
+                    continue
+                else:
+                    raise util.Abort(_('cannot follow file not in parent '
+                                       'revision: "%s"') % f)
             filelog = repo.file(f)
             if not filelog:
                 # A zero count may be a directory or deleted file, so
@@ -1595,9 +1609,6 @@
     if slowpath:
         # See walkchangerevs() slow path.
         #
-        if follow:
-            raise util.Abort(_('can only follow copies/renames for explicit '
-                               'filenames'))
         # pats/include/exclude cannot be represented as separate
         # revset expressions as their filtering logic applies at file
         # level. For instance "-I a -X a" matches a revision touching
@@ -1629,7 +1640,10 @@
 
     filematcher = None
     if opts.get('patch') or opts.get('stat'):
-        if follow and not match.always():
+        # When following files, track renames via a special matcher.
+        # If we're forced to take the slowpath it means we're following
+        # at least one pattern/directory, so don't bother with rename tracking.
+        if follow and not match.always() and not slowpath:
             # _makelogfilematcher expects its files argument to be relative to
             # the repo root, so use match.files(), not pats.
             filematcher = _makelogfilematcher(repo, match.files(), followfirst)
@@ -2093,9 +2107,10 @@
 
                 user = opts.get('user') or old.user()
                 date = opts.get('date') or old.date()
-            editor = getcommiteditor(**opts)
+            editform = 'commit.amend'
+            editor = getcommiteditor(editform=editform, **opts)
             if not message:
-                editor = getcommiteditor(edit=True)
+                editor = getcommiteditor(edit=True, editform=editform)
                 message = old.description()
 
             pureextra = extra.copy()
@@ -2169,17 +2184,24 @@
         lockmod.release(lock, wlock)
     return newid
 
-def commiteditor(repo, ctx, subs):
+def commiteditor(repo, ctx, subs, editform=''):
     if ctx.description():
         return ctx.description()
-    return commitforceeditor(repo, ctx, subs)
+    return commitforceeditor(repo, ctx, subs, editform=editform)
 
-def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None):
+def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
+                      editform=''):
     if not extramsg:
         extramsg = _("Leave message empty to abort commit.")
-    tmpl = repo.ui.config('committemplate', 'changeset', '').strip()
-    if tmpl:
-        committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
+
+    forms = [e for e in editform.split('.') if e]
+    forms.insert(0, 'changeset')
+    while forms:
+        tmpl = repo.ui.config('committemplate', '.'.join(forms))
+        if tmpl:
+            committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
+            break
+        forms.pop()
     else:
         committext = buildcommittext(repo, ctx, subs, extramsg)
 
@@ -2206,6 +2228,10 @@
     except SyntaxError, inst:
         raise util.Abort(inst.args[0])
 
+    for k, v in repo.ui.configitems('committemplate'):
+        if k != 'changeset':
+            t.t.cache[k] = v
+
     if not extramsg:
         extramsg = '' # ensure that extramsg is string
 
--- a/mercurial/commands.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/commands.py	Mon Aug 11 11:24:05 2014 -0500
@@ -505,11 +505,12 @@
 
 
         def commitfunc(ui, repo, message, match, opts):
-            e = cmdutil.getcommiteditor(**opts)
+            editform = 'backout'
+            e = cmdutil.getcommiteditor(editform=editform, **opts)
             if not message:
                 # we don't translate commit messages
                 message = "Backed out changeset %s" % short(node)
-                e = cmdutil.getcommiteditor(edit=True)
+                e = cmdutil.getcommiteditor(edit=True, editform=editform)
             return repo.commit(message, opts.get('user'), opts.get('date'),
                                match, editor=e)
         newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
@@ -1385,9 +1386,6 @@
         # Let --subrepos on the command line override config setting.
         ui.setconfig('ui', 'commitsubrepos', True, 'commit')
 
-    # Save this for restoring it later
-    oldcommitphase = ui.config('phases', 'new-commit')
-
     cmdutil.checkunfinished(repo, commit=True)
 
     branch = repo[None].branch()
@@ -1441,21 +1439,24 @@
             newmarks.write()
     else:
         def commitfunc(ui, repo, message, match, opts):
+            backup = ui.backupconfig('phases', 'new-commit')
+            baseui = repo.baseui
+            basebackup = baseui.backupconfig('phases', 'new-commit')
             try:
                 if opts.get('secret'):
                     ui.setconfig('phases', 'new-commit', 'secret', 'commit')
                     # Propagate to subrepos
-                    repo.baseui.setconfig('phases', 'new-commit', 'secret',
-                                          'commit')
-
+                    baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
+
+                editform = 'commit.normal'
+                editor = cmdutil.getcommiteditor(editform=editform, **opts)
                 return repo.commit(message, opts.get('user'), opts.get('date'),
                                    match,
-                                   editor=cmdutil.getcommiteditor(**opts),
+                                   editor=editor,
                                    extra=extra)
             finally:
-                ui.setconfig('phases', 'new-commit', oldcommitphase, 'commit')
-                repo.baseui.setconfig('phases', 'new-commit', oldcommitphase,
-                                      'commit')
+                ui.restoreconfig(backup)
+                repo.baseui.restoreconfig(basebackup)
 
 
         node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
@@ -3054,6 +3055,7 @@
      ('c', 'continue', False, _('resume interrupted graft')),
      ('e', 'edit', False, _('invoke editor on commit messages')),
      ('', 'log', None, _('append graft info to log message')),
+     ('f', 'force', False, _('force graft')),
      ('D', 'currentdate', False,
       _('record the current date as commit date')),
      ('U', 'currentuser', False,
@@ -3077,6 +3079,10 @@
 
       (grafted from CHANGESETHASH)
 
+    If --force is specified, revisions will be grafted even if they
+    are already ancestors of or have been grafted to the destination.
+    This is useful when the revisions have since been backed out.
+
     If a graft merge results in conflicts, the graft process is
     interrupted so that the current merge can be manually resolved.
     Once all conflicts are addressed, the graft process can be
@@ -3084,7 +3090,8 @@
 
     .. note::
 
-      The -c/--continue option does not reapply earlier options.
+      The -c/--continue option does not reapply earlier options, except
+      for --force.
 
     .. container:: verbose
 
@@ -3121,7 +3128,7 @@
     if not opts.get('date') and opts.get('currentdate'):
         opts['date'] = "%d %d" % util.makedate()
 
-    editor = cmdutil.getcommiteditor(**opts)
+    editor = cmdutil.getcommiteditor(editform='graft', **opts)
 
     cont = False
     if opts['continue']:
@@ -3150,52 +3157,59 @@
     if not revs:
         return -1
 
-    # check for ancestors of dest branch
-    crev = repo['.'].rev()
-    ancestors = repo.changelog.ancestors([crev], inclusive=True)
-    # Cannot use x.remove(y) on smart set, this has to be a list.
-    # XXX make this lazy in the future
-    revs = list(revs)
-    # don't mutate while iterating, create a copy
-    for rev in list(revs):
-        if rev in ancestors:
-            ui.warn(_('skipping ancestor revision %s\n') % rev)
-            # XXX remove on list is slow
-            revs.remove(rev)
-    if not revs:
-        return -1
-
-    # analyze revs for earlier grafts
-    ids = {}
-    for ctx in repo.set("%ld", revs):
-        ids[ctx.hex()] = ctx.rev()
-        n = ctx.extra().get('source')
-        if n:
-            ids[n] = ctx.rev()
-
-    # check ancestors for earlier grafts
-    ui.debug('scanning for duplicate grafts\n')
-
-    for rev in repo.changelog.findmissingrevs(revs, [crev]):
-        ctx = repo[rev]
-        n = ctx.extra().get('source')
-        if n in ids:
-            r = repo[n].rev()
-            if r in revs:
-                ui.warn(_('skipping revision %s (already grafted to %s)\n')
-                        % (r, rev))
+    # Don't check in the --continue case, in effect retaining --force across
+    # --continues. That's because without --force, any revisions we decided to
+    # skip would have been filtered out here, so they wouldn't have made their
+    # way to the graftstate. With --force, any revisions we would have otherwise
+    # skipped would not have been filtered out, and if they hadn't been applied
+    # already, they'd have been in the graftstate.
+    if not (cont or opts.get('force')):
+        # check for ancestors of dest branch
+        crev = repo['.'].rev()
+        ancestors = repo.changelog.ancestors([crev], inclusive=True)
+        # Cannot use x.remove(y) on smart set, this has to be a list.
+        # XXX make this lazy in the future
+        revs = list(revs)
+        # don't mutate while iterating, create a copy
+        for rev in list(revs):
+            if rev in ancestors:
+                ui.warn(_('skipping ancestor revision %s\n') % rev)
+                # XXX remove on list is slow
+                revs.remove(rev)
+        if not revs:
+            return -1
+
+        # analyze revs for earlier grafts
+        ids = {}
+        for ctx in repo.set("%ld", revs):
+            ids[ctx.hex()] = ctx.rev()
+            n = ctx.extra().get('source')
+            if n:
+                ids[n] = ctx.rev()
+
+        # check ancestors for earlier grafts
+        ui.debug('scanning for duplicate grafts\n')
+
+        for rev in repo.changelog.findmissingrevs(revs, [crev]):
+            ctx = repo[rev]
+            n = ctx.extra().get('source')
+            if n in ids:
+                r = repo[n].rev()
+                if r in revs:
+                    ui.warn(_('skipping revision %s (already grafted to %s)\n')
+                            % (r, rev))
+                    revs.remove(r)
+                elif ids[n] in revs:
+                    ui.warn(_('skipping already grafted revision %s '
+                                '(%s also has origin %d)\n') % (ids[n], rev, r))
+                    revs.remove(ids[n])
+            elif ctx.hex() in ids:
+                r = ids[ctx.hex()]
+                ui.warn(_('skipping already grafted revision %s '
+                                '(was grafted from %d)\n') % (r, rev))
                 revs.remove(r)
-            elif ids[n] in revs:
-                ui.warn(_('skipping already grafted revision %s '
-                            '(%s also has origin %d)\n') % (ids[n], rev, r))
-                revs.remove(ids[n])
-        elif ctx.hex() in ids:
-            r = ids[ctx.hex()]
-            ui.warn(_('skipping already grafted revision %s '
-                            '(was grafted from %d)\n') % (r, rev))
-            revs.remove(r)
-    if not revs:
-        return -1
+        if not revs:
+            return -1
 
     wlock = repo.wlock()
     try:
@@ -4027,11 +4041,11 @@
     rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
 
     ret = 1
-    m = scmutil.match(repo[rev], pats, opts, default='relglob')
+    ctx = repo[rev]
+    m = scmutil.match(ctx, pats, opts, default='relglob')
     m.bad = lambda x, y: False
-    for abs in repo[rev].walk(m):
-        if not rev and abs not in repo.dirstate:
-            continue
+
+    for abs in ctx.matches(m):
         if opts.get('fullpath'):
             ui.write(repo.wjoin(abs), end)
         else:
@@ -4560,17 +4574,22 @@
             ctx = repo[r]
             ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
     else:
+        tr = None
         lock = repo.lock()
         try:
+            tr = repo.transaction("phase")
             # set phase
             if not revs:
                 raise util.Abort(_('empty revision set'))
             nodes = [repo[r].node() for r in revs]
             olddata = repo._phasecache.getphaserevs(repo)[:]
-            phases.advanceboundary(repo, targetphase, nodes)
+            phases.advanceboundary(repo, tr, targetphase, nodes)
             if opts['force']:
-                phases.retractboundary(repo, targetphase, nodes)
+                phases.retractboundary(repo, tr, targetphase, nodes)
+            tr.close()
         finally:
+            if tr is not None:
+                tr.release()
             lock.release()
         # moving revision from public to draft may hide them
         # We have to check result on an unfiltered repository
@@ -5785,7 +5804,11 @@
         if date:
             date = util.parsedate(date)
 
-        editor = cmdutil.getcommiteditor(**opts)
+        if opts.get('remove'):
+            editform = 'tag.remove'
+        else:
+            editform = 'tag.add'
+        editor = cmdutil.getcommiteditor(editform=editform, **opts)
 
         # don't allow tagging the null rev
         if (not opts.get('remove') and
--- a/mercurial/config.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/config.py	Mon Aug 11 11:24:05 2014 -0500
@@ -76,7 +76,7 @@
             # no data before, remove everything
             section, item = data
             if section in self._data:
-                del self._data[section][item]
+                self._data[section].pop(item, None)
             self._source.pop((section, item), None)
 
     def parse(self, src, data, sections=None, remap=None, include=None):
--- a/mercurial/context.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/context.py	Mon Aug 11 11:24:05 2014 -0500
@@ -276,7 +276,7 @@
     def dirs(self):
         return self._dirs
 
-    def dirty(self):
+    def dirty(self, missing=False, merge=True, branch=True):
         return False
 
     def status(self, other=None, match=None, listignored=False,
@@ -598,6 +598,9 @@
                 continue
             match.bad(fn, _('no such file in rev %s') % self)
 
+    def matches(self, match):
+        return self.walk(match)
+
 class basefilectx(object):
     """A filecontext object represents the common logic for its children:
     filectx: read-only access to a filerevision that is already present
@@ -711,6 +714,10 @@
             return util.binary(self.data())
         except IOError:
             return False
+    def isexec(self):
+        return 'x' in self.flags()
+    def islink(self):
+        return 'l' in self.flags()
 
     def cmp(self, fctx):
         """compare with other file context
@@ -1144,6 +1151,9 @@
         return sorted(self._repo.dirstate.walk(match, sorted(self.substate),
                                                True, False))
 
+    def matches(self, match):
+        return sorted(self._repo.dirstate.matches(match))
+
     def ancestors(self):
         for a in self._repo.changelog.ancestors(
             [p.rev() for p in self._parents]):
@@ -1546,6 +1556,14 @@
         # invert comparison to reuse the same code path
         return fctx.cmp(self)
 
+    def remove(self, ignoremissing=False):
+        """wraps unlink for a repo's working directory"""
+        util.unlinkpath(self._repo.wjoin(self._path), ignoremissing)
+
+    def write(self, data, flags):
+        """wraps repo.wwrite"""
+        self._repo.wwrite(self._path, data, flags)
+
 class memctx(committablectx):
     """Use memctx to perform in-memory commits via localrepo.commitctx().
 
@@ -1586,6 +1604,20 @@
         self._filectxfn = filectxfn
         self.substate = {}
 
+        # if store is not callable, wrap it in a function
+        if not callable(filectxfn):
+            def getfilectx(repo, memctx, path):
+                fctx = filectxfn[path]
+                # this is weird but apparently we only keep track of one parent
+                # (why not only store that instead of a tuple?)
+                copied = fctx.renamed()
+                if copied:
+                    copied = copied[0]
+                return memfilectx(repo, path, fctx.data(),
+                                  islink=fctx.islink(), isexec=fctx.isexec(),
+                                  copied=copied, memctx=memctx)
+            self._filectxfn = getfilectx
+
         self._extra = extra and extra.copy() or {}
         if self._extra.get('branch', '') == '':
             self._extra['branch'] = 'default'
@@ -1613,7 +1645,7 @@
         for f, fnode in man.iteritems():
             p1node = nullid
             p2node = nullid
-            p = pctx[f].parents()
+            p = pctx[f].parents() # if file isn't in pctx, check p2?
             if len(p) > 0:
                 p1node = p[0].node()
                 if len(p) > 1:
@@ -1650,9 +1682,14 @@
         return len(self.data())
     def flags(self):
         return self._flags
-    def isexec(self):
-        return 'x' in self._flags
-    def islink(self):
-        return 'l' in self._flags
     def renamed(self):
         return self._copied
+
+    def remove(self, ignoremissing=False):
+        """wraps unlink for a repo's working directory"""
+        # need to figure out what to do here
+        del self._changectx[self._path]
+
+    def write(self, data, flags):
+        """wraps repo.wwrite"""
+        self._data = data
--- a/mercurial/dirstate.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/dirstate.py	Mon Aug 11 11:24:05 2014 -0500
@@ -873,3 +873,21 @@
 
         return (lookup, modified, added, removed, deleted, unknown, ignored,
                 clean)
+
+    def matches(self, match):
+        '''
+        return files in the dirstate (in whatever state) filtered by match
+        '''
+        dmap = self._map
+        if match.always():
+            return dmap.keys()
+        files = match.files()
+        if match.matchfn == match.exact:
+            # fast path -- filter the other way around, since typically files is
+            # much smaller than dmap
+            return [f for f in files if f in dmap]
+        if not match.anypats() and util.all(fn in dmap for fn in files):
+            # fast path -- all the values are known to be files, so just return
+            # that
+            return list(files)
+        return [f for f in dmap if match(f)]
--- a/mercurial/exchange.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/exchange.py	Mon Aug 11 11:24:05 2014 -0500
@@ -77,8 +77,57 @@
         self.remoteheads = None
         # testable as a boolean indicating if any nodes are missing locally.
         self.incoming = None
-        # set of all heads common after changeset bundle push
-        self.commonheads = None
+        # phases changes that must be pushed along side the changesets
+        self.outdatedphases = None
+        # phases changes that must be pushed if changeset push fails
+        self.fallbackoutdatedphases = None
+        # outgoing obsmarkers
+        self.outobsmarkers = set()
+
+    @util.propertycache
+    def futureheads(self):
+        """future remote heads if the changeset push succeeds"""
+        return self.outgoing.missingheads
+
+    @util.propertycache
+    def fallbackheads(self):
+        """future remote heads if the changeset push fails"""
+        if self.revs is None:
+            # not target to push, all common are relevant
+            return self.outgoing.commonheads
+        unfi = self.repo.unfiltered()
+        # I want cheads = heads(::missingheads and ::commonheads)
+        # (missingheads is revs with secret changeset filtered out)
+        #
+        # This can be expressed as:
+        #     cheads = ( (missingheads and ::commonheads)
+        #              + (commonheads and ::missingheads))"
+        #              )
+        #
+        # while trying to push we already computed the following:
+        #     common = (::commonheads)
+        #     missing = ((commonheads::missingheads) - commonheads)
+        #
+        # We can pick:
+        # * missingheads part of common (::commonheads)
+        common = set(self.outgoing.common)
+        nm = self.repo.changelog.nodemap
+        cheads = [node for node in self.revs if nm[node] in common]
+        # and
+        # * commonheads parents on missing
+        revset = unfi.set('%ln and parents(roots(%ln))',
+                         self.outgoing.commonheads,
+                         self.outgoing.missing)
+        cheads.extend(c.node() for c in revset)
+        return cheads
+
+    @property
+    def commonheads(self):
+        """set of all common heads after changeset bundle push"""
+        if self.ret:
+            return self.futureheads
+        else:
+            return self.fallbackheads
 
 def push(repo, remote, force=False, revs=None, newbranch=False):
     '''Push outgoing changesets (limited by revs) from a local
@@ -136,7 +185,6 @@
                 and pushop.remote.capable('bundle2-exp')):
                 _pushbundle2(pushop)
             _pushchangeset(pushop)
-            _pushcomputecommonheads(pushop)
             _pushsyncphase(pushop)
             _pushobsolete(pushop)
         finally:
@@ -149,8 +197,39 @@
     _pushbookmark(pushop)
     return pushop.ret
 
+# list of steps to perform discovery before push
+pushdiscoveryorder = []
+
+# Mapping between step name and function
+#
+# This exists to help extensions wrap steps if necessary
+pushdiscoverymapping = {}
+
+def pushdiscovery(stepname):
+    """decorator for function performing discovery before push
+
+    The function is added to the step -> function mapping and appended to the
+    list of steps.  Beware that decorated function will be added in order (this
+    may matter).
+
+    You can only use this decorator for a new step, if you want to wrap a step
+    from an extension, change the pushdiscovery dictionary directly."""
+    def dec(func):
+        assert stepname not in pushdiscoverymapping
+        pushdiscoverymapping[stepname] = func
+        pushdiscoveryorder.append(stepname)
+        return func
+    return dec
+
 def _pushdiscovery(pushop):
-    # discovery
+    """Run all discovery steps"""
+    for stepname in pushdiscoveryorder:
+        step = pushdiscoverymapping[stepname]
+        step(pushop)
+
+@pushdiscovery('changeset')
+def _pushdiscoverychangeset(pushop):
+    """discover the changeset that need to be pushed"""
     unfi = pushop.repo.unfiltered()
     fci = discovery.findcommonincoming
     commoninc = fci(unfi, pushop.remote, force=pushop.force)
@@ -162,6 +241,45 @@
     pushop.remoteheads = remoteheads
     pushop.incoming = inc
 
+@pushdiscovery('phase')
+def _pushdiscoveryphase(pushop):
+    """discover the phase that needs to be pushed
+
+    (computed for both success and failure case for changesets push)"""
+    outgoing = pushop.outgoing
+    unfi = pushop.repo.unfiltered()
+    remotephases = pushop.remote.listkeys('phases')
+    publishing = remotephases.get('publishing', False)
+    ana = phases.analyzeremotephases(pushop.repo,
+                                     pushop.fallbackheads,
+                                     remotephases)
+    pheads, droots = ana
+    extracond = ''
+    if not publishing:
+        extracond = ' and public()'
+    revset = 'heads((%%ln::%%ln) %s)' % extracond
+    # Get the list of all revs draft on remote by public here.
+    # XXX Beware that revset break if droots is not strictly
+    # XXX root we may want to ensure it is but it is costly
+    fallback = list(unfi.set(revset, droots, pushop.fallbackheads))
+    if not outgoing.missing:
+        future = fallback
+    else:
+        # adds changeset we are going to push as draft
+        #
+        # should not be necessary for pushblishing server, but because of an
+        # issue fixed in xxxxx we have to do it anyway.
+        fdroots = list(unfi.set('roots(%ln  + %ln::)',
+                       outgoing.missing, droots))
+        fdroots = [f.node() for f in fdroots]
+        future = list(unfi.set(revset, fdroots, pushop.futureheads))
+    pushop.outdatedphases = future
+    pushop.fallbackoutdatedphases = fallback
+
+@pushdiscovery('obsmarker')
+def _pushdiscoveryobsmarkers(pushop):
+    pushop.outobsmarkers = pushop.repo.obsstore
+
 def _pushcheckoutgoing(pushop):
     outgoing = pushop.outgoing
     unfi = pushop.repo.unfiltered()
@@ -201,6 +319,31 @@
                              newbm)
     return True
 
+# List of names of steps to perform for an outgoing bundle2, order matters.
+b2partsgenorder = []
+
+# Mapping between step name and function
+#
+# This exists to help extensions wrap steps if necessary
+b2partsgenmapping = {}
+
+def b2partsgenerator(stepname):
+    """decorator for function generating bundle2 part
+
+    The function is added to the step -> function mapping and appended to the
+    list of steps.  Beware that decorated functions will be added in order
+    (this may matter).
+
+    You can only use this decorator for new steps, if you want to wrap a step
+    from an extension, attack the b2partsgenmapping dictionary directly."""
+    def dec(func):
+        assert stepname not in b2partsgenmapping
+        b2partsgenmapping[stepname] = func
+        b2partsgenorder.append(stepname)
+        return func
+    return dec
+
+@b2partsgenerator('changeset')
 def _pushb2ctx(pushop, bundler):
     """handle changegroup push through bundle2
 
@@ -227,8 +370,37 @@
         pushop.ret = cgreplies['changegroup'][0]['return']
     return handlereply
 
-# list of function that may decide to add parts to an outgoing bundle2
-bundle2partsgenerators = [_pushb2ctx]
+@b2partsgenerator('phase')
+def _pushb2phases(pushop, bundler):
+    """handle phase push through bundle2"""
+    if 'phases' in pushop.stepsdone:
+        return
+    b2caps = bundle2.bundle2caps(pushop.remote)
+    if not 'b2x:pushkey' in b2caps:
+        return
+    pushop.stepsdone.add('phases')
+    part2node = []
+    enc = pushkey.encode
+    for newremotehead in pushop.outdatedphases:
+        part = bundler.newpart('b2x:pushkey')
+        part.addparam('namespace', enc('phases'))
+        part.addparam('key', enc(newremotehead.hex()))
+        part.addparam('old', enc(str(phases.draft)))
+        part.addparam('new', enc(str(phases.public)))
+        part2node.append((part.id, newremotehead))
+    def handlereply(op):
+        for partid, node in part2node:
+            partrep = op.records.getreplies(partid)
+            results = partrep['pushkey']
+            assert len(results) <= 1
+            msg = None
+            if not results:
+                msg = _('server ignored update of %s to public!\n') % node
+            elif not int(results[0]['return']):
+                msg = _('updating %s to public failed!\n') % node
+            if msg is not None:
+                pushop.ui.warn(msg)
+    return handlereply
 
 def _pushbundle2(pushop):
     """push data to the remote using bundle2
@@ -240,7 +412,8 @@
     capsblob = bundle2.encodecaps(pushop.repo.bundle2caps)
     bundler.newpart('b2x:replycaps', data=capsblob)
     replyhandlers = []
-    for partgen in bundle2partsgenerators:
+    for partgenname in b2partsgenorder:
+        partgen = b2partsgenmapping[partgenname]
         ret = partgen(pushop, bundler)
         if callable(ret):
             replyhandlers.append(ret)
@@ -307,43 +480,8 @@
         # change
         pushop.ret = pushop.remote.addchangegroup(cg, 'push', pushop.repo.url())
 
-def _pushcomputecommonheads(pushop):
-    unfi = pushop.repo.unfiltered()
-    if pushop.ret:
-        # push succeed, synchronize target of the push
-        cheads = pushop.outgoing.missingheads
-    elif pushop.revs is None:
-        # All out push fails. synchronize all common
-        cheads = pushop.outgoing.commonheads
-    else:
-        # I want cheads = heads(::missingheads and ::commonheads)
-        # (missingheads is revs with secret changeset filtered out)
-        #
-        # This can be expressed as:
-        #     cheads = ( (missingheads and ::commonheads)
-        #              + (commonheads and ::missingheads))"
-        #              )
-        #
-        # while trying to push we already computed the following:
-        #     common = (::commonheads)
-        #     missing = ((commonheads::missingheads) - commonheads)
-        #
-        # We can pick:
-        # * missingheads part of common (::commonheads)
-        common = set(pushop.outgoing.common)
-        nm = pushop.repo.changelog.nodemap
-        cheads = [node for node in pushop.revs if nm[node] in common]
-        # and
-        # * commonheads parents on missing
-        revset = unfi.set('%ln and parents(roots(%ln))',
-                         pushop.outgoing.commonheads,
-                         pushop.outgoing.missing)
-        cheads.extend(c.node() for c in revset)
-    pushop.commonheads = cheads
-
 def _pushsyncphase(pushop):
     """synchronise phase information locally and remotely"""
-    unfi = pushop.repo.unfiltered()
     cheads = pushop.commonheads
     # even when we don't push, exchanging phase data is useful
     remotephases = pushop.remote.listkeys('phases')
@@ -376,12 +514,18 @@
             _localphasemove(pushop, cheads, phases.draft)
         ### Apply local phase on remote
 
-        # Get the list of all revs draft on remote by public here.
-        # XXX Beware that revset break if droots is not strictly
-        # XXX root we may want to ensure it is but it is costly
-        outdated = unfi.set('heads((%ln::%ln) and public())',
-                            droots, cheads)
+        if pushop.ret:
+            if 'phases' in pushop.stepsdone:
+                # phases already pushed though bundle2
+                return
+            outdated = pushop.outdatedphases
+        else:
+            outdated = pushop.fallbackoutdatedphases
 
+        pushop.stepsdone.add('phases')
+
+        # filter heads already turned public by the push
+        outdated = [c for c in outdated if c.node() not in pheads]
         b2caps = bundle2.bundle2caps(pushop.remote)
         if 'b2x:pushkey' in b2caps:
             # server supports bundle2, let's do a batched push through it
@@ -431,7 +575,12 @@
 def _localphasemove(pushop, nodes, phase=phases.public):
     """move <nodes> to <phase> in the local source repo"""
     if pushop.locallocked:
-        phases.advanceboundary(pushop.repo, phase, nodes)
+        tr = pushop.repo.transaction('push-phase-sync')
+        try:
+            phases.advanceboundary(pushop.repo, tr, phase, nodes)
+            tr.close()
+        finally:
+            tr.release()
     else:
         # repo is not locked, do not change any phases!
         # Informs the user that phases should have been moved when
@@ -444,13 +593,16 @@
 
 def _pushobsolete(pushop):
     """utility function to push obsolete markers to a remote"""
+    if 'obsmarkers' in pushop.stepsdone:
+        return
     pushop.ui.debug('try to push obsolete markers to remote\n')
     repo = pushop.repo
     remote = pushop.remote
+    pushop.stepsdone.add('obsmarkers')
     if (obsolete._enabled and repo.obsstore and
         'obsolete' in remote.listkeys('namespaces')):
         rslts = []
-        remotedata = repo.listkeys('obsolete')
+        remotedata = obsolete._pushkeyescape(pushop.outobsmarkers)
         for key in sorted(remotedata, reverse=True):
             # reverse sort to ensure we end with dump0
             data = remotedata[key]
@@ -673,14 +825,29 @@
         pheads, _dr = phases.analyzeremotephases(pullop.repo,
                                                  pullop.pulledsubset,
                                                  remotephases)
-        phases.advanceboundary(pullop.repo, phases.public, pheads)
-        phases.advanceboundary(pullop.repo, phases.draft,
-                               pullop.pulledsubset)
+        dheads = pullop.pulledsubset
     else:
         # Remote is old or publishing all common changesets
         # should be seen as public
-        phases.advanceboundary(pullop.repo, phases.public,
-                               pullop.pulledsubset)
+        pheads = pullop.pulledsubset
+        dheads = []
+    unfi = pullop.repo.unfiltered()
+    phase = unfi._phasecache.phase
+    rev = unfi.changelog.nodemap.get
+    public = phases.public
+    draft = phases.draft
+
+    # exclude changesets already public locally and update the others
+    pheads = [pn for pn in pheads if phase(unfi, rev(pn)) > public]
+    if pheads:
+        tr = pullop.gettransaction()
+        phases.advanceboundary(pullop.repo, tr, public, pheads)
+
+    # exclude changesets already draft locally and update the others
+    dheads = [pn for pn in dheads if phase(unfi, rev(pn)) > draft]
+    if dheads:
+        tr = pullop.gettransaction()
+        phases.advanceboundary(pullop.repo, tr, draft, dheads)
 
 def _pullobsolete(pullop):
     """utility function to pull obsolete markers from a remote
@@ -726,9 +893,13 @@
     The implementation is at a very early stage and will get massive rework
     when the API of bundle is refined.
     """
-    # build changegroup bundle here.
-    cg = changegroup.getbundle(repo, source, heads=heads,
-                               common=common, bundlecaps=bundlecaps)
+    cg = None
+    if kwargs.get('cg', True):
+        # build changegroup bundle here.
+        cg = changegroup.getbundle(repo, source, heads=heads,
+                                   common=common, bundlecaps=bundlecaps)
+    elif 'HG2X' not in bundlecaps:
+        raise ValueError(_('request for bundle10 must include changegroup'))
     if bundlecaps is None or 'HG2X' not in bundlecaps:
         if kwargs:
             raise ValueError(_('unsupported getbundle arguments: %s')
--- a/mercurial/filemerge.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/filemerge.py	Mon Aug 11 11:24:05 2014 -0500
@@ -178,24 +178,30 @@
 
     ui = repo.ui
 
+    validkeep = ['keep', 'keep-merge3']
+
     # do we attempt to simplemerge first?
     try:
         premerge = _toolbool(ui, tool, "premerge", not binary)
     except error.ConfigError:
         premerge = _toolstr(ui, tool, "premerge").lower()
-        valid = 'keep'.split()
-        if premerge not in valid:
-            _valid = ', '.join(["'" + v + "'" for v in valid])
+        if premerge not in validkeep:
+            _valid = ', '.join(["'" + v + "'" for v in validkeep])
             raise error.ConfigError(_("%s.premerge not valid "
                                       "('%s' is neither boolean nor %s)") %
                                     (tool, premerge, _valid))
 
     if premerge:
+        if premerge == 'keep-merge3':
+            if not labels:
+                labels = _defaultconflictlabels
+            if len(labels) < 3:
+                labels.append('base')
         r = simplemerge.simplemerge(ui, a, b, c, quiet=True, label=labels)
         if not r:
             ui.debug(" premerge successful\n")
             return 0
-        if premerge != 'keep':
+        if premerge not in validkeep:
             util.copyfile(back, a) # restore from backup and try again
     return 1 # continue merging
 
@@ -206,7 +212,8 @@
     """
     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."""
+    the partially merged file. Markers will have two sections, one for each side
+    of merge."""
     tool, toolpath, binary, symlink = toolconf
     if symlink:
         repo.ui.warn(_('warning: internal:merge cannot merge symlinks '
@@ -218,10 +225,25 @@
 
         ui = repo.ui
 
-        r = simplemerge.simplemerge(ui, a, b, c, label=labels, no_minimal=True)
+        r = simplemerge.simplemerge(ui, a, b, c, label=labels)
         return True, r
     return False, 0
 
+@internaltool('merge3', True,
+              _("merging %s incomplete! "
+                "(edit conflicts, then use 'hg resolve --mark')\n"))
+def _imerge3(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
+    """
+    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."""
+    if not labels:
+        labels = _defaultconflictlabels
+    if len(labels) < 3:
+        labels.append('base')
+    return _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels)
+
 @internaltool('tagmerge', True,
               _("automatic tag merging of %s failed! "
                 "(use 'hg resolve --tool internal:merge' or another merge "
@@ -312,23 +334,27 @@
 
 _defaultconflictlabels = ['local', 'other']
 
-def _formatlabels(repo, fcd, fco, labels):
+def _formatlabels(repo, fcd, fco, fca, labels):
     """Formats the given labels using the conflict marker template.
 
     Returns a list of formatted labels.
     """
     cd = fcd.changectx()
     co = fco.changectx()
+    ca = fca.changectx()
 
     ui = repo.ui
     template = ui.config('ui', 'mergemarkertemplate', _defaultconflictmarker)
     template = templater.parsestring(template, quoted=False)
-    tmpl = templater.templater(None, cache={ 'conflictmarker' : template })
+    tmpl = templater.templater(None, cache={'conflictmarker': template})
+
+    pad = max(len(l) for l in labels)
 
-    pad = max(len(labels[0]), len(labels[1]))
-
-    return [_formatconflictmarker(repo, cd, tmpl, labels[0], pad),
-            _formatconflictmarker(repo, co, tmpl, labels[1], pad)]
+    newlabels = [_formatconflictmarker(repo, cd, tmpl, labels[0], pad),
+                 _formatconflictmarker(repo, co, tmpl, labels[1], pad)]
+    if len(labels) > 2:
+        newlabels.append(_formatconflictmarker(repo, ca, tmpl, labels[2], pad))
+    return newlabels
 
 def filemerge(repo, mynode, orig, fcd, fco, fca, labels=None):
     """perform a 3-way merge in the working directory
@@ -388,16 +414,13 @@
     ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca))
 
     markerstyle = ui.config('ui', 'mergemarkers', 'basic')
-    if markerstyle == 'basic':
-        formattedlabels = _defaultconflictlabels
-    else:
-        if not labels:
-            labels = _defaultconflictlabels
-
-        formattedlabels = _formatlabels(repo, fcd, fco, labels)
+    if not labels:
+        labels = _defaultconflictlabels
+    if markerstyle != 'basic':
+        labels = _formatlabels(repo, fcd, fco, fca, labels)
 
     needcheck, r = func(repo, mynode, orig, fcd, fco, fca, toolconf,
-                        (a, b, c, back), labels=formattedlabels)
+                        (a, b, c, back), labels=labels)
     if not needcheck:
         if r:
             if onfailure:
--- a/mercurial/help/config.txt	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/help/config.txt	Mon Aug 11 11:24:05 2014 -0500
@@ -388,6 +388,48 @@
 - :hg:`tag`
 - :hg:`transplant`
 
+Configuring items below instead of ``changeset`` allows showing
+customized message only for specific actions, or showing different
+messages for each actions.
+
+- ``changeset.backout`` for :hg:`backout`
+- ``changeset.commit.amend`` for :hg:`commit --amend`
+- ``changeset.commit.normal`` for :hg:`commit` without ``--amend``
+- ``changeset.fetch`` for :hg:`fetch` (impling merge commit)
+- ``changeset.gpg.sign`` for :hg:`sign`
+- ``changeset.graft`` for :hg:`graft`
+- ``changeset.histedit.edit`` for ``edit`` of :hg:`histedit`
+- ``changeset.histedit.fold`` for ``fold`` of :hg:`histedit`
+- ``changeset.histedit.mess`` for ``mess`` of :hg:`histedit`
+- ``changeset.histedit.pick`` for ``pick`` of :hg:`histedit`
+- ``changeset.import.bypass`` for :hg:`import --bypass`
+- ``changeset.import.normal`` for :hg:`import` without ``--bypass``
+- ``changeset.mq.qnew`` for :hg:`qnew`
+- ``changeset.mq.qfold`` for :hg:`qfold`
+- ``changeset.mq.qrefresh`` for :hg:`qrefresh`
+- ``changeset.rebase.collapse`` for :hg:`rebase --collapse`
+- ``changeset.rebase.normal`` for :hg:`rebase` without ``--collapse``
+- ``changeset.shelve.shelve`` for :hg:`shelve`
+- ``changeset.tag.add`` for :hg:`tag` without ``--remove``
+- ``changeset.tag.remove`` for :hg:`tag --remove`
+- ``changeset.transplant`` for :hg:`transplant`
+
+These dot-separated lists of names are treated as hierarchical ones.
+For example, ``changeset.tag.remove`` customizes the commit message
+only for :hg:`tag --remove`, but ``changeset.tag`` customizes the
+commit message for :hg:`tag` regardless of ``--remove`` option.
+
+In this section, items other than ``changeset`` can be referred from
+others. For example, the configuration to list committed files up
+below can be referred as ``{listupfiles}``::
+
+    [committemplate]
+    listupfiles = {file_adds %
+       "HG: added {file}\n"     }{file_mods %
+       "HG: changed {file}\n"   }{file_dels %
+       "HG: removed {file}\n"   }{if(files, "",
+       "HG: no files changed\n")}
+
 ``decode/encode``
 -----------------
 
@@ -912,8 +954,10 @@
 
 ``premerge``
   Attempt to run internal non-interactive 3-way merge tool before
-  launching external tool.  Options are ``true``, ``false``, or ``keep``
-  to leave markers in the file if the premerge fails.
+  launching external tool.  Options are ``true``, ``false``, ``keep`` or
+  ``keep-merge3``. The ``keep`` option will leave markers in the file if the
+  premerge fails. The ``keep-merge3`` will do the same but include information
+  about the base of the merge in the marker (see internal:merge3).
   Default: True
 
 ``binary``
--- a/mercurial/i18n.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/i18n.py	Mon Aug 11 11:24:05 2014 -0500
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 import encoding
-import gettext, sys, os
+import gettext, sys, os, locale
 
 # modelled after templater.templatepath:
 if getattr(sys, 'frozen', None) is not None:
@@ -20,7 +20,25 @@
     if os.path.isdir(localedir):
         break
 
-t = gettext.translation('hg', localedir, fallback=True)
+_languages = None
+if (os.name == 'nt'
+    and 'LANGUAGE' not in os.environ
+    and 'LC_ALL' not in os.environ
+    and 'LC_MESSAGES' not in os.environ
+    and 'LANG' not in os.environ):
+    # Try to detect UI language by "User Interface Language Management" API
+    # if no locale variables are set. Note that locale.getdefaultlocale()
+    # uses GetLocaleInfo(), which may be different from UI language.
+    # (See http://msdn.microsoft.com/en-us/library/dd374098(v=VS.85).aspx )
+    try:
+        import ctypes
+        langid = ctypes.windll.kernel32.GetUserDefaultUILanguage()
+        _languages = [locale.windows_locale[langid]]
+    except (ImportError, AttributeError, KeyError):
+        # ctypes not found or unknown langid
+        pass
+
+t = gettext.translation('hg', localedir, _languages, fallback=True)
 
 def gettext(message):
     """Translate message.
--- a/mercurial/localrepo.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/localrepo.py	Mon Aug 11 11:24:05 2014 -0500
@@ -182,7 +182,9 @@
 
     bundle2caps = {'HG2X': (),
                    'b2x:listkeys': (),
-                   'b2x:pushkey': ()}
+                   'b2x:pushkey': (),
+                   'b2x:changegroup': (),
+                  }
 
     # a list of (ui, featureset) functions.
     # only functions defined in module of enabled extensions are invoked
@@ -1087,8 +1089,6 @@
             return l
 
         def unlock():
-            if hasunfilteredcache(self, '_phasecache'):
-                self._phasecache.write()
             for k, ce in self._filecache.items():
                 if k == 'dirstate' or k not in self.__dict__:
                     continue
@@ -1440,7 +1440,7 @@
                 # be compliant anyway
                 #
                 # if minimal phase was 0 we don't need to retract anything
-                phases.retractboundary(self, targetphase, [n])
+                phases.retractboundary(self, tr, targetphase, [n])
             tr.close()
             branchmap.updatecache(self.filtered('served'))
             return n
--- a/mercurial/phases.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/phases.py	Mon Aug 11 11:24:05 2014 -0500
@@ -196,19 +196,24 @@
             return
         f = self.opener('phaseroots', 'w', atomictemp=True)
         try:
-            for phase, roots in enumerate(self.phaseroots):
-                for h in roots:
-                    f.write('%i %s\n' % (phase, hex(h)))
+            self._write(f)
         finally:
             f.close()
+
+    def _write(self, fp):
+        for phase, roots in enumerate(self.phaseroots):
+            for h in roots:
+                fp.write('%i %s\n' % (phase, hex(h)))
         self.dirty = False
 
-    def _updateroots(self, phase, newroots):
+    def _updateroots(self, phase, newroots, tr):
         self.phaseroots[phase] = newroots
         self._phaserevs = None
         self.dirty = True
 
-    def advanceboundary(self, repo, targetphase, nodes):
+        tr.addfilegenerator('phase', ('phaseroots',), self._write)
+
+    def advanceboundary(self, repo, tr, targetphase, nodes):
         # Be careful to preserve shallow-copied values: do not update
         # phaseroots values, replace them.
 
@@ -224,15 +229,15 @@
             roots = set(ctx.node() for ctx in repo.set(
                     'roots((%ln::) - (%ln::%ln))', olds, olds, nodes))
             if olds != roots:
-                self._updateroots(phase, roots)
+                self._updateroots(phase, roots, tr)
                 # some roots may need to be declared for lower phases
                 delroots.extend(olds - roots)
             # declare deleted root in the target phase
             if targetphase != 0:
-                self.retractboundary(repo, targetphase, delroots)
+                self.retractboundary(repo, tr, targetphase, delroots)
         repo.invalidatevolatilesets()
 
-    def retractboundary(self, repo, targetphase, nodes):
+    def retractboundary(self, repo, tr, targetphase, nodes):
         # Be careful to preserve shallow-copied values: do not update
         # phaseroots values, replace them.
 
@@ -247,7 +252,7 @@
             currentroots.update(newroots)
             ctxs = repo.set('roots(%ln::)', currentroots)
             currentroots.intersection_update(ctx.node() for ctx in ctxs)
-            self._updateroots(targetphase, currentroots)
+            self._updateroots(targetphase, currentroots, tr)
         repo.invalidatevolatilesets()
 
     def filterunknown(self, repo):
@@ -278,7 +283,7 @@
         # (see branchmap one)
         self._phaserevs = None
 
-def advanceboundary(repo, targetphase, nodes):
+def advanceboundary(repo, tr, targetphase, nodes):
     """Add nodes to a phase changing other nodes phases if necessary.
 
     This function move boundary *forward* this means that all nodes
@@ -286,10 +291,10 @@
 
     Simplify boundary to contains phase roots only."""
     phcache = repo._phasecache.copy()
-    phcache.advanceboundary(repo, targetphase, nodes)
+    phcache.advanceboundary(repo, tr, targetphase, nodes)
     repo._phasecache.replace(phcache)
 
-def retractboundary(repo, targetphase, nodes):
+def retractboundary(repo, tr, targetphase, nodes):
     """Set nodes back to a phase changing other nodes phases if
     necessary.
 
@@ -298,7 +303,7 @@
 
     Simplify boundary to contains phase roots only."""
     phcache = repo._phasecache.copy()
-    phcache.retractboundary(repo, targetphase, nodes)
+    phcache.retractboundary(repo, tr, targetphase, nodes)
     repo._phasecache.replace(phcache)
 
 def listphases(repo):
@@ -331,13 +336,16 @@
 def pushphase(repo, nhex, oldphasestr, newphasestr):
     """List phases root for serialization over pushkey"""
     repo = repo.unfiltered()
+    tr = None
     lock = repo.lock()
     try:
         currentphase = repo[nhex].phase()
         newphase = abs(int(newphasestr)) # let's avoid negative index surprise
         oldphase = abs(int(oldphasestr)) # let's avoid negative index surprise
         if currentphase == oldphase and newphase < oldphase:
-            advanceboundary(repo, newphase, [bin(nhex)])
+            tr = repo.transaction('pushkey-phase')
+            advanceboundary(repo, tr, newphase, [bin(nhex)])
+            tr.close()
             return 1
         elif currentphase == newphase:
             # raced, but got correct result
@@ -345,6 +353,8 @@
         else:
             return 0
     finally:
+        if tr:
+            tr.release()
         lock.release()
 
 def analyzeremotephases(repo, subset, roots):
--- a/mercurial/repair.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/repair.py	Mon Aug 11 11:24:05 2014 -0500
@@ -47,7 +47,13 @@
 
     return s
 
-def strip(ui, repo, nodelist, backup="all", topic='backup'):
+def strip(ui, repo, nodelist, backup=True, topic='backup'):
+
+    # Simple way to maintain backwards compatibility for this
+    # argument.
+    if backup in ['none', 'strip']:
+        backup = False
+
     repo = repo.unfiltered()
     repo.destroying()
 
@@ -58,8 +64,6 @@
     striplist = [cl.rev(node) for node in nodelist]
     striprev = min(striplist)
 
-    keeppartialbundle = backup == 'strip'
-
     # Some revisions with rev > striprev may not be descendants of striprev.
     # We have to find these revisions and put them in a bundle, so that
     # we can restore them after the truncations.
@@ -109,7 +113,7 @@
     # create a changegroup for all the branches we need to keep
     backupfile = None
     vfs = repo.vfs
-    if backup == "all":
+    if backup:
         backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
         repo.ui.status(_("saved backup bundle to %s\n") %
                        vfs.join(backupfile))
@@ -118,7 +122,7 @@
     if saveheads or savebases:
         # do not compress partial bundle if we remove it from disk later
         chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
-                            compress=keeppartialbundle)
+                            compress=False)
 
     mfst = repo.manifest
 
@@ -156,8 +160,6 @@
             if not repo.ui.verbose:
                 repo.ui.popbuffer()
             f.close()
-            if not keeppartialbundle:
-                vfs.unlink(chgrpfile)
 
         # remove undo files
         for undovfs, undofile in repo.undofiles():
@@ -179,5 +181,9 @@
             ui.warn(_("strip failed, partial bundle stored in '%s'\n")
                     % vfs.join(chgrpfile))
         raise
+    else:
+        if saveheads or savebases:
+            # Remove partial backup only if there were no exceptions
+            vfs.unlink(chgrpfile)
 
     repo.destroyed()
--- a/mercurial/simplemerge.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/simplemerge.py	Mon Aug 11 11:24:05 2014 -0500
@@ -82,8 +82,7 @@
                     start_marker='<<<<<<<',
                     mid_marker='=======',
                     end_marker='>>>>>>>',
-                    base_marker=None,
-                    reprocess=False):
+                    base_marker=None):
         """Return merge in cvs-like form.
         """
         self.conflicts = False
@@ -93,8 +92,6 @@
                 newline = '\r\n'
             elif self.a[0].endswith('\r'):
                 newline = '\r'
-        if base_marker and reprocess:
-            raise CantReprocessAndShowBase
         if name_a:
             start_marker = start_marker + ' ' + name_a
         if name_b:
@@ -102,8 +99,6 @@
         if name_base and base_marker:
             base_marker = base_marker + ' ' + name_base
         merge_regions = self.merge_regions()
-        if reprocess is True:
-            merge_regions = self.reprocess_merge_regions(merge_regions)
         for t in merge_regions:
             what = t[0]
             if what == 'unchanged':
@@ -131,33 +126,6 @@
             else:
                 raise ValueError(what)
 
-    def merge_annotated(self):
-        """Return merge with conflicts, showing origin of lines.
-
-        Most useful for debugging merge.
-        """
-        for t in self.merge_regions():
-            what = t[0]
-            if what == 'unchanged':
-                for i in range(t[1], t[2]):
-                    yield 'u | ' + self.base[i]
-            elif what == 'a' or what == 'same':
-                for i in range(t[1], t[2]):
-                    yield what[0] + ' | ' + self.a[i]
-            elif what == 'b':
-                for i in range(t[1], t[2]):
-                    yield 'b | ' + self.b[i]
-            elif what == 'conflict':
-                yield '<<<<\n'
-                for i in range(t[3], t[4]):
-                    yield 'A | ' + self.a[i]
-                yield '----\n'
-                for i in range(t[5], t[6]):
-                    yield 'B | ' + self.b[i]
-                yield '>>>>\n'
-            else:
-                raise ValueError(what)
-
     def merge_groups(self):
         """Yield sequence of line groups.  Each one is a tuple:
 
@@ -278,42 +246,6 @@
                 ia = aend
                 ib = bend
 
-    def reprocess_merge_regions(self, merge_regions):
-        """Where there are conflict regions, remove the agreed lines.
-
-        Lines where both A and B have made the same changes are
-        eliminated.
-        """
-        for region in merge_regions:
-            if region[0] != "conflict":
-                yield region
-                continue
-            type, iz, zmatch, ia, amatch, ib, bmatch = region
-            a_region = self.a[ia:amatch]
-            b_region = self.b[ib:bmatch]
-            matches = mdiff.get_matching_blocks(''.join(a_region),
-                                                ''.join(b_region))
-            next_a = ia
-            next_b = ib
-            for region_ia, region_ib, region_len in matches[:-1]:
-                region_ia += ia
-                region_ib += ib
-                reg = self.mismatch_region(next_a, region_ia, next_b,
-                                           region_ib)
-                if reg is not None:
-                    yield reg
-                yield 'same', region_ia, region_len + region_ia
-                next_a = region_ia + region_len
-                next_b = region_ib + region_len
-            reg = self.mismatch_region(next_a, amatch, next_b, bmatch)
-            if reg is not None:
-                yield reg
-
-    def mismatch_region(next_a, region_ia,  next_b, region_ib):
-        if next_a < region_ia or next_b < region_ib:
-            return 'conflict', None, None, next_a, region_ia, next_b, region_ib
-    mismatch_region = staticmethod(mismatch_region)
-
     def find_sync_regions(self):
         """Return a list of sync regions, where both descendants match the base.
 
@@ -415,13 +347,16 @@
 
     name_a = local
     name_b = other
+    name_base = None
     labels = opts.get('label', [])
     if len(labels) > 0:
         name_a = labels[0]
     if len(labels) > 1:
         name_b = labels[1]
     if len(labels) > 2:
-        raise util.Abort(_("can only specify two labels."))
+        name_base = labels[2]
+    if len(labels) > 3:
+        raise util.Abort(_("can only specify three labels."))
 
     try:
         localtext = readfile(local)
@@ -437,11 +372,12 @@
     else:
         out = sys.stdout
 
-    reprocess = not opts.get('no_minimal')
-
     m3 = Merge3Text(basetext, localtext, othertext)
-    for line in m3.merge_lines(name_a=name_a, name_b=name_b,
-                               reprocess=reprocess):
+    extrakwargs = {}
+    if name_base is not None:
+        extrakwargs['base_marker'] = '|||||||'
+        extrakwargs['name_base'] = name_base
+    for line in m3.merge_lines(name_a=name_a, name_b=name_b, **extrakwargs):
         out.write(line)
 
     if not opts.get('print'):
--- a/mercurial/transaction.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/transaction.py	Mon Aug 11 11:24:05 2014 -0500
@@ -96,6 +96,9 @@
             opener.chmod(self.journal, createmode & 0666)
             opener.chmod(self.backupjournal, createmode & 0666)
 
+        # hold file generations to be performed on commit
+        self._filegenerators = {}
+
     def __del__(self):
         if self.journal:
             self._abort()
@@ -154,7 +157,7 @@
 
         if file in self.map or file in self.backupmap:
             return
-        backupfile = "journal.%s" % file
+        backupfile = "%s.backup.%s" % (self.journal, file)
         if self.opener.exists(file):
             filepath = self.opener.join(file)
             backuppath = self.opener.join(backupfile)
@@ -173,6 +176,28 @@
         self.backupsfile.flush()
 
     @active
+    def addfilegenerator(self, genid, filenames, genfunc, order=0):
+        """add a function to generates some files at transaction commit
+
+        The `genfunc` argument is a function capable of generating proper
+        content of each entry in the `filename` tuple.
+
+        At transaction close time, `genfunc` will be called with one file
+        object argument per entries in `filenames`.
+
+        The transaction itself is responsible for the backup, creation and
+        final write of such file.
+
+        The `genid` argument is used to ensure the same set of file is only
+        generated once. Call to `addfilegenerator` for a `genid` already
+        present will overwrite the old entry.
+
+        The `order` argument may be used to control the order in which multiple
+        generator will be executed.
+        """
+        self._filegenerators[genid] = (order, filenames, genfunc)
+
+    @active
     def find(self, file):
         if file in self.map:
             return self.entries[self.map[file]]
@@ -213,6 +238,18 @@
     @active
     def close(self):
         '''commit the transaction'''
+        # write files registered for generation
+        for order, filenames, genfunc in sorted(self._filegenerators.values()):
+            files = []
+            try:
+                for name in filenames:
+                    self.addbackup(name)
+                    files.append(self.opener(name, 'w', atomictemp=True))
+                genfunc(*files)
+            finally:
+                for f in files:
+                    f.close()
+
         if self.count == 1 and self.onclose is not None:
             self.onclose()
 
--- a/mercurial/wireproto.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/mercurial/wireproto.py	Mon Aug 11 11:24:05 2014 -0500
@@ -203,7 +203,8 @@
 gboptsmap = {'heads':  'nodes',
              'common': 'nodes',
              'bundlecaps': 'csv',
-             'listkeys': 'csv'}
+             'listkeys': 'csv',
+             'cg': 'boolean'}
 
 # client side
 
@@ -349,6 +350,8 @@
                 value = encodelist(value)
             elif keytype == 'csv':
                 value = ','.join(value)
+            elif keytype == 'boolean':
+                value = bool(value)
             elif keytype != 'plain':
                 raise KeyError('unknown getbundle option type %s'
                                % keytype)
@@ -652,6 +655,8 @@
             opts[k] = decodelist(v)
         elif keytype == 'csv':
             opts[k] = set(v.split(','))
+        elif keytype == 'boolean':
+            opts[k] = '%i' % bool(v)
         elif keytype != 'plain':
             raise KeyError('unknown getbundle option type %s'
                            % keytype)
--- a/tests/hghave.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/hghave.py	Mon Aug 11 11:24:05 2014 -0500
@@ -5,6 +5,17 @@
 
 tempprefix = 'hg-hghave-'
 
+checks = {
+    "true": (lambda: True, "yak shaving"),
+    "false": (lambda: False, "nail clipper"),
+}
+
+def check(name, desc):
+    def decorator(func):
+        checks[name] = (func, desc)
+        return func
+    return decorator
+
 def matchoutput(cmd, regexp, ignorestatus=False):
     """Return True if cmd executes successfully and its output
     is matched by the supplied regular expression.
@@ -19,9 +30,11 @@
         ret = 1
     return (ignorestatus or ret is None) and r.search(s)
 
+@check("baz", "GNU Arch baz client")
 def has_baz():
     return matchoutput('baz --version 2>&1', r'baz Bazaar version')
 
+@check("bzr", "Canonical's Bazaar client")
 def has_bzr():
     try:
         import bzrlib
@@ -29,6 +42,7 @@
     except ImportError:
         return False
 
+@check("bzr114", "Canonical's Bazaar client >= 1.14")
 def has_bzr114():
     try:
         import bzrlib
@@ -37,21 +51,26 @@
     except ImportError:
         return False
 
+@check("cvs", "cvs client/server")
 def has_cvs():
     re = r'Concurrent Versions System.*?server'
     return matchoutput('cvs --version 2>&1', re) and not has_msys()
 
+@check("cvs112", "cvs client/server >= 1.12")
 def has_cvs112():
     re = r'Concurrent Versions System \(CVS\) 1.12.*?server'
     return matchoutput('cvs --version 2>&1', re) and not has_msys()
 
+@check("darcs", "darcs client")
 def has_darcs():
     return matchoutput('darcs --version', r'2\.[2-9]', True)
 
+@check("mtn", "monotone client (>= 1.0)")
 def has_mtn():
     return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
         'mtn --version', r'monotone 0\.', True)
 
+@check("eol-in-paths", "end-of-lines in paths")
 def has_eol_in_paths():
     try:
         fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
@@ -61,6 +80,7 @@
     except (IOError, OSError):
         return False
 
+@check("execbit", "executable bit")
 def has_executablebit():
     try:
         EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
@@ -78,6 +98,7 @@
         return False
     return not (new_file_has_exec or exec_flags_cannot_flip)
 
+@check("icasefs", "case insensitive file system")
 def has_icasefs():
     # Stolen from mercurial.util
     fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
@@ -96,6 +117,7 @@
     finally:
         os.remove(path)
 
+@check("fifo", "named pipes")
 def has_fifo():
     if getattr(os, "mkfifo", None) is None:
         return False
@@ -107,9 +129,11 @@
     except OSError:
         return False
 
+@check("killdaemons", 'killdaemons.py support')
 def has_killdaemons():
     return True
 
+@check("cacheable", "cacheable filesystem")
 def has_cacheable_fs():
     from mercurial import util
 
@@ -120,6 +144,7 @@
     finally:
         os.remove(path)
 
+@check("lsprof", "python lsprof module")
 def has_lsprof():
     try:
         import _lsprof
@@ -127,12 +152,15 @@
     except ImportError:
         return False
 
+@check("gettext", "GNU Gettext (msgfmt)")
 def has_gettext():
     return matchoutput('msgfmt --version', 'GNU gettext-tools')
 
+@check("git", "git command line client")
 def has_git():
     return matchoutput('git --version 2>&1', r'^git version')
 
+@check("docutils", "Docutils text processing library")
 def has_docutils():
     try:
         from docutils.core import publish_cmdline
@@ -146,16 +174,20 @@
         return (0, 0)
     return (int(m.group(1)), int(m.group(2)))
 
+@check("svn15", "subversion client and admin tools >= 1.5")
 def has_svn15():
     return getsvnversion() >= (1, 5)
 
+@check("svn13", "subversion client and admin tools >= 1.3")
 def has_svn13():
     return getsvnversion() >= (1, 3)
 
+@check("svn", "subversion client and admin tools")
 def has_svn():
     return matchoutput('svn --version 2>&1', r'^svn, version') and \
         matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
 
+@check("svn-bindings", "subversion python bindings")
 def has_svn_bindings():
     try:
         import svn.core
@@ -166,10 +198,12 @@
     except ImportError:
         return False
 
+@check("p4", "Perforce server and client")
 def has_p4():
     return (matchoutput('p4 -V', r'Rev\. P4/') and
             matchoutput('p4d -V', r'Rev\. P4D/'))
 
+@check("symlink", "symbolic links")
 def has_symlink():
     if getattr(os, "symlink", None) is None:
         return False
@@ -181,6 +215,7 @@
     except (OSError, AttributeError):
         return False
 
+@check("hardlink", "hardlinks")
 def has_hardlink():
     from mercurial import util
     fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
@@ -196,12 +231,15 @@
     finally:
         os.unlink(fn)
 
+@check("tla", "GNU Arch tla client")
 def has_tla():
     return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
 
+@check("gpg", "gpg client")
 def has_gpg():
     return matchoutput('gpg --version 2>&1', r'GnuPG')
 
+@check("unix-permissions", "unix-style permissions")
 def has_unix_permissions():
     d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
     try:
@@ -218,14 +256,17 @@
     finally:
         os.rmdir(d)
 
+@check("root", "root permissions")
 def has_root():
     return getattr(os, 'geteuid', None) and os.geteuid() == 0
 
+@check("pyflakes", "Pyflakes python linter")
 def has_pyflakes():
     return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
                        r"<stdin>:1: 're' imported but unused",
                        True)
 
+@check("pygments", "Pygments source highlighting library")
 def has_pygments():
     try:
         import pygments
@@ -233,14 +274,17 @@
     except ImportError:
         return False
 
+@check("python243", "python >= 2.4.3")
 def has_python243():
     return sys.version_info >= (2, 4, 3)
 
+@check("outer-repo", "outer repo")
 def has_outer_repo():
     # failing for other reasons than 'no repo' imply that there is a repo
     return not matchoutput('hg root 2>&1',
                            r'abort: no repository found', True)
 
+@check("ssl", "python >= 2.6 ssl module and python OpenSSL")
 def has_ssl():
     try:
         import ssl
@@ -250,19 +294,24 @@
     except ImportError:
         return False
 
+@check("windows", "Windows")
 def has_windows():
     return os.name == 'nt'
 
+@check("system-sh", "system() uses sh")
 def has_system_sh():
     return os.name != 'nt'
 
+@check("serve", "platform and python can manage 'hg serve -d'")
 def has_serve():
     return os.name != 'nt' # gross approximation
 
+@check("test-repo", "running tests from repository")
 def has_test_repo():
     t = os.environ["TESTDIR"]
     return os.path.isdir(os.path.join(t, "..", ".hg"))
 
+@check("tic", "terminfo compiler and curses module")
 def has_tic():
     try:
         import curses
@@ -271,63 +320,20 @@
     except ImportError:
         return False
 
+@check("msys", "Windows with MSYS")
 def has_msys():
     return os.getenv('MSYSTEM')
 
+@check("aix", "AIX")
 def has_aix():
     return sys.platform.startswith("aix")
 
+@check("absimport", "absolute_import in __future__")
 def has_absimport():
     import __future__
     from mercurial import util
     return util.safehasattr(__future__, "absolute_import")
 
+@check("py3k", "running with Python 3.x")
 def has_py3k():
     return 3 == sys.version_info[0]
-
-checks = {
-    "true": (lambda: True, "yak shaving"),
-    "false": (lambda: False, "nail clipper"),
-    "baz": (has_baz, "GNU Arch baz client"),
-    "bzr": (has_bzr, "Canonical's Bazaar client"),
-    "bzr114": (has_bzr114, "Canonical's Bazaar client >= 1.14"),
-    "cacheable": (has_cacheable_fs, "cacheable filesystem"),
-    "cvs": (has_cvs, "cvs client/server"),
-    "cvs112": (has_cvs112, "cvs client/server >= 1.12"),
-    "darcs": (has_darcs, "darcs client"),
-    "docutils": (has_docutils, "Docutils text processing library"),
-    "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"),
-    "execbit": (has_executablebit, "executable bit"),
-    "fifo": (has_fifo, "named pipes"),
-    "gettext": (has_gettext, "GNU Gettext (msgfmt)"),
-    "git": (has_git, "git command line client"),
-    "gpg": (has_gpg, "gpg client"),
-    "hardlink": (has_hardlink, "hardlinks"),
-    "icasefs": (has_icasefs, "case insensitive file system"),
-    "killdaemons": (has_killdaemons, 'killdaemons.py support'),
-    "lsprof": (has_lsprof, "python lsprof module"),
-    "mtn": (has_mtn, "monotone client (>= 1.0)"),
-    "outer-repo": (has_outer_repo, "outer repo"),
-    "p4": (has_p4, "Perforce server and client"),
-    "pyflakes": (has_pyflakes, "Pyflakes python linter"),
-    "pygments": (has_pygments, "Pygments source highlighting library"),
-    "python243": (has_python243, "python >= 2.4.3"),
-    "root": (has_root, "root permissions"),
-    "serve": (has_serve, "platform and python can manage 'hg serve -d'"),
-    "ssl": (has_ssl, "python >= 2.6 ssl module and python OpenSSL"),
-    "svn": (has_svn, "subversion client and admin tools"),
-    "svn13": (has_svn13, "subversion client and admin tools >= 1.3"),
-    "svn15": (has_svn15, "subversion client and admin tools >= 1.5"),
-    "svn-bindings": (has_svn_bindings, "subversion python bindings"),
-    "symlink": (has_symlink, "symbolic links"),
-    "system-sh": (has_system_sh, "system() uses sh"),
-    "test-repo": (has_test_repo, "running tests from repository"),
-    "tic": (has_tic, "terminfo compiler and curses module"),
-    "tla": (has_tla, "GNU Arch tla client"),
-    "unix-permissions": (has_unix_permissions, "unix-style permissions"),
-    "windows": (has_windows, "Windows"),
-    "msys": (has_msys, "Windows with MSYS"),
-    "aix": (has_aix, "AIX"),
-    "absimport": (has_absimport, "absolute_import in __future__"),
-    "py3k": (has_py3k, "running with Python 3.x"),
-}
--- a/tests/run-tests.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/run-tests.py	Mon Aug 11 11:24:05 2014 -0500
@@ -57,6 +57,7 @@
 import threading
 import killdaemons as killmod
 import Queue as queue
+from xml.dom import minidom
 import unittest
 
 processlock = threading.Lock()
@@ -190,6 +191,8 @@
              " (implies --keep-tmpdir)")
     parser.add_option("-v", "--verbose", action="store_true",
         help="output verbose messages")
+    parser.add_option("--xunit", type="string",
+                      help="record xunit results at specified path")
     parser.add_option("--view", type="string",
         help="external diff viewer")
     parser.add_option("--with-hg", type="string",
@@ -304,6 +307,20 @@
 
     return log(*msg)
 
+# Bytes that break XML even in a CDATA block: control characters 0-31
+# sans \t, \n and \r
+CDATA_EVIL = re.compile(r"[\000-\010\013\014\016-\037]")
+
+def cdatasafe(data):
+    """Make a string safe to include in a CDATA block.
+
+    Certain control characters are illegal in a CDATA block, and
+    there's no way to include a ]]> in a CDATA either. This function
+    replaces illegal bytes with ? and adds a space between the ]] so
+    that it won't break the CDATA block.
+    """
+    return CDATA_EVIL.sub('?', data).replace(']]>', '] ]>')
+
 def log(*msg):
     """Log something to stdout.
 
@@ -460,8 +477,15 @@
                 raise
             except SkipTest, e:
                 result.addSkip(self, str(e))
+                # The base class will have already counted this as a
+                # test we "ran", but we want to exclude skipped tests
+                # from those we count towards those run.
+                result.testsRun -= 1
             except IgnoreTest, e:
                 result.addIgnore(self, str(e))
+                # As with skips, ignores also should be excluded from
+                # the number of tests executed.
+                result.testsRun -= 1
             except WarnTest, e:
                 result.addWarn(self, str(e))
             except self.failureException, e:
@@ -786,7 +810,15 @@
         for n, l in enumerate(lines):
             if not l.endswith('\n'):
                 l += '\n'
-            if l.startswith('#if'):
+            if l.startswith('#require'):
+                lsplit = l.split()
+                if len(lsplit) < 2 or lsplit[0] != '#require':
+                    after.setdefault(pos, []).append('  !!! invalid #require\n')
+                if not self._hghave(lsplit[1:]):
+                    script = ["exit 80\n"]
+                    break
+                after.setdefault(pos, []).append(l)
+            elif l.startswith('#if'):
                 lsplit = l.split()
                 if len(lsplit) < 2 or lsplit[0] != '#if':
                     after.setdefault(pos, []).append('  !!! invalid #if\n')
@@ -1077,6 +1109,10 @@
 
         self.times = []
         self._started = {}
+        self._stopped = {}
+        # Data stored for the benefit of generating xunit reports.
+        self.successes = []
+        self.faildata = {}
 
     def addFailure(self, test, reason):
         self.failures.append((test, reason))
@@ -1084,21 +1120,25 @@
         if self._options.first:
             self.stop()
         else:
+            iolock.acquire()
             if not self._options.nodiff:
                 self.stream.write('\nERROR: %s output changed\n' % test)
 
             self.stream.write('!')
+            iolock.release()
 
-    def addError(self, *args, **kwargs):
-        super(TestResult, self).addError(*args, **kwargs)
+    def addSuccess(self, test):
+        super(TestResult, self).addSuccess(test)
+        self.successes.append(test)
 
+    def addError(self, test, err):
+        super(TestResult, self).addError(test, err)
         if self._options.first:
             self.stop()
 
     # Polyfill.
     def addSkip(self, test, reason):
         self.skipped.append((test, reason))
-
         if self.showAll:
             self.stream.writeln('skipped %s' % reason)
         else:
@@ -1107,12 +1147,13 @@
 
     def addIgnore(self, test, reason):
         self.ignored.append((test, reason))
-
         if self.showAll:
             self.stream.writeln('ignored %s' % reason)
         else:
             if reason != 'not retesting':
                 self.stream.write('i')
+            else:
+                self.testsRun += 1
             self.stream.flush()
 
     def addWarn(self, test, reason):
@@ -1131,6 +1172,8 @@
         """Record a mismatch in test output for a particular test."""
 
         accepted = False
+        failed = False
+        lines = []
 
         iolock.acquire()
         if self._options.nodiff:
@@ -1159,7 +1202,8 @@
                     else:
                         rename(test.errpath, '%s.out' % test.path)
                     accepted = True
-
+            if not accepted and not failed:
+                self.faildata[test.name] = ''.join(lines)
         iolock.release()
 
         return accepted
@@ -1167,17 +1211,28 @@
     def startTest(self, test):
         super(TestResult, self).startTest(test)
 
-        self._started[test.name] = time.time()
+        # os.times module computes the user time and system time spent by
+        # child's processes along with real elapsed time taken by a process.
+        # This module has one limitation. It can only work for Linux user
+        # and not for Windows.
+        self._started[test.name] = os.times()
 
     def stopTest(self, test, interrupted=False):
         super(TestResult, self).stopTest(test)
 
-        self.times.append((test.name, time.time() - self._started[test.name]))
+        self._stopped[test.name] = os.times()
+
+        starttime = self._started[test.name]
+        endtime = self._stopped[test.name]
+        self.times.append((test.name, endtime[2] - starttime[2],
+                    endtime[3] - starttime[3], endtime[4] - starttime[4]))
+
         del self._started[test.name]
+        del self._stopped[test.name]
 
         if interrupted:
             self.stream.writeln('INTERRUPTED: %s (after %d seconds)' % (
-                test.name, self.times[-1][1]))
+                test.name, self.times[-1][3]))
 
 class TestSuite(unittest.TestSuite):
     """Custom unitest TestSuite that knows how to execute Mercurial tests."""
@@ -1323,20 +1378,39 @@
         for test, msg in result.errors:
             self.stream.writeln('Errored %s: %s' % (test.name, msg))
 
+        if self._runner.options.xunit:
+            xuf = open(self._runner.options.xunit, 'wb')
+            try:
+                timesd = dict(
+                    (test, real) for test, cuser, csys, real in result.times)
+                doc = minidom.Document()
+                s = doc.createElement('testsuite')
+                s.setAttribute('name', 'run-tests')
+                s.setAttribute('tests', str(result.testsRun))
+                s.setAttribute('errors', "0") # TODO
+                s.setAttribute('failures', str(failed))
+                s.setAttribute('skipped', str(skipped + ignored))
+                doc.appendChild(s)
+                for tc in result.successes:
+                    t = doc.createElement('testcase')
+                    t.setAttribute('name', tc.name)
+                    t.setAttribute('time', '%.3f' % timesd[tc.name])
+                    s.appendChild(t)
+                for tc, err in sorted(result.faildata.iteritems()):
+                    t = doc.createElement('testcase')
+                    t.setAttribute('name', tc)
+                    t.setAttribute('time', '%.3f' % timesd[tc])
+                    cd = doc.createCDATASection(cdatasafe(err))
+                    t.appendChild(cd)
+                    s.appendChild(t)
+                xuf.write(doc.toprettyxml(indent='  ', encoding='utf-8'))
+            finally:
+                xuf.close()
+
         self._runner._checkhglib('Tested')
 
-        # When '--retest' is enabled, only failure tests run. At this point
-        # "result.testsRun" holds the count of failure test that has run. But
-        # as while printing output, we have subtracted the skipped and ignored
-        # count from "result.testsRun". Therefore, to make the count remain
-        # the same, we need to add skipped and ignored count in here.
-        if self._runner.options.retest:
-            result.testsRun = result.testsRun + skipped + ignored
-
-        # This differs from unittest's default output in that we don't count
-        # skipped and ignored tests as part of the total test count.
         self.stream.writeln('# Ran %d tests, %d skipped, %d warned, %d failed.'
-            % (result.testsRun - skipped - ignored,
+            % (result.testsRun,
                skipped + ignored, warned, failed))
         if failed:
             self.stream.writeln('python hash seed: %s' %
@@ -1348,11 +1422,12 @@
 
     def printtimes(self, times):
         self.stream.writeln('# Producing time report')
-        times.sort(key=lambda t: (t[1], t[0]), reverse=True)
-        cols = '%7.3f   %s'
-        self.stream.writeln('%-7s   %s' % ('Time', 'Test'))
-        for test, timetaken in times:
-            self.stream.writeln(cols % (timetaken, test))
+        times.sort(key=lambda t: (t[3]))
+        cols = '%7.3f %7.3f %7.3f   %s'
+        self.stream.writeln('%-7s %-7s %-7s   %s' % ('cuser', 'csys', 'real',
+                    'Test'))
+        for test, cuser, csys, real in times:
+            self.stream.writeln(cols % (cuser, csys, real, test))
 
 class TestRunner(object):
     """Holds context for executing tests.
--- a/tests/test-acl.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-acl.t	Mon Aug 11 11:24:05 2014 -0500
@@ -82,6 +82,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   listing keys for "bookmarks"
   3 changesets found
   list of changesets:
@@ -140,6 +141,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -202,6 +204,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -274,6 +277,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -341,6 +345,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -413,6 +418,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -482,6 +488,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -556,6 +563,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -627,6 +635,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -700,6 +709,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -779,6 +789,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -859,6 +870,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -934,6 +946,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -1020,6 +1033,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -1100,6 +1114,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -1176,6 +1191,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -1252,6 +1268,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -1329,6 +1346,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   invalid branchheads cache (served): tip differs
   listing keys for "bookmarks"
   3 changesets found
@@ -1444,6 +1462,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   listing keys for "bookmarks"
   4 changesets found
   list of changesets:
@@ -1527,6 +1546,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   listing keys for "bookmarks"
   4 changesets found
   list of changesets:
@@ -1606,6 +1626,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   listing keys for "bookmarks"
   4 changesets found
   list of changesets:
@@ -1681,6 +1702,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   listing keys for "bookmarks"
   4 changesets found
   list of changesets:
@@ -1750,6 +1772,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   listing keys for "bookmarks"
   4 changesets found
   list of changesets:
@@ -1838,6 +1861,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   listing keys for "bookmarks"
   4 changesets found
   list of changesets:
@@ -1925,6 +1949,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   listing keys for "bookmarks"
   4 changesets found
   list of changesets:
@@ -1999,6 +2024,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   listing keys for "bookmarks"
   4 changesets found
   list of changesets:
@@ -2080,6 +2106,7 @@
   query 1; heads
   searching for changes
   all remote heads known locally
+  listing keys for "phases"
   listing keys for "bookmarks"
   4 changesets found
   list of changesets:
--- a/tests/test-archive-symlinks.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-archive-symlinks.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" symlink || exit 80
+#require symlink
 
   $ origdir=`pwd`
 
--- a/tests/test-archive.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-archive.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
   $ hg init test
   $ cd test
--- a/tests/test-bad-pull.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-bad-pull.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
 #if windows
   $ hg clone http://localhost:$HGPORT/ copy
--- a/tests/test-bookmarks-pushpull.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-bookmarks-pushpull.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
   $ cat << EOF >> $HGRCPATH
   > [ui]
--- a/tests/test-bundle2.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-bundle2.t	Mon Aug 11 11:24:05 2014 -0500
@@ -964,7 +964,8 @@
   >     raise util.Abort('Abandon ship!', hint="don't panic")
   > 
   > def uisetup(ui):
-  >     exchange.bundle2partsgenerators.insert(0, _pushbundle2failpart)
+  >     exchange.b2partsgenmapping['failpart'] = _pushbundle2failpart
+  >     exchange.b2partsgenorder.insert(0, 'failpart')
   > 
   > EOF
 
--- a/tests/test-casecollision-merge.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-casecollision-merge.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,6 +1,4 @@
-run only on case-insensitive filesystems
-
-  $ "$TESTDIR/hghave" icasefs || exit 80
+#require icasefs
 
 ################################
 test for branch merging
--- a/tests/test-casecollision.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-casecollision.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,6 +1,4 @@
-run only on case-sensitive filesystems
-
-  $ "$TESTDIR/hghave" no-icasefs || exit 80
+#require no-icasefs
 
 test file addition with colliding case
 
--- a/tests/test-casefolding.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-casefolding.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" icasefs || exit 80
+#require icasefs
 
   $ hg debugfs | grep 'case-sensitive:'
   case-sensitive: no
--- a/tests/test-changelog-exec.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-changelog-exec.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,9 +1,9 @@
+#require execbit
+
 b51a8138292a introduced a regression where we would mention in the
 changelog executable files added by the second parent of a merge. Test
 that that doesn't happen anymore
 
-  $ "$TESTDIR/hghave" execbit || exit 80
-
   $ hg init repo
   $ cd repo
   $ echo foo > foo
--- a/tests/test-check-code-hg.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-check-code-hg.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-#if test-repo
+#require test-repo
 
   $ check_code="$TESTDIR"/../contrib/check-code.py
   $ cd "$TESTDIR"/..
@@ -13,5 +13,3 @@
   Skipping mercurial/httpclient/__init__.py it has no-che?k-code (glob)
   Skipping mercurial/httpclient/_readers.py it has no-che?k-code (glob)
   Skipping mercurial/httpclient/socketutil.py it has no-che?k-code (glob)
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-check-commit-hg.t	Mon Aug 11 11:24:05 2014 -0500
@@ -0,0 +1,26 @@
+#require test-repo
+
+Enable obsolescence to avoid the warning issue when obsmarker are found
+
+  $ cat > obs.py << EOF
+  > import mercurial.obsolete
+  > mercurial.obsolete._enabled = True
+  > EOF
+  $ echo '[extensions]' >> $HGRCPATH
+  $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
+
+Go back in the hg repo
+
+  $ cd $TESTDIR/..
+
+  $ for node in `hg log --rev 'draft() and ::.' --template '{node|short}\n'`; do
+  >    hg export $node | contrib/check-commit > ${TESTTMP}/check-commit.out
+  >    if [ $? -ne 0 ]; then
+  >        echo "Revision $node does not comply to commit message rules"
+  >        echo '------------------------------------------------------'
+  >        cat ${TESTTMP}/check-commit.out
+  >        echo
+  >   fi
+  > done
+
+
--- a/tests/test-check-pyflakes.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-check-pyflakes.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-#if test-repo pyflakes
+#require test-repo pyflakes
 
   $ cd "`dirname "$TESTDIR"`"
 
@@ -19,4 +19,4 @@
   contrib/win32/hgwebdir_wsgi.py:93: 'from isapi.install import *' used; unable to detect undefined names (glob)
   tests/filterpyflakes.py:58: undefined name 'undefinedname'
   
-#endif
+
--- a/tests/test-clone-cgi.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-clone-cgi.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" no-msys || exit 80 # MSYS will translate web paths as if they were file paths
+#require no-msys # MSYS will translate web paths as if they were file paths
 
 This is a test of the wire protocol over CGI-based hgweb.
 initialize repository
--- a/tests/test-commit.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-commit.t	Mon Aug 11 11:24:05 2014 -0500
@@ -359,6 +359,20 @@
 
   $ cat >> .hg/hgrc <<EOF
   > [committemplate]
+  > changeset.commit.normal = HG: this is "commit.normal" template
+  >     HG: {extramsg}
+  >     {if(currentbookmark,
+  >    "HG: bookmark '{currentbookmark}' is activated\n",
+  >    "HG: no bookmark is activated\n")}{subrepos %
+  >    "HG: subrepo '{subrepo}' is changed\n"}
+  > 
+  > changeset.commit = HG: this is "commit" template
+  >     HG: {extramsg}
+  >     {if(currentbookmark,
+  >    "HG: bookmark '{currentbookmark}' is activated\n",
+  >    "HG: no bookmark is activated\n")}{subrepos %
+  >    "HG: subrepo '{subrepo}' is changed\n"}
+  > 
   > changeset = HG: this is customized commit template
   >     HG: {extramsg}
   >     {if(currentbookmark,
@@ -373,7 +387,7 @@
   $ echo 'sub2 = sub2' >> .hgsub
 
   $ HGEDITOR=cat hg commit -S -q
-  HG: this is customized commit template
+  HG: this is "commit.normal" template
   HG: Leave message empty to abort commit.
   HG: bookmark 'currentbookmark' is activated
   HG: subrepo 'sub' is changed
@@ -381,9 +395,28 @@
   abort: empty commit message
   [255]
 
+  $ cat >> .hg/hgrc <<EOF
+  > [committemplate]
+  > changeset.commit.normal =
+  > # now, "changeset.commit" should be chosen for "hg commit"
+  > EOF
+
   $ hg bookmark --inactive currentbookmark
   $ hg forget .hgsub
   $ HGEDITOR=cat hg commit -q
+  HG: this is "commit" template
+  HG: Leave message empty to abort commit.
+  HG: no bookmark is activated
+  abort: empty commit message
+  [255]
+
+  $ cat >> .hg/hgrc <<EOF
+  > [committemplate]
+  > changeset.commit =
+  > # now, "changeset" should be chosen for "hg commit"
+  > EOF
+
+  $ HGEDITOR=cat hg commit -q
   HG: this is customized commit template
   HG: Leave message empty to abort commit.
   HG: no bookmark is activated
--- a/tests/test-completion.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-completion.t	Mon Aug 11 11:24:05 2014 -0500
@@ -257,7 +257,7 @@
   debugsuccessorssets: 
   debugwalk: include, exclude
   debugwireargs: three, four, five, ssh, remotecmd, insecure
-  graft: rev, continue, edit, log, currentdate, currentuser, date, user, tool, dry-run
+  graft: rev, continue, edit, log, force, currentdate, currentuser, date, user, tool, dry-run
   grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
   heads: rev, topo, active, closed, style, template
   help: extension, command, keyword
--- a/tests/test-conflict.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-conflict.t	Mon Aug 11 11:24:05 2014 -0500
@@ -198,3 +198,37 @@
   5
   >>>>>>> other
   Hop we are done.
+
+internal:merge3
+
+  $ hg up -q --clean .
+
+  $ hg merge 1 --tool internal:merge3
+  merging a
+  warning: conflicts during merge.
+  merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+  [1]
+  $ cat a
+  Small Mathematical Series.
+  <<<<<<< local
+  1
+  2
+  3
+  6
+  8
+  ||||||| base
+  One
+  Two
+  Three
+  Four
+  Five
+  =======
+  1
+  2
+  3
+  4
+  5
+  >>>>>>> other
+  Hop we are done.
--- a/tests/test-contrib.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-contrib.t	Mon Aug 11 11:24:05 2014 -0500
@@ -143,23 +143,11 @@
   $ echo not other >> conflict-local
   $ echo end >> conflict-local
   $ echo end >> conflict-other
+
   $ python simplemerge -p conflict-local base conflict-other
   base
   <<<<<<< conflict-local
   not other
-  =======
-  other
-  >>>>>>> conflict-other
-  end
-  warning: conflicts during merge.
-  [1]
-
---no-minimal
-
-  $ python simplemerge -p --no-minimal conflict-local base conflict-other
-  base
-  <<<<<<< conflict-local
-  not other
   end
   =======
   other
@@ -174,10 +162,11 @@
   base
   <<<<<<< foo
   not other
+  end
   =======
   other
+  end
   >>>>>>> conflict-other
-  end
   warning: conflicts during merge.
   [1]
 
@@ -187,17 +176,33 @@
   base
   <<<<<<< foo
   not other
+  end
   =======
   other
+  end
   >>>>>>> bar
+  warning: conflicts during merge.
+  [1]
+
+3 labels
+
+  $ python simplemerge -p -L foo -L bar -L base conflict-local base conflict-other
+  base
+  <<<<<<< foo
+  not other
   end
+  ||||||| base
+  =======
+  other
+  end
+  >>>>>>> bar
   warning: conflicts during merge.
   [1]
 
 too many labels
 
-  $ python simplemerge -p -L foo -L bar -L baz conflict-local base conflict-other
-  abort: can only specify two labels.
+  $ python simplemerge -p -L foo -L bar -L baz -L buz conflict-local base conflict-other
+  abort: can only specify three labels.
   [255]
 
 binary file
@@ -231,7 +236,7 @@
    -L --label       labels to use on conflict markers
    -a --text        treat all files as text
    -p --print       print results instead of overwriting LOCAL
-      --no-minimal  do not try to minimize conflict regions
+      --no-minimal  no effect (DEPRECATED)
    -h --help        display help and exit
    -q --quiet       suppress output
 
@@ -251,7 +256,7 @@
    -L --label       labels to use on conflict markers
    -a --text        treat all files as text
    -p --print       print results instead of overwriting LOCAL
-      --no-minimal  do not try to minimize conflict regions
+      --no-minimal  no effect (DEPRECATED)
    -h --help        display help and exit
    -q --quiet       suppress output
   [1]
@@ -272,7 +277,7 @@
    -L --label       labels to use on conflict markers
    -a --text        treat all files as text
    -p --print       print results instead of overwriting LOCAL
-      --no-minimal  do not try to minimize conflict regions
+      --no-minimal  no effect (DEPRECATED)
    -h --help        display help and exit
    -q --quiet       suppress output
   [1]
--- a/tests/test-convert-baz.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-baz.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" baz symlink || exit 80
+#require baz symlink
 
   $ baz my-id "mercurial <mercurial@selenic.com>"
 
--- a/tests/test-convert-bzr-114.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-bzr-114.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,5 @@
+#require bzr114
 
-  $ "$TESTDIR/hghave" bzr114 || exit 80
   $ . "$TESTDIR/bzr-definitions"
 
 The file/directory replacement can only be reproduced on
--- a/tests/test-convert-cvs-branch.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-cvs-branch.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,7 +1,8 @@
+#require cvs
+
 This is http://mercurial.selenic.com/bts/issue1148
 and http://mercurial.selenic.com/bts/issue1447
 
-  $ "$TESTDIR/hghave" cvs || exit 80
   $ cvscall()
   > {
   >     cvs -f "$@" > /dev/null
--- a/tests/test-convert-cvs-detectmerge.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-cvs-detectmerge.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,8 +1,9 @@
+#require cvs
+
 Test config convert.cvsps.mergefrom config setting.
 (Should test similar mergeto feature, but I don't understand it yet.)
 Requires builtin cvsps.
 
-  $ "$TESTDIR/hghave" cvs || exit 80
   $ CVSROOT=`pwd`/cvsrepo
   $ export CVSROOT
 
--- a/tests/test-convert-cvs-synthetic.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-cvs-synthetic.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,6 +1,7 @@
+#require cvs112
+
 This feature requires use of builtin cvsps!
 
-  $ "$TESTDIR/hghave" cvs112 || exit 80
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "convert = " >> $HGRCPATH
 
--- a/tests/test-convert-cvs.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-cvs.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,5 @@
+#require cvs
 
-  $ "$TESTDIR/hghave" cvs || exit 80
   $ cvscall()
   > {
   >     cvs -f "$@"
--- a/tests/test-convert-cvsnt-mergepoints.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-cvsnt-mergepoints.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,5 @@
+#require cvs
 
-  $ "$TESTDIR/hghave" cvs || exit 80
   $ filterpath()
   > {
   >     eval "$@" | sed "s:$CVSROOT:*REPO*:g"
--- a/tests/test-convert-darcs.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-darcs.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,5 @@
+#require darcs
 
-  $ "$TESTDIR/hghave" darcs || exit 80
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "convert=" >> $HGRCPATH
   $ DARCS_EMAIL='test@example.org'; export DARCS_EMAIL
--- a/tests/test-convert-git.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-git.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,5 @@
+#require git
 
-  $ "$TESTDIR/hghave" git || exit 80
   $ echo "[core]" >> $HOME/.gitconfig
   $ echo "autocrlf = false" >> $HOME/.gitconfig
   $ echo "[core]" >> $HOME/.gitconfig
--- a/tests/test-convert-hg-svn.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-hg-svn.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,5 @@
+#require svn svn-bindings
 
-  $ "$TESTDIR/hghave" svn svn-bindings || exit 80
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "convert = " >> $HGRCPATH
   $ echo "mq = " >> $HGRCPATH
--- a/tests/test-convert-mtn.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-mtn.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,4 @@
-
-  $ "$TESTDIR/hghave" mtn || exit 80
+#require mtn
 
 Monotone directory is called .monotone on *nix and monotone
 on Windows.
--- a/tests/test-convert-p4-filetypes.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-p4-filetypes.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" p4 execbit symlink || exit 80
+#require p4 execbit symlink
 
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "convert = " >> $HGRCPATH
--- a/tests/test-convert-p4.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-p4.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" p4 || exit 80
+#require p4
 
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "convert = " >> $HGRCPATH
--- a/tests/test-convert-svn-branches.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-svn-branches.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,4 @@
-
-  $ "$TESTDIR/hghave" svn svn-bindings || exit 80
+#require svn svn-bindings
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
--- a/tests/test-convert-svn-encoding.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-svn-encoding.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,4 @@
-
-  $ "$TESTDIR/hghave" svn svn-bindings || exit 80
+#require svn svn-bindings
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
--- a/tests/test-convert-svn-move.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-svn-move.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,4 @@
-
-  $ "$TESTDIR/hghave" svn svn-bindings || exit 80
+#require svn svn-bindings
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
--- a/tests/test-convert-svn-sink.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-svn-sink.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" svn13 || exit 80
+#require svn13
 
   $ svnupanddisplay()
   > {
--- a/tests/test-convert-svn-source.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-svn-source.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,4 @@
-
-  $ "$TESTDIR/hghave" svn svn-bindings || exit 80
+#require svn svn-bindings
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
--- a/tests/test-convert-svn-startrev.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-svn-startrev.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,4 @@
-
-  $ "$TESTDIR/hghave" svn svn-bindings || exit 80
+#require svn svn-bindings
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
--- a/tests/test-convert-svn-tags.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-svn-tags.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,4 @@
-
-  $ "$TESTDIR/hghave" svn svn-bindings || exit 80
+#require svn svn-bindings
 
   $ cat >> $HGRCPATH <<EOF
   > [extensions]
--- a/tests/test-convert-tagsbranch-topology.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-tagsbranch-topology.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,5 @@
+#require git
 
-  $ "$TESTDIR/hghave" git || exit 80
   $ echo "[core]" >> $HOME/.gitconfig
   $ echo "autocrlf = false" >> $HOME/.gitconfig
   $ echo "[core]" >> $HOME/.gitconfig
--- a/tests/test-convert-tla.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-convert-tla.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,5 @@
+#require tla symlink
 
-  $ "$TESTDIR/hghave" tla symlink || exit 80
   $ tla my-id "mercurial <mercurial@selenic.com>"
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "convert=" >> $HGRCPATH
--- a/tests/test-diff-upgrade.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-diff-upgrade.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" execbit || exit 80
+#require execbit
 
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "autodiff=$TESTDIR/autodiff.py" >> $HGRCPATH
--- a/tests/test-eolfilename.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-eolfilename.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,6 +1,6 @@
-http://mercurial.selenic.com/bts/issue352
+#require eol-in-paths
 
-  $ "$TESTDIR/hghave" eol-in-paths || exit 80
+http://mercurial.selenic.com/bts/issue352
 
 test issue352
 
--- a/tests/test-execute-bit.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-execute-bit.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" execbit || exit 80
+#require execbit
 
   $ hg init
   $ echo a > a
--- a/tests/test-fetch.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-fetch.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "fetch=" >> $HGRCPATH
--- a/tests/test-flags.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-flags.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" execbit || exit 80
+#require execbit
 
   $ umask 027
 
--- a/tests/test-gendoc.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-gendoc.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,6 +1,7 @@
+#require docutils
+
 Test document extraction
 
-  $ "$TESTDIR/hghave" docutils || exit 80
   $ HGENCODING=UTF-8
   $ export HGENCODING
   $ { echo C; ls "$TESTDIR/../i18n"/*.po | sort; } | while read PO; do
--- a/tests/test-getbundle.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-getbundle.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
 = Test the getbundle() protocol function =
 
--- a/tests/test-glog.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-glog.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1645,13 +1645,28 @@
         ('symbol', 'filelog')
         ('string', 'aa'))))
 
-Test --follow on a directory
+Test --follow on a non-existent directory
 
   $ testlog -f dir
   abort: cannot follow file not in parent revision: "dir"
   abort: cannot follow file not in parent revision: "dir"
   abort: cannot follow file not in parent revision: "dir"
 
+Test --follow on a directory
+
+  $ hg up -q '.^'
+  $ testlog -f dir
+  []
+  (group
+    (func
+      ('symbol', '_matchfiles')
+      (list
+        (list
+          ('string', 'r:')
+          ('string', 'd:relpath'))
+        ('string', 'p:dir'))))
+  $ hg up -q tip
+
 Test --follow on file not in parent revision
 
   $ testlog -f a
@@ -1662,9 +1677,15 @@
 Test --follow and patterns
 
   $ testlog -f 'glob:*'
-  abort: can only follow copies/renames for explicit filenames
-  abort: can only follow copies/renames for explicit filenames
-  abort: can only follow copies/renames for explicit filenames
+  []
+  (group
+    (func
+      ('symbol', '_matchfiles')
+      (list
+        (list
+          ('string', 'r:')
+          ('string', 'd:relpath'))
+        ('string', 'p:glob:*'))))
 
 Test --follow on a single rename
 
@@ -1829,9 +1850,15 @@
           ('string', 'd:relpath'))
         ('string', 'p:a'))))
   $ testlog --removed --follow a
-  abort: can only follow copies/renames for explicit filenames
-  abort: can only follow copies/renames for explicit filenames
-  abort: can only follow copies/renames for explicit filenames
+  []
+  (group
+    (func
+      ('symbol', '_matchfiles')
+      (list
+        (list
+          ('string', 'r:')
+          ('string', 'd:relpath'))
+        ('string', 'p:a'))))
 
 Test --patch and --stat with --follow and --follow-first
 
--- a/tests/test-gpg.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-gpg.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,6 +1,7 @@
+#require gpg
+
 Test the GPG extension
 
-  $ "$TESTDIR/hghave" gpg || exit 80
   $ cat <<EOF >> $HGRCPATH
   > [extensions]
   > gpg=
--- a/tests/test-graft.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-graft.t	Mon Aug 11 11:24:05 2014 -0500
@@ -631,3 +631,50 @@
   grafting revision 13
   grafting revision 19
   merging b
+
+graft with --force (still doesn't graft merges)
+
+  $ hg graft 19 0 6
+  skipping ungraftable merge revision 6
+  skipping ancestor revision 0
+  skipping already grafted revision 19 (22 also has origin 2)
+  [255]
+  $ hg graft 19 0 6 --force
+  skipping ungraftable merge revision 6
+  grafting revision 19
+  merging b
+  grafting revision 0
+
+graft --force after backout
+
+  $ echo abc > a
+  $ hg ci -m 28
+  $ hg backout 28
+  reverting a
+  changeset 29:484c03b8dfa4 backs out changeset 28:6c56f0f7f033
+  $ hg graft 28
+  skipping ancestor revision 28
+  [255]
+  $ hg graft 28 --force
+  grafting revision 28
+  merging a
+  $ cat a
+  abc
+
+graft --continue after --force
+
+  $ hg backout 30
+  reverting a
+  changeset 31:3b96c18b7a1b backs out changeset 30:8f539994be33
+  $ hg graft 28 --force --tool internal:fail
+  grafting revision 28
+  abort: unresolved conflicts, can't continue
+  (use hg resolve and hg graft --continue)
+  [255]
+  $ hg resolve --all
+  merging a
+  (no more unresolved files)
+  $ hg graft -c
+  grafting revision 28
+  $ cat a
+  abc
--- a/tests/test-hardlinks.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-hardlinks.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" hardlink || exit 80
+#require hardlink
 
   $ cat > nlinks.py <<EOF
   > import sys
--- a/tests/test-hgweb-commands.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-hgweb-commands.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
 An attempt at more fully testing the hgweb web interface.
 The following things are tested elsewhere and are therefore omitted:
--- a/tests/test-hgweb-descend-empties.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-hgweb-descend-empties.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
 Test chains of near empty directories, terminating 3 different ways:
 - a1: file at level 4 (deepest)
--- a/tests/test-hgweb-diffs.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-hgweb-diffs.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
 setting up repo
 
--- a/tests/test-hgweb-empty.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-hgweb-empty.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
 Some tests for hgweb in an empty repository
 
--- a/tests/test-hgweb-filelog.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-hgweb-filelog.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
   $ hg init test
   $ cd test
--- a/tests/test-hgweb-raw.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-hgweb-raw.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
 Test raw style of hgweb
 
--- a/tests/test-hgweb-removed.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-hgweb-removed.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
 setting up repo
 
--- a/tests/test-hgweb.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-hgweb.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
 Some tests for hgweb. Tests static files, plain files and different 404's.
 
--- a/tests/test-hgwebdir.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-hgwebdir.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
 hide outer repo and work in dir without '.hg'
   $ hg init
--- a/tests/test-hgwebdirsym.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-hgwebdirsym.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,6 +1,6 @@
-Tests whether or not hgwebdir properly handles various symlink topologies.
+#require serve symlink
 
-  $ "$TESTDIR/hghave" serve symlink || exit 80
+Tests whether or not hgwebdir properly handles various symlink topologies.
 
 hide outer repo
   $ hg init
--- a/tests/test-highlight.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-highlight.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,5 @@
+#require pygments serve
 
-  $ "$TESTDIR/hghave" pygments serve || exit 80
   $ cat <<EOF >> $HGRCPATH
   > [extensions]
   > highlight =
--- a/tests/test-hook.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-hook.t	Mon Aug 11 11:24:05 2014 -0500
@@ -210,6 +210,7 @@
   $ hg push -B baz ../a
   pushing to ../a
   searching for changes
+  listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
   no changes found
   listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
   listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
--- a/tests/test-http-branchmap.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-http-branchmap.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
   $ hgserve() {
   >     hg serve -a localhost -p $HGPORT1 -d --pid-file=hg.pid \
--- a/tests/test-http-clone-r.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-http-clone-r.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
 creating 'remote
 
--- a/tests/test-http-proxy.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-http-proxy.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
   $ hg init a
   $ cd a
--- a/tests/test-http.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-http.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
   $ hg init test
   $ cd test
@@ -261,9 +261,10 @@
   "GET /?cmd=listkeys HTTP/1.1" 403 - x-hgarg-1:namespace=namespaces
   "GET /?cmd=capabilities HTTP/1.1" 200 -
   "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872
+  "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=phases
+  "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases
   "GET /?cmd=branchmap HTTP/1.1" 200 -
   "GET /?cmd=branchmap HTTP/1.1" 200 -
-  "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=bookmarks
   "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
   "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=686173686564+5eb5abfefeea63c80dd7553bcc3783f37e0c5524
   "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases
--- a/tests/test-https.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-https.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,6 +1,6 @@
-Proper https client requires the built-in ssl from Python 2.6.
+#require serve ssl
 
-  $ "$TESTDIR/hghave" serve ssl || exit 80
+Proper https client requires the built-in ssl from Python 2.6.
 
 Certificates created with:
  printf '.\n.\n.\n.\n.\nlocalhost\nhg@localhost\n' | \
--- a/tests/test-hup.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-hup.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,6 +1,7 @@
+#require serve fifo
+
 Test hangup signal in the middle of transaction
 
-  $ "$TESTDIR/hghave" serve fifo || exit 80
   $ hg init
   $ mkfifo p
   $ hg serve --stdio < p 1>out 2>&1 &
--- a/tests/test-i18n.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-i18n.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,6 +1,6 @@
-Translations are optional:
+#require gettext
 
-  $ "$TESTDIR/hghave" gettext || exit 80
+(Translations are optional)
 
 #if no-outer-repo
 
--- a/tests/test-identify.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-identify.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
 #if no-outer-repo
 
--- a/tests/test-incoming-outgoing.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-incoming-outgoing.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
   $ hg init test
   $ cd test
--- a/tests/test-inherit-mode.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-inherit-mode.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,7 +1,6 @@
-test that new files created in .hg inherit the permissions from .hg/store
+#require unix-permissions
 
-
-  $ "$TESTDIR/hghave" unix-permissions || exit 80
+test that new files created in .hg inherit the permissions from .hg/store
 
   $ mkdir dir
 
@@ -121,7 +120,6 @@
   00660 ../push/.hg/store/data/dir/bar.i
   00660 ../push/.hg/store/data/foo.i
   00660 ../push/.hg/store/fncache
-  00660 ../push/.hg/store/phaseroots
   00660 ../push/.hg/store/undo
   00660 ../push/.hg/store/undo.phaseroots
   00660 ../push/.hg/undo.bookmarks
--- a/tests/test-issue1438.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-issue1438.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,6 +1,6 @@
-http://mercurial.selenic.com/bts/issue1438
+#require symlink
 
-  $ "$TESTDIR/hghave" symlink || exit 80
+http://mercurial.selenic.com/bts/issue1438
 
   $ hg init
 
--- a/tests/test-issue1802.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-issue1802.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" execbit || exit 80
+#require execbit
 
 Create extension that can disable exec checks:
 
--- a/tests/test-known.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-known.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
 = Test the known() protocol function =
 
--- a/tests/test-largefiles-update.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-largefiles-update.t	Mon Aug 11 11:24:05 2014 -0500
@@ -99,4 +99,72 @@
   $ cat .hglf/large1
   58e24f733a964da346e2407a2bee99d9001184f5
 
+Test that "hg rollback" restores status of largefiles correctly
+
+  $ hg update -C -q
+  $ hg remove large1
+  $ hg forget large2
+  $ echo largeX > largeX
+  $ hg add --large largeX
+  $ hg commit -m 'will be rollback-ed soon'
+  $ echo largeY > largeY
+  $ hg add --large largeY
+  $ hg status -A large1
+  large1: No such file or directory
+  $ hg status -A large2
+  ? large2
+  $ hg status -A largeX
+  C largeX
+  $ hg status -A largeY
+  A largeY
+  $ hg rollback
+  repository tip rolled back to revision 3 (undo commit)
+  working directory now based on revision 3
+  $ hg status -A large1
+  R large1
+  $ hg status -A large2
+  R large2
+  $ hg status -A largeX
+  A largeX
+  $ hg status -A largeY
+  ? largeY
+
+Test that "hg status" shows status of largefiles correctly just after
+automated commit like rebase/transplant
+
+  $ cat >> .hg/hgrc <<EOF
+  > [extensions]
+  > rebase =
+  > strip =
+  > transplant =
+  > EOF
+  $ hg update -q -C 1
+  $ hg remove large1
+  $ echo largeX > largeX
+  $ hg add --large largeX
+  $ hg commit -m '#4'
+
+  $ hg rebase -s 1 -d 2 --keep
+  $ hg status -A large1
+  large1: No such file or directory
+  $ hg status -A largeX
+  C largeX
+  $ hg strip -q 5
+
+  $ hg update -q -C 2
+  $ hg transplant -q 1 4
+  $ hg status -A large1
+  large1: No such file or directory
+  $ hg status -A largeX
+  C largeX
+  $ hg strip -q 5
+
+  $ hg update -q -C 2
+  $ hg transplant -q --merge 1 --merge 4
+  $ hg status -A large1
+  large1: No such file or directory
+  $ hg status -A largeX
+  C largeX
+  $ hg strip -q 5
+
   $ cd ..
--- a/tests/test-lock-badness.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-lock-badness.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-#if unix-permissions no-root no-windows
+#require unix-permissions no-root no-windows
 
 Prepare
 
@@ -39,4 +39,3 @@
   [255]
 
   $ chmod 700 a/.hg/store
-#endif
--- a/tests/test-log.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-log.t	Mon Aug 11 11:24:05 2014 -0500
@@ -78,12 +78,52 @@
   summary:     c
   
 
--f, directory
+-f, non-existent directory
 
   $ hg log -f dir
   abort: cannot follow file not in parent revision: "dir"
   [255]
 
+-f, directory
+
+  $ hg up -q 3
+  $ hg log -f dir
+  changeset:   2:f8954cd4dc1f
+  user:        test
+  date:        Thu Jan 01 00:00:03 1970 +0000
+  summary:     c
+  
+-f, directory with --patch
+
+  $ hg log -f dir -p
+  changeset:   2:f8954cd4dc1f
+  user:        test
+  date:        Thu Jan 01 00:00:03 1970 +0000
+  summary:     c
+  
+  diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
+  --- /dev/null* (glob)
+  +++ b/dir/b* (glob)
+  @@ -0,0 +1,1 @@
+  +a
+  
+
+-f, pattern
+
+  $ hg log -f -I 'dir**' -p
+  changeset:   2:f8954cd4dc1f
+  user:        test
+  date:        Thu Jan 01 00:00:03 1970 +0000
+  summary:     c
+  
+  diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
+  --- /dev/null* (glob)
+  +++ b/dir/b* (glob)
+  @@ -0,0 +1,1 @@
+  +a
+  
+  $ hg up -q 4
+
 -f, a wrong style
 
   $ hg log -f -l1 --style something
--- a/tests/test-merge-tools.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-merge-tools.t	Mon Aug 11 11:24:05 2014 -0500
@@ -30,6 +30,14 @@
   $ echo "revision 3" >> f
   $ hg commit -Am "revision 3"
   created new head
+
+revision 4 - hard to merge
+
+  $ hg update 0 > /dev/null
+  $ echo "revision 4" > f
+  $ hg commit -Am "revision 4"
+  created new head
+
   $ echo "[merge-tools]" > .hg/hgrc
 
   $ beforemerge() {
@@ -701,6 +709,77 @@
   # hg stat
   M f
 
+premerge=keep keeps conflict markers in:
+
+  $ beforemerge
+  [merge-tools]
+  false.whatever=
+  true.priority=1
+  true.executable=cat
+  # hg update -C 1
+  $ hg merge -r 4 --config merge-tools.true.premerge=keep
+  merging f
+  <<<<<<< local: ef83787e2614  - test: revision 1
+  revision 1
+  space
+  =======
+  revision 4
+  >>>>>>> other: 81448d39c9a0 - test: revision 4
+  revision 0
+  space
+  revision 4
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ aftermerge
+  # cat f
+  <<<<<<< local: ef83787e2614  - test: revision 1
+  revision 1
+  space
+  =======
+  revision 4
+  >>>>>>> other: 81448d39c9a0 - test: revision 4
+  # hg stat
+  M f
+
+premerge=keep-merge3 keeps conflict markers with base content:
+
+  $ beforemerge
+  [merge-tools]
+  false.whatever=
+  true.priority=1
+  true.executable=cat
+  # hg update -C 1
+  $ hg merge -r 4 --config merge-tools.true.premerge=keep-merge3
+  merging f
+  <<<<<<< local: ef83787e2614  - test: revision 1
+  revision 1
+  space
+  ||||||| base
+  revision 0
+  space
+  =======
+  revision 4
+  >>>>>>> other: 81448d39c9a0 - test: revision 4
+  revision 0
+  space
+  revision 4
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ aftermerge
+  # cat f
+  <<<<<<< local: ef83787e2614  - test: revision 1
+  revision 1
+  space
+  ||||||| base
+  revision 0
+  space
+  =======
+  revision 4
+  >>>>>>> other: 81448d39c9a0 - test: revision 4
+  # hg stat
+  M f
+
+
 Tool execution
 
 set tools.args explicit to include $base $local $other $output:
@@ -832,17 +911,17 @@
   true.priority=1
   true.executable=cat
   # hg update -C 1
-  $ echo "revision 4" > '"; exit 1; echo "'
-  $ hg commit -Am "revision 4"
-  adding "; exit 1; echo "
-  warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
-  $ hg update -C 1 > /dev/null
   $ echo "revision 5" > '"; exit 1; echo "'
   $ hg commit -Am "revision 5"
   adding "; exit 1; echo "
   warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
+  $ hg update -C 1 > /dev/null
+  $ echo "revision 6" > '"; exit 1; echo "'
+  $ hg commit -Am "revision 6"
+  adding "; exit 1; echo "
+  warning: filename contains '"', which is reserved on Windows: '"; exit 1; echo "'
   created new head
-  $ hg merge --config merge-tools.true.executable="true" -r 4
+  $ hg merge --config merge-tools.true.executable="true" -r 5
   merging "; exit 1; echo "
   0 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
--- a/tests/test-merge-types.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-merge-types.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" symlink execbit || exit 80
+#require symlink execbit
 
   $ tellmeabout() {
   > if [ -h $1 ]; then
--- a/tests/test-mq-qclone-http.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-mq-qclone-http.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
 hide outer repo
   $ hg init
--- a/tests/test-mq-qimport.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-mq-qimport.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
   $ cat > writelines.py <<EOF
   > import sys
--- a/tests/test-mq-qrefresh-replace-log-message.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-mq-qrefresh-replace-log-message.t	Mon Aug 11 11:24:05 2014 -0500
@@ -32,17 +32,19 @@
 
   $ cat >> .hg/hgrc <<EOF
   > [committemplate]
+  > listupfiles = {file_adds %
+  >    "HG: added {file}\n"     }{file_mods %
+  >    "HG: changed {file}\n"   }{file_dels %
+  >    "HG: removed {file}\n"   }{if(files, "",
+  >    "HG: no files changed\n")}
+  > 
   > changeset = HG: this is customized commit template
   >     {desc}\n\n
   >     HG: Enter commit message.  Lines beginning with 'HG:' are removed.
   >     HG: {extramsg}
   >     HG: --
   >     HG: user: {author}
-  >     HG: branch '{branch}'\n{file_adds %
-  >    "HG: added {file}\n"     }{file_mods %
-  >    "HG: changed {file}\n"   }{file_dels %
-  >    "HG: removed {file}\n"   }{if(files, "",
-  >    "HG: no files changed\n")}
+  >     HG: branch '{branch}'\n{listupfiles}
   > EOF
 
   $ echo bbbb > file
--- a/tests/test-mq-subrepo-svn.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-mq-subrepo-svn.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" svn13 || exit 80
+#require svn13
 
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "mq=" >> $HGRCPATH
--- a/tests/test-mq-symlinks.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-mq-symlinks.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" symlink || exit 80
+#require symlink
 
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "mq=" >> $HGRCPATH
--- a/tests/test-newcgi.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-newcgi.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" no-msys || exit 80 # MSYS will translate web paths as if they were file paths
+#require no-msys # MSYS will translate web paths as if they were file paths
 
 This tests if CGI files from after d0db3462d568 but
 before d74fc8dec2b4 still work.
--- a/tests/test-newercgi.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-newercgi.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" no-msys || exit 80 # MSYS will translate web paths as if they were file paths
+#require no-msys # MSYS will translate web paths as if they were file paths
 
 This is a rudimentary test of the CGI files as of d74fc8dec2b4.
 
--- a/tests/test-no-symlinks.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-no-symlinks.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" no-symlink || exit 80
+#require no-symlink
 
 # The following script was used to create the bundle:
 #
--- a/tests/test-obsolete.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-obsolete.t	Mon Aug 11 11:24:05 2014 -0500
@@ -2,6 +2,8 @@
   > [phases]
   > # public changeset are not obsolete
   > publish=false
+  > [ui]
+  > logtemplate="{rev}:{node|short} ({phase}) [{tags} {bookmarks}] {desc|firstline}\n"
   > EOF
   $ mkcommit() {
   >    echo "$1" > "$1"
@@ -58,11 +60,7 @@
 
   $ hg up null --quiet # having 0 as parent prevents it to be hidden
   $ hg tip
-  changeset:   -1:000000000000
-  tag:         tip
-  user:        
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  
+  -1:000000000000 (public) [tip ] 
   $ hg up --hidden tip --quiet
   $ cd ..
 
@@ -125,59 +123,22 @@
 Check that graphlog detect that a changeset is obsolete:
 
   $ hg log -G
-  @  changeset:   5:5601fb93a350
-  |  tag:         tip
-  |  parent:      1:7c3bad9141dc
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add new_3_c
+  @  5:5601fb93a350 (draft) [tip ] add new_3_c
   |
-  o  changeset:   1:7c3bad9141dc
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add b
+  o  1:7c3bad9141dc (draft) [ ] add b
   |
-  o  changeset:   0:1f0dee641bb7
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     add a
+  o  0:1f0dee641bb7 (draft) [ ] add a
   
 
 check that heads does not report them
 
   $ hg heads
-  changeset:   5:5601fb93a350
-  tag:         tip
-  parent:      1:7c3bad9141dc
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add new_3_c
-  
+  5:5601fb93a350 (draft) [tip ] add new_3_c
   $ hg heads --hidden
-  changeset:   5:5601fb93a350
-  tag:         tip
-  parent:      1:7c3bad9141dc
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add new_3_c
-  
-  changeset:   4:ca819180edb9
-  parent:      1:7c3bad9141dc
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add new_2_c
-  
-  changeset:   3:cdbce2fbb163
-  parent:      1:7c3bad9141dc
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add new_c
-  
-  changeset:   2:245bde4270cd
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add original_c
-  
+  5:5601fb93a350 (draft) [tip ] add new_3_c
+  4:ca819180edb9 (draft) [ ] add new_2_c
+  3:cdbce2fbb163 (draft) [ ] add new_c
+  2:245bde4270cd (draft) [ ] add original_c
 
 
 check that summary does not report them
@@ -204,13 +165,7 @@
 check that various commands work well with filtering
 
   $ hg tip
-  changeset:   5:5601fb93a350
-  tag:         tip
-  parent:      1:7c3bad9141dc
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add new_3_c
-  
+  5:5601fb93a350 (draft) [tip ] add new_3_c
   $ hg log -r 6
   abort: unknown revision '6'!
   [255]
@@ -222,27 +177,13 @@
 
   $ hg --hidden phase --public 2
   $ hg log -G
-  @  changeset:   5:5601fb93a350
-  |  tag:         tip
-  |  parent:      1:7c3bad9141dc
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add new_3_c
+  @  5:5601fb93a350 (draft) [tip ] add new_3_c
   |
-  | o  changeset:   2:245bde4270cd
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     add original_c
+  | o  2:245bde4270cd (public) [ ] add original_c
+  |/
+  o  1:7c3bad9141dc (public) [ ] add b
   |
-  o  changeset:   1:7c3bad9141dc
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add b
-  |
-  o  changeset:   0:1f0dee641bb7
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     add a
+  o  0:1f0dee641bb7 (public) [ ] add a
   
 
 And that bumped changeset are detected
@@ -253,13 +194,7 @@
 the public changeset
 
   $ hg log --hidden -r 'bumped()'
-  changeset:   5:5601fb93a350
-  tag:         tip
-  parent:      1:7c3bad9141dc
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add new_3_c
-  
+  5:5601fb93a350 (draft) [tip ] add new_3_c
 
 And that we can't push bumped changeset
 
@@ -289,27 +224,13 @@
   $ hg debugobsolete -d '1338 0' --flags 1 `getid new_3_c` `getid n3w_3_c`
   $ hg log -r 'bumped()'
   $ hg log -G
-  @  changeset:   6:6f9641995072
-  |  tag:         tip
-  |  parent:      1:7c3bad9141dc
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add n3w_3_c
+  @  6:6f9641995072 (draft) [tip ] add n3w_3_c
   |
-  | o  changeset:   2:245bde4270cd
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     add original_c
+  | o  2:245bde4270cd (public) [ ] add original_c
+  |/
+  o  1:7c3bad9141dc (public) [ ] add b
   |
-  o  changeset:   1:7c3bad9141dc
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add b
-  |
-  o  changeset:   0:1f0dee641bb7
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     add a
+  o  0:1f0dee641bb7 (public) [ ] add a
   
 
 
@@ -328,28 +249,10 @@
   $ cd tmpc
   $ hg incoming ../tmpb
   comparing with ../tmpb
-  changeset:   0:1f0dee641bb7
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add a
-  
-  changeset:   1:7c3bad9141dc
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add b
-  
-  changeset:   2:245bde4270cd
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add original_c
-  
-  changeset:   6:6f9641995072
-  tag:         tip
-  parent:      1:7c3bad9141dc
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add n3w_3_c
-  
+  0:1f0dee641bb7 (public) [ ] add a
+  1:7c3bad9141dc (public) [ ] add b
+  2:245bde4270cd (public) [ ] add original_c
+  6:6f9641995072 (draft) [tip ] add n3w_3_c
 
 Try to pull markers
 (extinct changeset are excluded but marker are pushed)
@@ -414,6 +317,7 @@
   $ hg init empty
   $ hg --config extensions.debugkeys=debugkeys.py -R empty push tmpd
   pushing to tmpd
+  listkeys phases
   no changes found
   listkeys phases
   listkeys bookmarks
@@ -426,45 +330,19 @@
   updating to branch default
   3 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg -R clone-dest log -G --hidden
-  @  changeset:   6:6f9641995072
-  |  tag:         tip
-  |  parent:      1:7c3bad9141dc
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add n3w_3_c
-  |
-  | x  changeset:   5:5601fb93a350
-  |/   parent:      1:7c3bad9141dc
-  |    user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     add new_3_c
-  |
-  | x  changeset:   4:ca819180edb9
-  |/   parent:      1:7c3bad9141dc
-  |    user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     add new_2_c
+  @  6:6f9641995072 (draft) [tip ] add n3w_3_c
   |
-  | x  changeset:   3:cdbce2fbb163
-  |/   parent:      1:7c3bad9141dc
-  |    user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     add new_c
+  | x  5:5601fb93a350 (draft) [ ] add new_3_c
+  |/
+  | x  4:ca819180edb9 (draft) [ ] add new_2_c
+  |/
+  | x  3:cdbce2fbb163 (draft) [ ] add new_c
+  |/
+  | o  2:245bde4270cd (public) [ ] add original_c
+  |/
+  o  1:7c3bad9141dc (public) [ ] add b
   |
-  | o  changeset:   2:245bde4270cd
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     add original_c
-  |
-  o  changeset:   1:7c3bad9141dc
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add b
-  |
-  o  changeset:   0:1f0dee641bb7
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     add a
+  o  0:1f0dee641bb7 (public) [ ] add a
   
   $ hg -R clone-dest debugobsolete
   245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C {'date': '56 12', 'user': 'test'}
@@ -519,27 +397,13 @@
 
 
   $ hg log -G
-  o  changeset:   3:6f9641995072
-  |  tag:         tip
-  |  parent:      1:7c3bad9141dc
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add n3w_3_c
+  o  3:6f9641995072 (draft) [tip ] add n3w_3_c
   |
-  | o  changeset:   2:245bde4270cd
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     add original_c
+  | o  2:245bde4270cd (public) [ ] add original_c
+  |/
+  o  1:7c3bad9141dc (public) [ ] add b
   |
-  o  changeset:   1:7c3bad9141dc
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add b
-  |
-  o  changeset:   0:1f0dee641bb7
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     add a
+  o  0:1f0dee641bb7 (public) [ ] add a
   
   $ hg up 'desc("n3w_3_c")'
   3 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -547,38 +411,17 @@
   $ mkcommit original_e
   $ hg debugobsolete `getid original_d` -d '0 0'
   $ hg log -r 'obsolete()'
-  changeset:   4:94b33453f93b
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add original_d
-  
+  4:94b33453f93b (draft) [ ] add original_d
   $ hg log -G -r '::unstable()'
-  @  changeset:   5:cda648ca50f5
-  |  tag:         tip
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add original_e
+  @  5:cda648ca50f5 (draft) [tip ] add original_e
+  |
+  x  4:94b33453f93b (draft) [ ] add original_d
   |
-  x  changeset:   4:94b33453f93b
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add original_d
+  o  3:6f9641995072 (draft) [ ] add n3w_3_c
   |
-  o  changeset:   3:6f9641995072
-  |  parent:      1:7c3bad9141dc
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add n3w_3_c
+  o  1:7c3bad9141dc (public) [ ] add b
   |
-  o  changeset:   1:7c3bad9141dc
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add b
-  |
-  o  changeset:   0:1f0dee641bb7
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     add a
+  o  0:1f0dee641bb7 (public) [ ] add a
   
 
 refuse to push obsolete changeset
@@ -607,38 +450,12 @@
   $ hg out  ../tmpf
   comparing with ../tmpf
   searching for changes
-  changeset:   0:1f0dee641bb7
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add a
-  
-  changeset:   1:7c3bad9141dc
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add b
-  
-  changeset:   2:245bde4270cd
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add original_c
-  
-  changeset:   3:6f9641995072
-  parent:      1:7c3bad9141dc
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add n3w_3_c
-  
-  changeset:   4:94b33453f93b
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add original_d
-  
-  changeset:   5:cda648ca50f5
-  tag:         tip
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add original_e
-  
+  0:1f0dee641bb7 (public) [ ] add a
+  1:7c3bad9141dc (public) [ ] add b
+  2:245bde4270cd (public) [ ] add original_c
+  3:6f9641995072 (draft) [ ] add n3w_3_c
+  4:94b33453f93b (draft) [ ] add original_d
+  5:cda648ca50f5 (draft) [tip ] add original_e
   $ hg push ../tmpf -f # -f because be push unstable too
   pushing to ../tmpf
   searching for changes
@@ -658,37 +475,17 @@
 Do not warn about new head when the new head is a successors of a remote one
 
   $ hg log -G
-  @  changeset:   5:cda648ca50f5
-  |  tag:         tip
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add original_e
+  @  5:cda648ca50f5 (draft) [tip ] add original_e
   |
-  x  changeset:   4:94b33453f93b
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add original_d
+  x  4:94b33453f93b (draft) [ ] add original_d
+  |
+  o  3:6f9641995072 (draft) [ ] add n3w_3_c
   |
-  o  changeset:   3:6f9641995072
-  |  parent:      1:7c3bad9141dc
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add n3w_3_c
+  | o  2:245bde4270cd (public) [ ] add original_c
+  |/
+  o  1:7c3bad9141dc (public) [ ] add b
   |
-  | o  changeset:   2:245bde4270cd
-  |/   user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    summary:     add original_c
-  |
-  o  changeset:   1:7c3bad9141dc
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     add b
-  |
-  o  changeset:   0:1f0dee641bb7
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     add a
+  o  0:1f0dee641bb7 (public) [ ] add a
   
   $ hg up -q 'desc(n3w_3_c)'
   $ mkcommit obsolete_e
@@ -697,13 +494,7 @@
   $ hg outgoing ../tmpf # parasite hg outgoing testin
   comparing with ../tmpf
   searching for changes
-  changeset:   6:3de5eca88c00
-  tag:         tip
-  parent:      3:6f9641995072
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add obsolete_e
-  
+  6:3de5eca88c00 (draft) [tip ] add obsolete_e
   $ hg push ../tmpf
   pushing to ../tmpf
   searching for changes
@@ -773,13 +564,7 @@
   $ echo "obs=!" >> $HGRCPATH
   $ hg log -r tip
   obsolete feature not enabled but 68 markers found!
-  changeset:   68:c15e9edfca13
-  tag:         tip
-  parent:      7:50c51b361e60
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     add celestine
-  
+  68:c15e9edfca13 (draft) [tip ] add celestine
 
 reenable for later test
 
@@ -805,40 +590,19 @@
   $ hg ci --amend
   $ cd ../other-issue3805
   $ hg log -G
-  @  changeset:   0:193e9254ce7e
-     tag:         tip
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     A
+  @  0:193e9254ce7e (draft) [tip ] A
   
   $ hg log -G -R ../repo-issue3805
-  @  changeset:   2:3816541e5485
-     tag:         tip
-     parent:      -1:000000000000
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     A
+  @  2:3816541e5485 (draft) [tip ] A
   
   $ hg incoming
   comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
   searching for changes
-  changeset:   2:3816541e5485
-  tag:         tip
-  parent:      -1:000000000000
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     A
-  
+  2:3816541e5485 (draft) [tip ] A
   $ hg incoming --bundle ../issue3805.hg
   comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
   searching for changes
-  changeset:   2:3816541e5485
-  tag:         tip
-  parent:      -1:000000000000
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     A
-  
+  2:3816541e5485 (draft) [tip ] A
   $ hg outgoing
   comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
   searching for changes
@@ -853,13 +617,7 @@
   $ hg incoming http://localhost:$HGPORT
   comparing with http://localhost:$HGPORT/
   searching for changes
-  changeset:   1:3816541e5485
-  tag:         tip
-  parent:      -1:000000000000
-  user:        test
-  date:        Thu Jan 01 00:00:00 1970 +0000
-  summary:     A
-  
+  1:3816541e5485 (public) [tip ] A
   $ hg outgoing http://localhost:$HGPORT
   comparing with http://localhost:$HGPORT/
   searching for changes
@@ -894,18 +652,9 @@
 
   $ hg tag -l visible -r 0 --hidden
   $ hg log -G
-  @  changeset:   2:3816541e5485
-     tag:         tip
-     parent:      -1:000000000000
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     A
+  @  2:3816541e5485 (draft) [tip ] A
   
-  x  changeset:   0:193e9254ce7e
-     tag:         visible
-     user:        test
-     date:        Thu Jan 01 00:00:00 1970 +0000
-     summary:     A
+  x  0:193e9254ce7e (draft) [visible ] A
   
 Test that removing a local tag does not cause some commands to fail
 
--- a/tests/test-oldcgi.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-oldcgi.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" no-msys || exit 80 # MSYS will translate web paths as if they were file paths
+#require no-msys # MSYS will translate web paths as if they were file paths
 
 This tests if CGI files from before d0db3462d568 still work.
 
--- a/tests/test-patchbomb.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-patchbomb.t	Mon Aug 11 11:24:05 2014 -0500
@@ -8,6 +8,21 @@
 --===+[0-9]+=+--$ -> --===*=-- (glob)
 --===+[0-9]+=+$ -> --===*= (glob)
 
+  $ cat > prune-blank-after-boundary.py <<EOF
+  > import sys
+  > skipblank = False
+  > trim = lambda x: x.strip(' \r\n')
+  > for l in sys.stdin:
+  >     if trim(l).endswith('=--') or trim(l).endswith('=='):
+  >         skipblank = True
+  >         print l,
+  >         continue
+  >     if not trim(l) and skipblank:
+  >         continue
+  >     skipblank = False
+  >     print l,
+  > EOF
+  $ FILTERBOUNDARY="python `pwd`/prune-blank-after-boundary.py"
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "patchbomb=" >> $HGRCPATH
 
@@ -214,7 +229,7 @@
 
 test bundle and description:
   $ hg email --date '1970-1-1 0:3' -n -f quux -t foo \
-  >  -c bar -s test -r tip -b --desc description
+  >  -c bar -s test -r tip -b --desc description | $FILTERBOUNDARY
   searching for changes
   1 changesets found
   
@@ -689,7 +704,7 @@
   
 
 test inline for single patch:
-  $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -i -r 2
+  $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -i -r 2 | $FILTERBOUNDARY
   this patch series consists of 1 patches.
   
   
@@ -732,7 +747,7 @@
 
 
 test inline for single patch (quoted-printable):
-  $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -i -r 4
+  $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -i -r 4 | $FILTERBOUNDARY
   this patch series consists of 1 patches.
   
   
@@ -791,7 +806,7 @@
 
 test inline for multiple patches:
   $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -i \
-  >  -r 0:1 -r 4
+  >  -r 0:1 -r 4 | $FILTERBOUNDARY
   this patch series consists of 3 patches.
   
   
@@ -943,7 +958,7 @@
   --===*=-- (glob)
 
 test attach for single patch:
-  $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -a -r 2
+  $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -a -r 2 | $FILTERBOUNDARY
   this patch series consists of 1 patches.
   
   
@@ -994,7 +1009,7 @@
   --===*=-- (glob)
 
 test attach for single patch (quoted-printable):
-  $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -a -r 4
+  $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -a -r 4 | $FILTERBOUNDARY
   this patch series consists of 1 patches.
   
   
@@ -1061,7 +1076,7 @@
   --===*=-- (glob)
 
 test attach and body for single patch:
-  $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -a --body -r 2
+  $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -a --body -r 2 | $FILTERBOUNDARY
   this patch series consists of 1 patches.
   
   
@@ -1123,7 +1138,7 @@
 
 test attach for multiple patches:
   $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -a \
-  >  -r 0:1 -r 4
+  >  -r 0:1 -r 4 | $FILTERBOUNDARY
   this patch series consists of 3 patches.
   
   
@@ -1579,7 +1594,8 @@
   $ hg tag -r2 two two.diff
 
 test inline for single named patch:
-  $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -i -r 2
+  $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -i \
+  >   -r 2 | $FILTERBOUNDARY
   this patch series consists of 1 patches.
   
   
@@ -1621,7 +1637,8 @@
   --===*=-- (glob)
 
 test inline for multiple named/unnamed patches:
-  $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -i -r 0:1
+  $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -i \
+  >    -r 0:1 | $FILTERBOUNDARY
   this patch series consists of 2 patches.
   
   
@@ -1927,7 +1944,7 @@
   $ hg up -qr1
   $ echo dirt > a
   $ hg email --date '1970-1-1 0:1' -n --flag fooFlag -f quux -t foo -c bar -s test \
-  >  -r 2
+  >  -r 2 | $FILTERBOUNDARY
   this patch series consists of 1 patches.
   
   
--- a/tests/test-permissions.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-permissions.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-#if unix-permissions no-root
+#require unix-permissions no-root
 
   $ hg init t
   $ cd t
@@ -70,5 +70,3 @@
   $ chmod +rx dir
 
   $ cd ..
-
-#endif
--- a/tests/test-phases-exchange.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-phases-exchange.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
   $ hgph() { hg log -G --template "{rev} {phase} {desc} - {node|short}\n" $*; }
 
--- a/tests/test-pull-http.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-pull-http.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
   $ hg init test
   $ cd test
--- a/tests/test-pull-permission.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-pull-permission.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-#if unix-permissions no-root
+#require unix-permissions no-root
 
   $ hg init a
   $ cd a
@@ -30,5 +30,3 @@
   1 files, 1 changesets, 1 total revisions
 
   $ cd ..
-
-#endif
--- a/tests/test-pull.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-pull.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
   $ hg init test
   $ cd test
--- a/tests/test-push-cgi.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-push-cgi.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" no-msys || exit 80 # MSYS will translate web paths as if they were file paths
+#require no-msys # MSYS will translate web paths as if they were file paths
 
 This is a test of the push wire protocol over CGI-based hgweb.
 
--- a/tests/test-push-http.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-push-http.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
   $ hg init test
   $ cd test
--- a/tests/test-push-warn.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-push-warn.t	Mon Aug 11 11:24:05 2014 -0500
@@ -35,6 +35,7 @@
   searching: 2 queries
   query 2; still undecided: 1, sample size is: 1
   2 total queries
+  listing keys for "phases"
   listing keys for "bookmarks"
   remote has heads on branch 'default' that are not known locally: 1c9246a22a0a
   new remote heads on branch 'default':
--- a/tests/test-relink.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-relink.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" hardlink || exit 80
+#require hardlink
 
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "relink=" >> $HGRCPATH
--- a/tests/test-repair-strip.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-repair-strip.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-#if unix-permissions no-root
+#require unix-permissions no-root
 
   $ echo "[extensions]" >> $HGRCPATH
   $ echo "mq=">> $HGRCPATH
@@ -130,5 +130,3 @@
   2 files, 2 changesets, 2 total revisions
 
   $ cd ..
-
-#endif
--- a/tests/test-revert-flags.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-revert-flags.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" execbit || exit 80
+#require execbit
 
   $ hg init repo
   $ cd repo
--- a/tests/test-revert.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-revert.t	Mon Aug 11 11:24:05 2014 -0500
@@ -14,6 +14,9 @@
   [255]
   $ hg revert --all
 
+Introduce some changes and revert them
+--------------------------------------
+
   $ echo 123 > b
 
 should show b unknown
@@ -42,6 +45,9 @@
   M c
   A b
   R a
+
+revert removal of a file
+
   $ hg revert a
 
 should show b added, copy saved, and c modified
@@ -49,6 +55,9 @@
   $ hg status
   M c
   A b
+
+revert addition of a file
+
   $ hg revert b
 
 should show b unknown, and c modified
@@ -56,12 +65,19 @@
   $ hg status
   M c
   ? b
+
+revert modification of a file (--no-backup)
+
   $ hg revert --no-backup c
 
 should show unknown: b
 
   $ hg status
   ? b
+
+revert deletion (! status) of a added file
+------------------------------------------
+
   $ hg add b
 
 should show b added
@@ -89,26 +105,30 @@
   c
   e
 
-should verbosely save backup to e.orig
+Test creation of backup (.orig) files
+-------------------------------------
 
   $ echo z > e
   $ hg revert --all -v
   saving current version of e as e.orig
   reverting e
 
-should say no changes needed
+revert on clean file (no change)
+--------------------------------
 
   $ hg revert a
   no changes needed to a
 
-should say file not managed
+revert on an untracked file
+---------------------------
 
   $ echo q > q
   $ hg revert q
   file not managed: q
   $ rm q
 
-should say file not found
+revert on file that does not exists
+-----------------------------------
 
   $ hg revert notfound
   notfound: no such file in rev 334a9e57682c
@@ -122,21 +142,26 @@
   A z
   ? e.orig
 
-should add a, remove d, forget z
+revert to another revision (--rev)
+----------------------------------
 
   $ hg revert --all -r0
   adding a
   removing d
   forgetting z
 
-should forget a, undelete d
+revert explicitly to parent (--rev)
+-----------------------------------
 
   $ hg revert --all -rtip
   forgetting a
   undeleting d
   $ rm a *.orig
 
-should silently add a
+revert to another revision (--rev) and exact match
+--------------------------------------------------
+
+exact match are more silent
 
   $ hg revert -r0 a
   $ hg st a
@@ -153,6 +178,10 @@
 
   $ hg update -C
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+revert of exec bit
+------------------
+
 #if execbit
   $ chmod +x c
   $ hg revert --all
@@ -180,6 +209,7 @@
 
 
 Issue241: update and revert produces inconsistent repositories
+--------------------------------------------------------------
 
   $ hg init a
   $ cd a
@@ -193,20 +223,23 @@
   $ mkdir b
   $ echo b > b/b
 
-should fail - no arguments
+call `hg revert` with no file specified
+---------------------------------------
 
   $ hg revert -rtip
   abort: no files or directories specified
   (use --all to revert all files, or 'hg update 1' to update)
   [255]
 
-should succeed
+call `hg revert` with --all
+---------------------------
 
   $ hg revert --all -rtip
   reverting a
 
 
 Issue332: confusing message when reverting directory
+----------------------------------------------------
 
   $ hg ci -A -m b
   adding b/b
@@ -224,6 +257,7 @@
 
 
 reverting a rename target should revert the source
+--------------------------------------------------
 
   $ hg mv a newa
   $ hg revert newa
@@ -258,6 +292,7 @@
   $ hg rm removed ignoreddir/removed
 
 should revert ignored* and undelete *removed
+--------------------------------------------
 
   $ hg revert -a --no-backup
   reverting ignored
@@ -271,10 +306,14 @@
   $ hg rm removed
 
 should silently revert the named files
+--------------------------------------
 
   $ hg revert --no-backup ignored removed
   $ hg st -mardi
 
+Reverting copy (issue3920)
+--------------------------
+
 someone set up us the copies
 
   $ rm .hgignore
@@ -300,8 +339,9 @@
   R ignored
 
 Test revert of a file added by one side of the merge
+====================================================
 
-(remove any pending change)
+remove any pending change
 
   $ hg revert --all
   forgetting allyour
@@ -309,7 +349,7 @@
   undeleting ignored
   $ hg purge --all --config extensions.purge=
 
-(Adds a new commit)
+Adds a new commit
 
   $ echo foo > newadd
   $ hg add newadd
@@ -317,7 +357,7 @@
   created new head
 
 
-(merge it with the other head)
+merge it with the other head
 
   $ hg merge # merge 1 into 2
   2 files updated, 0 files merged, 1 files removed, 0 files unresolved
@@ -331,7 +371,7 @@
   commit: 2 modified, 1 removed (merge)
   update: (current)
 
-(clarifies who added what)
+clarifies who added what
 
   $ hg status
   M allyour
@@ -344,7 +384,8 @@
   A base
   R ignored
 
-(revert file added by p1() to p1() state)
+revert file added by p1() to p1() state
+-----------------------------------------
 
   $ hg revert -r 'p1()' 'glob:newad?'
   $ hg status
@@ -352,7 +393,8 @@
   M base
   R ignored
 
-(revert file added by p1() to p2() state)
+revert file added by p1() to p2() state
+------------------------------------------
 
   $ hg revert -r 'p2()' 'glob:newad?'
   removing newadd
@@ -362,7 +404,8 @@
   R ignored
   R newadd
 
-(revert file added by p2() to p2() state)
+revert file added by p2() to p2() state
+------------------------------------------
 
   $ hg revert -r 'p2()' 'glob:allyou?'
   $ hg status
@@ -371,7 +414,8 @@
   R ignored
   R newadd
 
-(revert file added by p2() to p1() state)
+revert file added by p2() to p1() state
+------------------------------------------
 
   $ hg revert -r 'p1()' 'glob:allyou?'
   removing allyour
--- a/tests/test-run-tests.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-run-tests.t	Mon Aug 11 11:24:05 2014 -0500
@@ -13,6 +13,8 @@
   $ cat > test-success.t << EOF
   >   $ echo babar
   >   babar
+  >   $ echo xyzzy
+  >   xyzzy
   > EOF
 
   $ $TESTDIR/run-tests.py --with-hg=`which hg`
@@ -25,16 +27,20 @@
   $ cat > test-failure.t << EOF
   >   $ echo babar
   >   rataxes
+  > This is a noop statement so that
+  > this test is still more bytes than success.
   > EOF
 
   $ $TESTDIR/run-tests.py --with-hg=`which hg`
   
   --- $TESTTMP/test-failure.t
   +++ $TESTTMP/test-failure.t.err
-  @@ -1,2 +1,2 @@
+  @@ -1,4 +1,4 @@
      $ echo babar
   -  rataxes
   +  babar
+   This is a noop statement so that
+   this test is still more bytes than success.
   
   ERROR: test-failure.t output changed
   !.
@@ -42,6 +48,39 @@
   # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
   python hash seed: * (glob)
   [1]
+test --xunit support
+  $ $TESTDIR/run-tests.py --with-hg=`which hg` --xunit=xunit.xml
+  
+  --- $TESTTMP/test-failure.t
+  +++ $TESTTMP/test-failure.t.err
+  @@ -1,4 +1,4 @@
+     $ echo babar
+  -  rataxes
+  +  babar
+   This is a noop statement so that
+   this test is still more bytes than success.
+  
+  ERROR: test-failure.t output changed
+  !.
+  Failed test-failure.t: output changed
+  # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
+  python hash seed: * (glob)
+  [1]
+  $ cat xunit.xml
+  <?xml version="1.0" encoding="utf-8"?>
+  <testsuite errors="0" failures="1" name="run-tests" skipped="0" tests="2">
+    <testcase name="test-success.t" time="*"/> (glob)
+    <testcase name="test-failure.t" time="*"> (glob)
+  <![CDATA[--- $TESTTMP/test-failure.t
+  +++ $TESTTMP/test-failure.t.err
+  @@ -1,4 +1,4 @@
+     $ echo babar
+  -  rataxes
+  +  babar
+   This is a noop statement so that
+   this test is still more bytes than success.
+  ]]>  </testcase>
+  </testsuite>
 
 test for --retest
 ====================
@@ -50,15 +89,17 @@
   
   --- $TESTTMP/test-failure.t
   +++ $TESTTMP/test-failure.t.err
-  @@ -1,2 +1,2 @@
+  @@ -1,4 +1,4 @@
      $ echo babar
   -  rataxes
   +  babar
+   This is a noop statement so that
+   this test is still more bytes than success.
   
   ERROR: test-failure.t output changed
   !
   Failed test-failure.t: output changed
-  # Ran 1 tests, 1 skipped, 0 warned, 1 failed.
+  # Ran 2 tests, 1 skipped, 0 warned, 1 failed.
   python hash seed: * (glob)
   [1]
 
@@ -71,16 +112,23 @@
   .
   # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
 
+success w/ keyword
+  $ $TESTDIR/run-tests.py --with-hg=`which hg` -k xyzzy
+  i.
+  # Ran 1 tests, 1 skipped, 0 warned, 0 failed.
+
 failed
 
   $ $TESTDIR/run-tests.py --with-hg=`which hg` test-failure.t
   
   --- $TESTTMP/test-failure.t
   +++ $TESTTMP/test-failure.t.err
-  @@ -1,2 +1,2 @@
+  @@ -1,4 +1,4 @@
      $ echo babar
   -  rataxes
   +  babar
+   This is a noop statement so that
+   this test is still more bytes than success.
   
   ERROR: test-failure.t output changed
   !
@@ -89,6 +137,25 @@
   python hash seed: * (glob)
   [1]
 
+failure w/ keyword
+  $ $TESTDIR/run-tests.py --with-hg=`which hg` -k rataxes
+  i
+  --- $TESTTMP/test-failure.t
+  +++ $TESTTMP/test-failure.t.err
+  @@ -1,4 +1,4 @@
+     $ echo babar
+  -  rataxes
+  +  babar
+   This is a noop statement so that
+   this test is still more bytes than success.
+  
+  ERROR: test-failure.t output changed
+  !
+  Failed test-failure.t: output changed
+  # Ran 1 tests, 1 skipped, 0 warned, 1 failed.
+  python hash seed: * (glob)
+  [1]
+
 Running In Debug Mode
 ======================
 
@@ -97,14 +164,18 @@
   SALT* 0 0 (glob)
   + echo babar
   babar
-  + echo SALT* 2 0 (glob)
-  SALT* 2 0 (glob)
+  + echo SALT* 4 0 (glob)
+  SALT* 4 0 (glob)
   .+ echo SALT* 0 0 (glob)
   SALT* 0 0 (glob)
   + echo babar
   babar
   + echo SALT* 2 0 (glob)
   SALT* 2 0 (glob)
+  + echo xyzzy
+  xyzzy
+  + echo SALT* 4 0 (glob)
+  SALT* 4 0 (glob)
   .
   # Ran 2 tests, 0 skipped, 0 warned, 0 failed.
 
@@ -118,19 +189,23 @@
   
   --- $TESTTMP/test-failure*.t (glob)
   +++ $TESTTMP/test-failure*.t.err (glob)
-  @@ -1,2 +1,2 @@
+  @@ -1,4 +1,4 @@
      $ echo babar
   -  rataxes
   +  babar
+   This is a noop statement so that
+   this test is still more bytes than success.
   
   ERROR: test-failure*.t output changed (glob)
   !
   --- $TESTTMP/test-failure*.t (glob)
   +++ $TESTTMP/test-failure*.t.err (glob)
-  @@ -1,2 +1,2 @@
+  @@ -1,4 +1,4 @@
      $ echo babar
   -  rataxes
   +  babar
+   This is a noop statement so that
+   this test is still more bytes than success.
   
   ERROR: test-failure*.t output changed (glob)
   !
@@ -156,10 +231,12 @@
   
   --- $TESTTMP/test-failure.t
   +++ $TESTTMP/test-failure.t.err
-  @@ -1,2 +1,2 @@
+  @@ -1,4 +1,4 @@
      $ echo babar
   -  rataxes
   +  babar
+   This is a noop statement so that
+   this test is still more bytes than success.
   Accept this change? [n] 
   ERROR: test-failure.t output changed
   !.
@@ -171,6 +248,8 @@
   $ cat test-failure.t
     $ echo babar
     rataxes
+  This is a noop statement so that
+  this test is still more bytes than success.
 
 Accept the fix
 
@@ -178,16 +257,20 @@
   
   --- $TESTTMP/test-failure.t
   +++ $TESTTMP/test-failure.t.err
-  @@ -1,2 +1,2 @@
+  @@ -1,4 +1,4 @@
      $ echo babar
   -  rataxes
   +  babar
+   This is a noop statement so that
+   this test is still more bytes than success.
   Accept this change? [n] ..
   # Ran 2 tests, 0 skipped, 0 warned, 0 failed.
 
   $ cat test-failure.t
     $ echo babar
     babar
+  This is a noop statement so that
+  this test is still more bytes than success.
 
 (reinstall)
   $ mv backup test-failure.t
@@ -201,3 +284,64 @@
   # Ran 2 tests, 0 skipped, 0 warned, 1 failed.
   python hash seed: * (glob)
   [1]
+
+test for --time
+==================
+
+  $ $TESTDIR/run-tests.py --with-hg=`which hg` test-success.t --time
+  .
+  # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
+  # Producing time report
+  cuser   csys    real      Test
+  \s*[\d\.]{5}   \s*[\d\.]{5}   \s*[\d\.]{5}   test-success.t (re)
+
+test for --time with --job enabled
+====================================
+
+  $ $TESTDIR/run-tests.py --with-hg=`which hg` test-success.t --time --jobs 2
+  .
+  # Ran 1 tests, 0 skipped, 0 warned, 0 failed.
+  # Producing time report
+  cuser   csys    real      Test
+  \s*[\d\.]{5}   \s*[\d\.]{5}   \s*[\d\.]{5}   test-success.t (re)
+
+Skips
+================
+  $ cat > test-skip.t <<EOF
+  >   $ echo xyzzy
+  > #require false
+  > EOF
+  $ $TESTDIR/run-tests.py --with-hg=`which hg` --nodiff
+  !.s
+  Skipped test-skip.t: irrelevant
+  Failed test-failure.t: output changed
+  # Ran 2 tests, 1 skipped, 0 warned, 1 failed.
+  python hash seed: * (glob)
+  [1]
+
+  $ $TESTDIR/run-tests.py --with-hg=`which hg` --keyword xyzzy
+  i.s
+  Skipped test-skip.t: irrelevant
+  # Ran 1 tests, 2 skipped, 0 warned, 0 failed.
+
+Skips with xml
+  $ $TESTDIR/run-tests.py --with-hg=`which hg` --keyword xyzzy \
+  >  --xunit=xunit.xml
+  i.s
+  Skipped test-skip.t: irrelevant
+  # Ran 1 tests, 2 skipped, 0 warned, 0 failed.
+  $ cat xunit.xml
+  <?xml version="1.0" encoding="utf-8"?>
+  <testsuite errors="0" failures="0" name="run-tests" skipped="2" tests="1">
+    <testcase name="test-success.t" time="*"/> (glob)
+  </testsuite>
+
+Missing skips or blacklisted skips don't count as executed:
+  $ echo test-failure.t > blacklist
+  $ $TESTDIR/run-tests.py --with-hg=`which hg` --blacklist=blacklist \
+  >   test-failure.t test-bogus.t
+  ss
+  Skipped test-bogus.t: Doesn't exist
+  Skipped test-failure.t: blacklisted
+  # Ran 0 tests, 2 skipped, 0 warned, 0 failed.
+
--- a/tests/test-schemes.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-schemes.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
   $ cat <<EOF >> $HGRCPATH
   > [extensions]
--- a/tests/test-serve.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-serve.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
   $ hgserve()
   > {
--- a/tests/test-share.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-share.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
   $ echo "[extensions]"      >> $HGRCPATH
   $ echo "share = "          >> $HGRCPATH
--- a/tests/test-simplemerge.py	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-simplemerge.py	Mon Aug 11 11:24:05 2014 -0500
@@ -320,66 +320,6 @@
         self.log(''.join(ml))
         self.assertEquals(ml, MERGED_RESULT)
 
-    def test_minimal_conflicts_common(self):
-        """Reprocessing"""
-        base_text = ("a\n" * 20).splitlines(True)
-        this_text = ("a\n"*10+"b\n" * 10).splitlines(True)
-        other_text = ("a\n"*10+"c\n"+"b\n" * 8 + "c\n").splitlines(True)
-        m3 = Merge3(base_text, other_text, this_text)
-        m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True)
-        merged_text = "".join(list(m_lines))
-        optimal_text = ("a\n" * 10 + "<<<<<<< OTHER\nc\n=======\n"
-            + ">>>>>>> THIS\n"
-            + 8* "b\n" + "<<<<<<< OTHER\nc\n=======\n"
-            + 2* "b\n" + ">>>>>>> THIS\n")
-        self.assertEquals(optimal_text, merged_text)
-
-    def test_minimal_conflicts_unique(self):
-        def add_newline(s):
-            """Add a newline to each entry in the string"""
-            return [(x+'\n') for x in s]
-
-        base_text = add_newline("abcdefghijklm")
-        this_text = add_newline("abcdefghijklmNOPQRSTUVWXYZ")
-        other_text = add_newline("abcdefghijklm1OPQRSTUVWXY2")
-        m3 = Merge3(base_text, other_text, this_text)
-        m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True)
-        merged_text = "".join(list(m_lines))
-        optimal_text = ''.join(add_newline("abcdefghijklm")
-            + ["<<<<<<< OTHER\n1\n=======\nN\n>>>>>>> THIS\n"]
-            + add_newline('OPQRSTUVWXY')
-            + ["<<<<<<< OTHER\n2\n=======\nZ\n>>>>>>> THIS\n"]
-            )
-        self.assertEquals(optimal_text, merged_text)
-
-    def test_minimal_conflicts_nonunique(self):
-        def add_newline(s):
-            """Add a newline to each entry in the string"""
-            return [(x+'\n') for x in s]
-
-        base_text = add_newline("abacddefgghij")
-        this_text = add_newline("abacddefgghijkalmontfprz")
-        other_text = add_newline("abacddefgghijknlmontfprd")
-        m3 = Merge3(base_text, other_text, this_text)
-        m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True)
-        merged_text = "".join(list(m_lines))
-        optimal_text = ''.join(add_newline("abacddefgghijk")
-            + ["<<<<<<< OTHER\nn\n=======\na\n>>>>>>> THIS\n"]
-            + add_newline('lmontfpr')
-            + ["<<<<<<< OTHER\nd\n=======\nz\n>>>>>>> THIS\n"]
-            )
-        self.assertEquals(optimal_text, merged_text)
-
-    def test_reprocess_and_base(self):
-        """Reprocessing and showing base breaks correctly"""
-        base_text = ("a\n" * 20).splitlines(True)
-        this_text = ("a\n"*10+"b\n" * 10).splitlines(True)
-        other_text = ("a\n"*10+"c\n"+"b\n" * 8 + "c\n").splitlines(True)
-        m3 = Merge3(base_text, other_text, this_text)
-        m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True,
-                                 base_marker='|||||||')
-        self.assertRaises(CantReprocessAndShowBase, list, m_lines)
-
     def test_binary(self):
         self.assertRaises(util.Abort, Merge3, ['\x00'], ['a'], ['b'])
 
--- a/tests/test-simplemerge.py.out	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-simplemerge.py.out	Mon Aug 11 11:24:05 2014 -0500
@@ -1,5 +1,5 @@
-....................
+................
 ----------------------------------------------------------------------
-Ran 20 tests in 0.000s
+Ran 16 tests in 0.000s
 
 OK
--- a/tests/test-static-http.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-static-http.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
 #if windows
   $ hg clone http://localhost:$HGPORT/ copy
--- a/tests/test-subrepo-git.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-subrepo-git.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" git || exit 80
+#require git
 
 make git commits repeatable
 
--- a/tests/test-subrepo-relative-path.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-subrepo-relative-path.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
 Preparing the subrepository 'sub'
 
--- a/tests/test-subrepo-svn.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-subrepo-svn.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" svn15 || exit 80
+#require svn15
 
   $ SVNREPOPATH=`pwd`/svn-repo
 #if windows
--- a/tests/test-symlink-placeholder.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-symlink-placeholder.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" symlink || exit 80
+#require symlink
 
 Create extension that can disable symlink support:
 
--- a/tests/test-symlinks.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-symlinks.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" symlink || exit 80
+#require symlink
 
 == tests added in 0.7 ==
 
--- a/tests/test-transplant.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-transplant.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
   $ cat <<EOF >> $HGRCPATH
   > [extensions]
--- a/tests/test-treediscovery-legacy.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-treediscovery-legacy.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
 Tests discovery against servers without getbundle support:
 
--- a/tests/test-treediscovery.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-treediscovery.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
 Tests discovery against servers without getbundle support:
 
--- a/tests/test-unbundlehash.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-unbundlehash.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
 Test wire protocol unbundle with hashed heads (capability: unbundlehash)
 
--- a/tests/test-update-issue1456.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-update-issue1456.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" execbit || exit 80
+#require execbit
 
   $ rm -rf a
   $ hg init a
--- a/tests/test-websub.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-websub.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" serve || exit 80
+#require serve
 
   $ hg init test
   $ cd test
--- a/tests/test-wireproto.t	Sun Aug 10 23:09:23 2014 -0500
+++ b/tests/test-wireproto.t	Mon Aug 11 11:24:05 2014 -0500
@@ -1,4 +1,4 @@
-  $ "$TESTDIR/hghave" killdaemons || exit 80
+#require killdaemons
 
 Test wire protocol argument passing