merge with stable
authorAugie Fackler <augie@google.com>
Thu, 09 May 2019 18:37:37 -0400
changeset 42267 838f3a094b4f
parent 42266 b3fc78c028ef (diff)
parent 42263 ce5f1232631f (current diff)
child 42268 af13e2088f77
merge with stable
tests/test-revset.t
--- a/contrib/all-revsets.txt	Wed May 08 16:09:50 2019 -0400
+++ b/contrib/all-revsets.txt	Thu May 09 18:37:37 2019 -0400
@@ -154,3 +154,6 @@
 roots(matching(tip, "author"))
 roots(matching(tip, "author")) and -10000:-1
 (-10000:-1) and roots(matching(tip, "author"))
+only(max(head()))
+only(max(head()), min(head()))
+only(max(head()), limit(head(), 1, 1))
--- a/contrib/byteify-strings.py	Wed May 08 16:09:50 2019 -0400
+++ b/contrib/byteify-strings.py	Thu May 09 18:37:37 2019 -0400
@@ -7,7 +7,7 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-from __future__ import absolute_import
+from __future__ import absolute_import, print_function
 
 import argparse
 import contextlib
@@ -227,4 +227,7 @@
                 process(fin, fout, opts)
 
 if __name__ == '__main__':
+    if sys.version_info.major < 3:
+        print('This script must be run under Python 3.')
+        sys.exit(3)
     main()
--- a/doc/gendoc.py	Wed May 08 16:09:50 2019 -0400
+++ b/doc/gendoc.py	Thu May 09 18:37:37 2019 -0400
@@ -120,7 +120,7 @@
 
     # print cmds
     ui.write(minirst.section(_(b"Commands")))
-    commandprinter(ui, table, minirst.subsection)
+    commandprinter(ui, table, minirst.subsection, minirst.subsubsection)
 
     # print help topics
     # The config help topic is included in the hgrc.5 man page.
@@ -143,7 +143,8 @@
         cmdtable = getattr(mod, 'cmdtable', None)
         if cmdtable:
             ui.write(minirst.subsubsection(_(b'Commands')))
-            commandprinter(ui, cmdtable, minirst.subsubsubsection)
+            commandprinter(ui, cmdtable, minirst.subsubsubsection,
+                    minirst.subsubsubsubsection)
 
 def showtopic(ui, topic):
     extrahelptable = [
@@ -177,7 +178,27 @@
         ui.write(doc)
         ui.write(b"\n")
 
-def commandprinter(ui, cmdtable, sectionfunc):
+def commandprinter(ui, cmdtable, sectionfunc, subsectionfunc):
+    """Render restructuredtext describing a list of commands and their
+    documentations, grouped by command category.
+
+    Args:
+      ui: UI object to write the output to
+      cmdtable: a dict that maps a string of the command name plus its aliases
+        (separated with pipes) to a 3-tuple of (the command's function, a list
+        of its option descriptions, and a string summarizing available
+        options). Example, with aliases added for demonstration purposes:
+
+          'phase|alias1|alias2': (
+             <function phase at 0x7f0816b05e60>,
+             [ ('p', 'public', False, 'set changeset phase to public'),
+               ...,
+               ('r', 'rev', [], 'target revision', 'REV')],
+             '[-p|-d|-s] [-f] [-r] [REV...]'
+          )
+      sectionfunc: minirst function to format command category headers
+      subsectionfunc: minirst function to format command headers
+    """
     h = {}
     for c, attr in cmdtable.items():
         f = c.split(b"|")[0]
@@ -185,45 +206,76 @@
         h[f] = c
     cmds = h.keys()
 
-    for f in sorted(cmds):
-        if f.startswith(b"debug"):
+    def helpcategory(cmd):
+        """Given a canonical command name from `cmds` (above), retrieve its
+        help category. If helpcategory is None, default to CATEGORY_NONE.
+        """
+        fullname = h[cmd]
+        details = cmdtable[fullname]
+        helpcategory = details[0].helpcategory
+        return helpcategory or help.registrar.command.CATEGORY_NONE
+
+    cmdsbycategory = {category: [] for category in help.CATEGORY_ORDER}
+    for cmd in cmds:
+        # If a command category wasn't registered, the command won't get
+        # rendered below, so we raise an AssertionError.
+        if helpcategory(cmd) not in cmdsbycategory:
+            raise AssertionError(
+                "The following command did not register its (category) in "
+                "help.CATEGORY_ORDER: %s (%s)" % (cmd, helpcategory(cmd)))
+        cmdsbycategory[helpcategory(cmd)].append(cmd)
+
+    # Print the help for each command. We present the commands grouped by
+    # category, and we use help.CATEGORY_ORDER as a guide for a helpful order
+    # in which to present the categories.
+    for category in help.CATEGORY_ORDER:
+        categorycmds = cmdsbycategory[category]
+        if not categorycmds:
+            # Skip empty categories
             continue
-        d = get_cmd(h[f], cmdtable)
-        ui.write(sectionfunc(d[b'cmd']))
-        # short description
-        ui.write(d[b'desc'][0])
-        # synopsis
-        ui.write(b"::\n\n")
-        synopsislines = d[b'synopsis'].splitlines()
-        for line in synopsislines:
-            # some commands (such as rebase) have a multi-line
+        # Print a section header for the category.
+        # For now, the category header is at the same level as the headers for
+        # the commands in the category; this is fixed in the next commit.
+        ui.write(sectionfunc(help.CATEGORY_NAMES[category]))
+        # Print each command in the category
+        for f in sorted(categorycmds):
+            if f.startswith(b"debug"):
+                continue
+            d = get_cmd(h[f], cmdtable)
+            ui.write(subsectionfunc(d[b'cmd']))
+            # short description
+            ui.write(d[b'desc'][0])
             # synopsis
-            ui.write(b"   %s\n" % line)
-        ui.write(b'\n')
-        # description
-        ui.write(b"%s\n\n" % d[b'desc'][1])
-        # options
-        opt_output = list(d[b'opts'])
-        if opt_output:
-            opts_len = max([len(line[0]) for line in opt_output])
-            ui.write(_(b"Options:\n\n"))
-            multioccur = False
-            for optstr, desc in opt_output:
-                if desc:
-                    s = b"%-*s  %s" % (opts_len, optstr, desc)
-                else:
-                    s = optstr
-                ui.write(b"%s\n" % s)
-                if optstr.endswith(b"[+]>"):
-                    multioccur = True
-            if multioccur:
-                ui.write(_(b"\n[+] marked option can be specified"
-                           b" multiple times\n"))
-            ui.write(b"\n")
-        # aliases
-        if d[b'aliases']:
-            ui.write(_(b"    aliases: %s\n\n") % b" ".join(d[b'aliases']))
-
+            ui.write(b"::\n\n")
+            synopsislines = d[b'synopsis'].splitlines()
+            for line in synopsislines:
+                # some commands (such as rebase) have a multi-line
+                # synopsis
+                ui.write(b"   %s\n" % line)
+            ui.write(b'\n')
+            # description
+            ui.write(b"%s\n\n" % d[b'desc'][1])
+            # options
+            opt_output = list(d[b'opts'])
+            if opt_output:
+                opts_len = max([len(line[0]) for line in opt_output])
+                ui.write(_(b"Options:\n\n"))
+                multioccur = False
+                for optstr, desc in opt_output:
+                    if desc:
+                        s = b"%-*s  %s" % (opts_len, optstr, desc)
+                    else:
+                        s = optstr
+                    ui.write(b"%s\n" % s)
+                    if optstr.endswith(b"[+]>"):
+                        multioccur = True
+                if multioccur:
+                    ui.write(_(b"\n[+] marked option can be specified"
+                               b" multiple times\n"))
+                ui.write(b"\n")
+            # aliases
+            if d[b'aliases']:
+                ui.write(_(b"    aliases: %s\n\n") % b" ".join(d[b'aliases']))
 
 def allextensionnames():
     return set(extensions.enabled().keys()) | set(extensions.disabled().keys())
--- a/hgext/absorb.py	Wed May 08 16:09:50 2019 -0400
+++ b/hgext/absorb.py	Thu May 09 18:37:37 2019 -0400
@@ -914,7 +914,10 @@
     """
     if stack is None:
         limit = ui.configint('absorb', 'max-stack-size')
-        stack = getdraftstack(repo['.'], limit)
+        headctx = repo['.']
+        if len(headctx.parents()) > 1:
+            raise error.Abort(_('cannot absorb into a merge'))
+        stack = getdraftstack(headctx, limit)
         if limit and len(stack) >= limit:
             ui.warn(_('absorb: only the recent %d changesets will '
                       'be analysed\n')
--- a/hgext/convert/monotone.py	Wed May 08 16:09:50 2019 -0400
+++ b/hgext/convert/monotone.py	Thu May 09 18:37:37 2019 -0400
@@ -284,9 +284,9 @@
                     # d2 => d3
                     ignoremove[tofile] = 1
             for tofile, fromfile in renamed.items():
-                self.ui.debug (_("copying file in renamed directory "
-                                 "from '%s' to '%s'")
-                               % (fromfile, tofile), '\n')
+                self.ui.debug(
+                    "copying file in renamed directory from '%s' to '%s'"
+                    % (fromfile, tofile), '\n')
                 files[tofile] = rev
                 copies[tofile] = fromfile
             for fromfile in renamed.values():
@@ -370,4 +370,3 @@
             self.mtnwritefp = None
             self.mtnreadfp.close()
             self.mtnreadfp = None
-
--- a/hgext/fix.py	Wed May 08 16:09:50 2019 -0400
+++ b/hgext/fix.py	Thu May 09 18:37:37 2019 -0400
@@ -72,12 +72,43 @@
 To account for changes made by each tool, the line numbers used for incremental
 formatting are recomputed before executing the next tool. So, each tool may see
 different values for the arguments added by the :linerange suboption.
+
+Each fixer tool is allowed to return some metadata in addition to the fixed file
+content. The metadata must be placed before the file content on stdout,
+separated from the file content by a zero byte. The metadata is parsed as a JSON
+value (so, it should be UTF-8 encoded and contain no zero bytes). A fixer tool
+is expected to produce this metadata encoding if and only if the :metadata
+suboption is true::
+
+  [fix]
+  tool:command = tool --prepend-json-metadata
+  tool:metadata = true
+
+The metadata values are passed to hooks, which can be used to print summaries or
+perform other post-fixing work. The supported hooks are::
+
+  "postfixfile"
+    Run once for each file in each revision where any fixer tools made changes
+    to the file content. Provides "$HG_REV" and "$HG_PATH" to identify the file,
+    and "$HG_METADATA" with a map of fixer names to metadata values from fixer
+    tools that affected the file. Fixer tools that didn't affect the file have a
+    valueof None. Only fixer tools that executed are present in the metadata.
+
+  "postfix"
+    Run once after all files and revisions have been handled. Provides
+    "$HG_REPLACEMENTS" with information about what revisions were created and
+    made obsolete. Provides a boolean "$HG_WDIRWRITTEN" to indicate whether any
+    files in the working copy were updated. Provides a list "$HG_METADATA"
+    mapping fixer tool names to lists of metadata values returned from
+    executions that modified a file. This aggregates the same metadata
+    previously passed to the "postfixfile" hook.
 """
 
 from __future__ import absolute_import
 
 import collections
 import itertools
+import json
 import os
 import re
 import subprocess
@@ -117,13 +148,14 @@
 configtable = {}
 configitem = registrar.configitem(configtable)
 
-# Register the suboptions allowed for each configured fixer.
+# Register the suboptions allowed for each configured fixer, and default values.
 FIXER_ATTRS = {
     'command': None,
     'linerange': None,
     'fileset': None,
     'pattern': None,
     'priority': 0,
+    'metadata': False,
 }
 
 for key, default in FIXER_ATTRS.items():
@@ -201,10 +233,12 @@
             for rev, path in items:
                 ctx = repo[rev]
                 olddata = ctx[path].data()
-                newdata = fixfile(ui, opts, fixers, ctx, path, basectxs[rev])
+                metadata, newdata = fixfile(ui, opts, fixers, ctx, path,
+                                            basectxs[rev])
                 # Don't waste memory/time passing unchanged content back, but
                 # produce one result per item either way.
-                yield (rev, path, newdata if newdata != olddata else None)
+                yield (rev, path, metadata,
+                       newdata if newdata != olddata else None)
         results = worker.worker(ui, 1.0, getfixes, tuple(), workqueue,
                                 threadsafe=False)
 
@@ -215,15 +249,25 @@
         # the tests deterministic. It might also be considered a feature since
         # it makes the results more easily reproducible.
         filedata = collections.defaultdict(dict)
+        aggregatemetadata = collections.defaultdict(list)
         replacements = {}
         wdirwritten = False
         commitorder = sorted(revstofix, reverse=True)
         with ui.makeprogress(topic=_('fixing'), unit=_('files'),
                              total=sum(numitems.values())) as progress:
-            for rev, path, newdata in results:
+            for rev, path, filerevmetadata, newdata in results:
                 progress.increment(item=path)
+                for fixername, fixermetadata in filerevmetadata.items():
+                    aggregatemetadata[fixername].append(fixermetadata)
                 if newdata is not None:
                     filedata[rev][path] = newdata
+                    hookargs = {
+                      'rev': rev,
+                      'path': path,
+                      'metadata': filerevmetadata,
+                    }
+                    repo.hook('postfixfile', throw=False,
+                              **pycompat.strkwargs(hookargs))
                 numitems[rev] -= 1
                 # Apply the fixes for this and any other revisions that are
                 # ready and sitting at the front of the queue. Using a loop here
@@ -240,6 +284,12 @@
                     del filedata[rev]
 
         cleanup(repo, replacements, wdirwritten)
+        hookargs = {
+            'replacements': replacements,
+            'wdirwritten': wdirwritten,
+            'metadata': aggregatemetadata,
+        }
+        repo.hook('postfix', throw=True, **pycompat.strkwargs(hookargs))
 
 def cleanup(repo, replacements, wdirwritten):
     """Calls scmutil.cleanupnodes() with the given replacements.
@@ -491,6 +541,7 @@
     A fixer tool's stdout will become the file's new content if and only if it
     exits with code zero.
     """
+    metadata = {}
     newdata = fixctx[path].data()
     for fixername, fixer in fixers.iteritems():
         if fixer.affects(opts, fixctx, path):
@@ -506,9 +557,20 @@
                 stdin=subprocess.PIPE,
                 stdout=subprocess.PIPE,
                 stderr=subprocess.PIPE)
-            newerdata, stderr = proc.communicate(newdata)
+            stdout, stderr = proc.communicate(newdata)
             if stderr:
                 showstderr(ui, fixctx.rev(), fixername, stderr)
+            newerdata = stdout
+            if fixer.shouldoutputmetadata():
+                try:
+                    metadatajson, newerdata = stdout.split('\0', 1)
+                    metadata[fixername] = json.loads(metadatajson)
+                except ValueError:
+                    ui.warn(_('ignored invalid output from fixer tool: %s\n') %
+                            (fixername,))
+                    continue
+            else:
+                metadata[fixername] = None
             if proc.returncode == 0:
                 newdata = newerdata
             else:
@@ -519,7 +581,7 @@
                     ui, _('no fixes will be applied'),
                     hint=_('use --config fix.failure=continue to apply any '
                            'successful fixes anyway'))
-    return newdata
+    return metadata, newdata
 
 def showstderr(ui, rev, fixername, stderr):
     """Writes the lines of the stderr string as warnings on the ui
@@ -667,6 +729,10 @@
         """Should this fixer run on the file at the given path and context?"""
         return scmutil.match(fixctx, [self._pattern], opts)(path)
 
+    def shouldoutputmetadata(self):
+        """Should the stdout of this fixer start with JSON and a null byte?"""
+        return self._metadata
+
     def command(self, ui, path, rangesfn):
         """A shell command to use to invoke this fixer on the given file/lines
 
--- a/hgext/gpg.py	Wed May 08 16:09:50 2019 -0400
+++ b/hgext/gpg.py	Thu May 09 18:37:37 2019 -0400
@@ -49,6 +49,11 @@
 
 # Custom help category
 _HELP_CATEGORY = 'gpg'
+help.CATEGORY_ORDER.insert(
+    help.CATEGORY_ORDER.index(registrar.command.CATEGORY_HELP),
+    _HELP_CATEGORY
+)
+help.CATEGORY_NAMES[_HELP_CATEGORY] = 'Signing changes (GPG)'
 
 class gpg(object):
     def __init__(self, path, key=None):
--- a/hgext/histedit.py	Wed May 08 16:09:50 2019 -0400
+++ b/hgext/histedit.py	Thu May 09 18:37:37 2019 -0400
@@ -1079,6 +1079,8 @@
 def changemode(state, mode):
     curmode, _ = state['mode']
     state['mode'] = (mode, curmode)
+    if mode == MODE_PATCH:
+        state['modes'][MODE_PATCH]['patchcontents'] = patchcontents(state)
 
 def makeselection(state, pos):
     state['selected'] = pos
@@ -1134,7 +1136,7 @@
     if mode != MODE_PATCH:
         return
     mode_state = state['modes'][mode]
-    num_lines = len(patchcontents(state))
+    num_lines = len(mode_state['patchcontents'])
     page_height = state['page_height']
     unit = page_height if unit == 'page' else 1
     num_pages = 1 + (num_lines - 1) / page_height
@@ -1227,15 +1229,25 @@
     else:
         win.addstr(y, x, line)
 
+def _trunc_head(line, n):
+    if len(line) <= n:
+        return line
+    return '> ' + line[-(n - 2):]
+def _trunc_tail(line, n):
+    if len(line) <= n:
+        return line
+    return line[:n - 2] + ' >'
+
 def patchcontents(state):
     repo = state['repo']
     rule = state['rules'][state['pos']]
-    repo.ui.verbose = True
     displayer = logcmdutil.changesetdisplayer(repo.ui, repo, {
         "patch": True,  "template": "status"
     }, buffered=True)
-    displayer.show(rule.ctx)
-    displayer.close()
+    overrides = {('ui',  'verbose'): True}
+    with repo.ui.configoverride(overrides, source='histedit'):
+        displayer.show(rule.ctx)
+        displayer.close()
     return displayer.hunk[rule.ctx.rev()].splitlines()
 
 def _chisteditmain(repo, rules, stdscr):
@@ -1283,11 +1295,23 @@
         line = "bookmark:  {0}".format(' '.join(bms))
         win.addstr(3, 1, line[:length])
 
-        line = "files:     {0}".format(','.join(ctx.files()))
+        line = "summary:   {0}".format(ctx.description().splitlines()[0])
         win.addstr(4, 1, line[:length])
 
-        line = "summary:   {0}".format(ctx.description().splitlines()[0])
-        win.addstr(5, 1, line[:length])
+        line = "files:     "
+        win.addstr(5, 1, line)
+        fnx = 1 + len(line)
+        fnmaxx = length - fnx + 1
+        y = 5
+        fnmaxn = maxy - (1 + y) - 1
+        files = ctx.files()
+        for i, line1 in enumerate(files):
+            if len(files) > fnmaxn and i == fnmaxn - 1:
+                win.addstr(y, fnx, _trunc_tail(','.join(files[i:]), fnmaxx))
+                y = y + 1
+                break
+            win.addstr(y, fnx, _trunc_head(line1, fnmaxx))
+            y = y + 1
 
         conflicts = rule.conflicts
         if len(conflicts) > 0:
@@ -1296,7 +1320,7 @@
         else:
             conflictstr = 'no overlap'
 
-        win.addstr(6, 1, conflictstr[:length])
+        win.addstr(y, 1, conflictstr[:length])
         win.noutrefresh()
 
     def helplines(mode):
@@ -1372,15 +1396,16 @@
 
     def renderpatch(win, state):
         start = state['modes'][MODE_PATCH]['line_offset']
-        renderstring(win, state, patchcontents(state)[start:], diffcolors=True)
+        content = state['modes'][MODE_PATCH]['patchcontents']
+        renderstring(win, state, content[start:], diffcolors=True)
 
     def layout(mode):
         maxy, maxx = stdscr.getmaxyx()
         helplen = len(helplines(mode))
         return {
-            'commit': (8, maxx),
+            'commit': (12, maxx),
             'help': (helplen, maxx),
-            'main': (maxy - helplen - 8, maxx),
+            'main': (maxy - helplen - 12, maxx),
         }
 
     def drawvertwin(size, y, x):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext/lfs/TODO.rst	Thu May 09 18:37:37 2019 -0400
@@ -0,0 +1,195 @@
+Prior to removing (EXPERIMENTAL)
+--------------------------------
+
+These things affect UI and/or behavior, and should probably be implemented (or
+ruled out) prior to taking off the experimental shrinkwrap.
+
+#. Finish the `hg convert` story
+
+   * Add an argument to accept a rules file to apply during conversion?
+     Currently `lfs.track` is the only way to affect the conversion.
+   * drop `lfs.track` config settings
+   * splice in `.hglfs` file for normal repo -> lfs conversions?
+
+#. Stop uploading blobs when pushing between local repos
+
+   * Could probably hardlink directly to the other local repo's store
+   * Support inferring `lfs.url` for local push/pull (currently only supports
+     http)
+
+#. Stop uploading blobs on strip/amend/histedit/etc.
+
+   * This seems to be a side effect of doing it for `hg bundle`, which probably
+     makes sense.
+
+#. Handle a server with the extension loaded and a client without the extension
+   more gracefully.
+
+   * `changegroup3` is still experimental, and not enabled by default.
+   * Figure out how to `introduce LFS to the server repo
+     <https://www.mercurial-scm.org/pipermail/mercurial-devel/2018-September/122281.html>`_.
+     See the TODO in test-lfs-serve.t.
+
+#. Remove `lfs.retry` hack in client?  This came from FB, but it's not clear why
+   it is/was needed.
+
+#. `hg export` currently writes out the LFS blob.  Should it write the pointer
+   instead?
+
+   * `hg diff` is similar, and probably shouldn't see the pointer file
+
+#. `Fix https multiplexing, and re-enable workers
+   <https://www.mercurial-scm.org/pipermail/mercurial-devel/2018-January/109916.html>`_.
+
+#. Show to-be-applied rules with `hg files -r 'wdir()' 'set:lfs()'`
+
+   * `debugignore` can show file + line number, so a dedicated command could be
+     useful too.
+
+#. Filesets, revsets and templates
+
+   * A dedicated revset should be faster than `'file(set:lfs())'`
+   * Attach `{lfsoid}` and `{lfspointer}` to `general keywords
+     <https://www.mercurial-scm.org/pipermail/mercurial-devel/2018-January/110251.html>`_,
+     IFF the file is a blob
+   * Drop existing items that would be redundant with general support
+
+#. Can `grep` avoid downloading most things?
+
+   * Add a command option to skip LFS blobs?
+
+#. Add a flag that's visible in `hg files -v` to indicate external storage?
+
+#. Server side issues
+
+   * Check for local disk space before allowing upload.  (I've got a patch for
+     this.)
+   * Make sure the http codes used are appropriate.
+   * `Why is copying the Authorization header into the JSON payload necessary
+     <https://www.mercurial-scm.org/pipermail/mercurial-devel/2018-April/116230.html>`_?
+   * `LFS-Authenticate` header support in client and server(?)
+
+#. Add locks on cache and blob store
+
+   * This is complicated with a global store, and multiple potentially unrelated
+     local repositories that reference the same blob.
+   * Alternately, maybe just handle collisions when trying to create the same
+     blob in the store somehow.
+
+#. Are proper file sizes reported in `debugupgraderepo`?
+
+#. Finish prefetching files
+
+   * `-T {rawdata}`
+   * `verify`
+   * `grep`
+
+#. Output cleanup
+
+   * Can we print the url when connecting to the blobstore?  (A sudden
+     connection refused after pulling commits looks confusing.)  Problem is,
+     'pushing to main url' is printed, and then lfs wants to upload before going
+     back to the main repo transfer, so then *that* could be confusing with
+     extra output. (This is kinda improved with 380f5131ee7b and 9f78d10742af.)
+
+   * Add more progress indicators?  Uploading a large repo looks idle for a long
+     time while it scans for blobs in each outgoing revision.
+
+   * Print filenames instead of hashes in error messages
+
+     * subrepo aware paths, where necessary
+
+   * Is existing output at the right status/note/debug level?
+
+#. Can `verify` be done without downloading everything?
+
+   * If we know that we are talking to an hg server, we can leverage the fact
+     that it validates in the Batch API portion, and skip d/l altogether.  OTOH,
+     maybe we should download the files unconditionally for forensics.  The
+     alternative is to define a custom transfer handler that definitively
+     verifies without transferring, and then cache those results.  When verify
+     comes looking, look in the cache instead of actually opening the file and
+     processing it.
+
+   * Yuya has concerns about when blob fetch takes place vs when revlog is
+     verified.  Since the visible hash matches the blob content, I don't think
+     there's a way to verify the pointer file that's actually stored in the
+     filelog (other than basic JSON checks).  Full verification requires the
+     blob.  See
+     https://www.mercurial-scm.org/pipermail/mercurial-devel/2018-April/116133.html
+
+   * Opening a corrupt pointer file aborts.  It probably shouldn't for verify.
+
+
+Future ideas/features/polishing
+-------------------------------
+
+These aren't in any particular order, and are things that don't have obvious BC
+concerns.
+
+#. Garbage collection `(issue5790) <https://bz.mercurial-scm.org/show_bug.cgi?id=5790>`_
+
+   * This gets complicated because of the global cache, which may or may not
+     consist of hardlinks to the repo, and may be in use by other repos.  (So
+     the gc may be pointless.)
+
+#. `Compress blobs <https://github.com/git-lfs/git-lfs/issues/260>`_
+
+   * 700MB repo becomes 2.5GB with all lfs blobs
+   * What implications are there for filesystem paths that don't indicate
+     compression?  (i.e. how to share with global cache and other local repos?)
+   * Probably needs to be stored under `.hg/store/lfs/zstd`, with a repo
+     requirement.
+   * Allow tuneable compression type and settings?
+   * Support compression over the wire if both sides understand the compression?
+   * `debugupgraderepo` to convert?
+   * Probably not worth supporting compressed and uncompressed concurrently
+
+#. Determine things to upload with `readfast()
+   <https://www.mercurial-scm.org/pipermail/mercurial-devel/2018-August/121315.html>`_
+
+   * Significantly faster when pushing an entire large repo to http.
+   * Causes test changes to fileset and templates; may need both this and
+     current methods of lookup.
+
+#. Is a command to download everything needed?  This would allow copying the
+   whole to a portable drive.  Currently this can be effected by running
+   `hg verify`.
+
+#. Stop reading in entire file into one buffer when passing through filelog
+   interface
+
+   * `Requires major replumbing to core
+     <https://www.mercurial-scm.org/wiki/HandlingLargeFiles>`_
+
+#. Keep corrupt files around in 'store/lfs/incoming' for forensics?
+
+   * Files should be downloaded to 'incoming', and moved to normal location when
+     done.
+
+#. Client side path enhancements
+
+   * Support paths.default:lfs = ... style paths
+   * SSH -> https server inference
+
+     * https://www.mercurial-scm.org/pipermail/mercurial-devel/2018-April/115416.html
+     * https://github.com/git-lfs/git-lfs/blob/master/docs/api/server-discovery.md#guessing-the-server
+
+#. Server enhancements
+
+   * Add support for transfer quotas?
+   * Download should be able to send the file in chunks, without reading the
+     whole thing into memory
+     (https://www.mercurial-scm.org/pipermail/mercurial-devel/2018-March/114584.html)
+   * Support for resuming transfers
+
+#. Handle 3rd party server storage.
+
+   * Teach client to handle lfs `verify` action.  This is needed after the
+     server instructs the client to upload the file to another server, in order
+     to tell the server that the upload completed.
+   * Teach the server to send redirects if configured, and process `verify`
+     requests.
+
+#. `Is any hg-git work needed
+   <https://groups.google.com/d/msg/hg-git/XYNQuudteeM/ivt8gXoZAAAJ>`_?
--- a/hgext/phabricator.py	Wed May 08 16:09:50 2019 -0400
+++ b/hgext/phabricator.py	Thu May 09 18:37:37 2019 -0400
@@ -65,6 +65,7 @@
     scmutil,
     smartset,
     tags,
+    templatefilters,
     templateutil,
     url as urlmod,
     util,
@@ -380,11 +381,12 @@
     params = {
         b'diff_id': diff[b'id'],
         b'name': b'hg:meta',
-        b'data': json.dumps({
-            u'user': encoding.unifromlocal(ctx.user()),
-            u'date': u'{:.0f} {}'.format(*ctx.date()),
-            u'node': encoding.unifromlocal(ctx.hex()),
-            u'parent': encoding.unifromlocal(ctx.p1().hex()),
+        b'data': templatefilters.json({
+            b'user': ctx.user(),
+            b'date': b'%d %d' % ctx.date(),
+            b'branch': ctx.branch(),
+            b'node': ctx.hex(),
+            b'parent': ctx.p1().hex(),
         }),
     }
     callconduit(ctx.repo(), b'differential.setdiffproperty', params)
@@ -392,12 +394,14 @@
     params = {
         b'diff_id': diff[b'id'],
         b'name': b'local:commits',
-        b'data': json.dumps({
-            encoding.unifromlocal(ctx.hex()): {
-                u'author': encoding.unifromlocal(stringutil.person(ctx.user())),
-                u'authorEmail': encoding.unifromlocal(
-                    stringutil.email(ctx.user())),
-                u'time': u'{:.0f}'.format(ctx.date()[0]),
+        b'data': templatefilters.json({
+            ctx.hex(): {
+                b'author': stringutil.person(ctx.user()),
+                b'authorEmail': stringutil.email(ctx.user()),
+                b'time': int(ctx.date()[0]),
+                b'commit': ctx.hex(),
+                b'parents': [ctx.p1().hex()],
+                b'branch': ctx.branch(),
             },
         }),
     }
@@ -632,7 +636,8 @@
 # Map from "hg:meta" keys to header understood by "hg import". The order is
 # consistent with "hg export" output.
 _metanamemap = util.sortdict([(b'user', b'User'), (b'date', b'Date'),
-                              (b'node', b'Node ID'), (b'parent', b'Parent ')])
+                              (b'branch', b'Branch'), (b'node', b'Node ID'),
+                              (b'parent', b'Parent ')])
 
 def _confirmbeforesend(repo, revs, oldmap):
     url, token = readurltoken(repo)
@@ -901,16 +906,31 @@
     """
     props = diff.get(b'properties') or {}
     meta = props.get(b'hg:meta')
-    if not meta and props.get(b'local:commits'):
-        commit = sorted(props[b'local:commits'].values())[0]
-        meta = {
-            b'date': b'%d 0' % commit[b'time'],
-            b'node': commit[b'rev'],
-            b'user': b'%s <%s>' % (commit[b'author'], commit[b'authorEmail']),
-        }
-        if len(commit.get(b'parents', ())) >= 1:
-            meta[b'parent'] = commit[b'parents'][0]
-    return meta or {}
+    if not meta:
+        if props.get(b'local:commits'):
+            commit = sorted(props[b'local:commits'].values())[0]
+            meta = {}
+            if b'author' in commit and b'authorEmail' in commit:
+                meta[b'user'] = b'%s <%s>' % (commit[b'author'],
+                                              commit[b'authorEmail'])
+            if b'time' in commit:
+                meta[b'date'] = b'%d 0' % commit[b'time']
+            if b'branch' in commit:
+                meta[b'branch'] = commit[b'branch']
+            node = commit.get(b'commit', commit.get(b'rev'))
+            if node:
+                meta[b'node'] = node
+            if len(commit.get(b'parents', ())) >= 1:
+                meta[b'parent'] = commit[b'parents'][0]
+        else:
+            meta = {}
+    if b'date' not in meta and b'dateCreated' in diff:
+        meta[b'date'] = b'%s 0' % diff[b'dateCreated']
+    if b'branch' not in meta and diff.get(b'branch'):
+        meta[b'branch'] = diff[b'branch']
+    if b'parent' not in meta and diff.get(b'sourceControlBaseRevision'):
+        meta[b'parent'] = diff[b'sourceControlBaseRevision']
+    return meta
 
 def readpatch(repo, drevs, write):
     """generate plain-text patch readable by 'hg import'
--- a/hgext/remotefilelog/__init__.py	Wed May 08 16:09:50 2019 -0400
+++ b/hgext/remotefilelog/__init__.py	Thu May 09 18:37:37 2019 -0400
@@ -487,37 +487,6 @@
         return orig(repo, matcher, added, removed, *args, **kwargs)
     extensions.wrapfunction(scmutil, '_findrenames', findrenames)
 
-    # prefetch files before mergecopies check
-    def computenonoverlap(orig, repo, c1, c2, *args, **kwargs):
-        u1, u2 = orig(repo, c1, c2, *args, **kwargs)
-        if isenabled(repo):
-            m1 = c1.manifest()
-            m2 = c2.manifest()
-            files = []
-
-            sparsematch1 = repo.maybesparsematch(c1.rev())
-            if sparsematch1:
-                sparseu1 = set()
-                for f in u1:
-                    if sparsematch1(f):
-                        files.append((f, hex(m1[f])))
-                        sparseu1.add(f)
-                u1 = sparseu1
-
-            sparsematch2 = repo.maybesparsematch(c2.rev())
-            if sparsematch2:
-                sparseu2 = set()
-                for f in u2:
-                    if sparsematch2(f):
-                        files.append((f, hex(m2[f])))
-                        sparseu2.add(f)
-                u2 = sparseu2
-
-            # batch fetch the needed files from the server
-            repo.fileservice.prefetch(files)
-        return u1, u2
-    extensions.wrapfunction(copies, '_computenonoverlap', computenonoverlap)
-
     # prefetch files before pathcopies check
     def computeforwardmissing(orig, a, b, match=None):
         missing = orig(a, b, match=match)
@@ -653,7 +622,7 @@
 
     # Prevent verify from processing files
     # a stub for mercurial.hg.verify()
-    def _verify(orig, repo):
+    def _verify(orig, repo, level=None):
         lock = repo.lock()
         try:
             return shallowverifier.shallowverifier(repo).verify()
--- a/hgext/remotefilelog/fileserverclient.py	Wed May 08 16:09:50 2019 -0400
+++ b/hgext/remotefilelog/fileserverclient.py	Thu May 09 18:37:37 2019 -0400
@@ -396,6 +396,9 @@
                                 batchdefault = 10
                             batchsize = self.ui.configint(
                                 'remotefilelog', 'batchsize', batchdefault)
+                            self.ui.debug(
+                                b'requesting %d files from '
+                                b'remotefilelog server...\n' % len(missed))
                             _getfilesbatch(
                                 remote, self.receivemissing, progress.increment,
                                 missed, idmap, batchsize)
--- a/mercurial/__init__.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/__init__.py	Thu May 09 18:37:37 2019 -0400
@@ -54,7 +54,16 @@
                 if finder == self:
                     continue
 
-                spec = finder.find_spec(fullname, path, target=target)
+                # Originally the API was a `find_module` method, but it was
+                # renamed to `find_spec` in python 3.4, with a new `target`
+                # argument.
+                find_spec_method = getattr(finder, 'find_spec', None)
+                if find_spec_method:
+                    spec = find_spec_method(fullname, path, target=target)
+                else:
+                    spec = finder.find_module(fullname)
+                    if spec is not None:
+                        spec = importlib.util.spec_from_loader(fullname, spec)
                 if spec:
                     break
 
--- a/mercurial/branchmap.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/branchmap.py	Thu May 09 18:37:37 2019 -0400
@@ -378,6 +378,10 @@
         # fetch current topological heads to speed up filtering
         topoheads = set(cl.headrevs())
 
+        # new tip revision which we found after iterating items from new
+        # branches
+        ntiprev = self.tiprev
+
         # if older branchheads are reachable from new ones, they aren't
         # really branchheads. Note checking parents is insufficient:
         # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
@@ -401,9 +405,12 @@
             bheadrevs = sorted(bheadset)
             self[branch] = [cl.node(rev) for rev in bheadrevs]
             tiprev = bheadrevs[-1]
-            if tiprev > self.tiprev:
-                self.tipnode = cl.node(tiprev)
-                self.tiprev = tiprev
+            if tiprev > ntiprev:
+                ntiprev = tiprev
+
+        if ntiprev > self.tiprev:
+            self.tiprev = ntiprev
+            self.tipnode = cl.node(ntiprev)
 
         if not self.validfor(repo):
             # cache key are not valid anymore
@@ -608,51 +615,59 @@
         wlock = None
         step = ''
         try:
+            # write the new names
             if self._rbcnamescount < len(self._names):
-                step = ' names'
                 wlock = repo.wlock(wait=False)
-                if self._rbcnamescount != 0:
-                    f = repo.cachevfs.open(_rbcnames, 'ab')
-                    if f.tell() == self._rbcsnameslen:
-                        f.write('\0')
-                    else:
-                        f.close()
-                        repo.ui.debug("%s changed - rewriting it\n" % _rbcnames)
-                        self._rbcnamescount = 0
-                        self._rbcrevslen = 0
-                if self._rbcnamescount == 0:
-                    # before rewriting names, make sure references are removed
-                    repo.cachevfs.unlinkpath(_rbcrevs, ignoremissing=True)
-                    f = repo.cachevfs.open(_rbcnames, 'wb')
-                f.write('\0'.join(encoding.fromlocal(b)
-                                  for b in self._names[self._rbcnamescount:]))
-                self._rbcsnameslen = f.tell()
-                f.close()
-                self._rbcnamescount = len(self._names)
+                step = ' names'
+                self._writenames(repo)
 
+            # write the new revs
             start = self._rbcrevslen * _rbcrecsize
             if start != len(self._rbcrevs):
                 step = ''
                 if wlock is None:
                     wlock = repo.wlock(wait=False)
-                revs = min(len(repo.changelog),
-                           len(self._rbcrevs) // _rbcrecsize)
-                f = repo.cachevfs.open(_rbcrevs, 'ab')
-                if f.tell() != start:
-                    repo.ui.debug("truncating cache/%s to %d\n"
-                                  % (_rbcrevs, start))
-                    f.seek(start)
-                    if f.tell() != start:
-                        start = 0
-                        f.seek(start)
-                    f.truncate()
-                end = revs * _rbcrecsize
-                f.write(self._rbcrevs[start:end])
-                f.close()
-                self._rbcrevslen = revs
+                self._writerevs(repo, start)
+
         except (IOError, OSError, error.Abort, error.LockError) as inst:
             repo.ui.debug("couldn't write revision branch cache%s: %s\n"
                           % (step, stringutil.forcebytestr(inst)))
         finally:
             if wlock is not None:
                 wlock.release()
+
+    def _writenames(self, repo):
+        """ write the new branch names to revbranchcache """
+        if self._rbcnamescount != 0:
+            f = repo.cachevfs.open(_rbcnames, 'ab')
+            if f.tell() == self._rbcsnameslen:
+                f.write('\0')
+            else:
+                f.close()
+                repo.ui.debug("%s changed - rewriting it\n" % _rbcnames)
+                self._rbcnamescount = 0
+                self._rbcrevslen = 0
+        if self._rbcnamescount == 0:
+            # before rewriting names, make sure references are removed
+            repo.cachevfs.unlinkpath(_rbcrevs, ignoremissing=True)
+            f = repo.cachevfs.open(_rbcnames, 'wb')
+        f.write('\0'.join(encoding.fromlocal(b)
+                          for b in self._names[self._rbcnamescount:]))
+        self._rbcsnameslen = f.tell()
+        f.close()
+        self._rbcnamescount = len(self._names)
+
+    def _writerevs(self, repo, start):
+        """ write the new revs to revbranchcache """
+        revs = min(len(repo.changelog), len(self._rbcrevs) // _rbcrecsize)
+        with repo.cachevfs.open(_rbcrevs, 'ab') as f:
+            if f.tell() != start:
+                repo.ui.debug("truncating cache/%s to %d\n" % (_rbcrevs, start))
+                f.seek(start)
+                if f.tell() != start:
+                    start = 0
+                    f.seek(start)
+                f.truncate()
+            end = revs * _rbcrecsize
+            f.write(self._rbcrevs[start:end])
+        self._rbcrevslen = revs
--- a/mercurial/commands.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/commands.py	Thu May 09 18:37:37 2019 -0400
@@ -63,6 +63,7 @@
     tags as tagsmod,
     ui as uimod,
     util,
+    verify as verifymod,
     wireprotoserver,
 )
 from .utils import (
@@ -1674,6 +1675,8 @@
 
         if not bheads:
             raise error.Abort(_('can only close branch heads'))
+        elif branch == repo['.'].branch() and repo['.'].node() not in bheads:
+            raise error.Abort(_('can only close branch heads'))
         elif opts.get('amend'):
             if (repo['.'].p1().branch() != branch and
                 repo['.'].p2().branch() != branch):
@@ -1732,6 +1735,10 @@
 
     cmdutil.commitstatus(repo, node, branch, bheads, opts)
 
+    if not ui.quiet and ui.configbool('commands', 'commit.post-status'):
+        status(ui, repo, modified=True, added=True, removed=True, deleted=True,
+               unknown=True, subrepos=opts.get('subrepos'))
+
 @command('config|showconfig|debugconfig',
     [('u', 'untrusted', None, _('show untrusted configuration options')),
      ('e', 'edit', None, _('edit user config')),
@@ -3715,7 +3722,8 @@
      _('follow line range of specified file (EXPERIMENTAL)'),
      _('FILE,RANGE')),
     ('', 'removed', None, _('include revisions where files were removed')),
-    ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
+    ('m', 'only-merges', None,
+     _('show only merges (DEPRECATED) (use -r "merge()" instead)')),
     ('u', 'user', [], _('revisions committed by user'), _('USER')),
     ('', 'only-branch', [],
      _('show only changesets within the given named branch (DEPRECATED)'),
@@ -6147,8 +6155,10 @@
                 ui.warn("(%s)\n" % obsfatemsg)
         return ret
 
-@command('verify', [], helpcategory=command.CATEGORY_MAINTENANCE)
-def verify(ui, repo):
+@command('verify',
+         [('', 'full', False, 'perform more checks (EXPERIMENTAL)')],
+         helpcategory=command.CATEGORY_MAINTENANCE)
+def verify(ui, repo, **opts):
     """verify the integrity of the repository
 
     Verify the integrity of the current repository.
@@ -6164,7 +6174,12 @@
 
     Returns 0 on success, 1 if errors are encountered.
     """
-    return hg.verify(repo)
+    opts = pycompat.byteskwargs(opts)
+
+    level = None
+    if opts['full']:
+        level = verifymod.VERIFY_FULL
+    return hg.verify(repo, level)
 
 @command(
     'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
--- a/mercurial/configitems.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/configitems.py	Thu May 09 18:37:37 2019 -0400
@@ -202,6 +202,9 @@
     default=dynamicdefault,
 )
 _registerdiffopts(section='commands', configprefix='commit.interactive.')
+coreconfigitem('commands', 'commit.post-status',
+    default=False,
+)
 coreconfigitem('commands', 'grep.all-files',
     default=False,
 )
@@ -532,6 +535,13 @@
 coreconfigitem('experimental', 'evolution.track-operation',
     default=True,
 )
+# repo-level config to exclude a revset visibility
+#
+# The target use case is to use `share` to expose different subset of the same
+# repository, especially server side. See also `server.view`.
+coreconfigitem('experimental', 'extra-filter-revs',
+    default=None,
+)
 coreconfigitem('experimental', 'maxdeltachainspan',
     default=-1,
 )
--- a/mercurial/copies.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/copies.py	Thu May 09 18:37:37 2019 -0400
@@ -108,39 +108,56 @@
     return min(limit, a, b)
 
 def _chain(src, dst, a, b):
-    """chain two sets of copies a->b"""
+    """chain two sets of copies 'a' and 'b'"""
+
+    # When chaining copies in 'a' (from 'src' via some other commit 'mid') with
+    # copies in 'b' (from 'mid' to 'dst'), we can get the different cases in the
+    # following table (not including trivial cases). For example, case 2 is
+    # where a file existed in 'src' and remained under that name in 'mid' and
+    # then was renamed between 'mid' and 'dst'.
+    #
+    # case src mid dst result
+    #   1   x   y   -    -
+    #   2   x   y   y   x->y
+    #   3   x   y   x    -
+    #   4   x   y   z   x->z
+    #   5   -   x   y    -
+    #   6   x   x   y   x->y
+
+    # Initialize result ('t') from 'a'. This catches cases 1 & 2. We'll remove
+    # case 1 later. We'll also catch cases 3 & 4 here. Case 4 will be
+    # overwritten later, and case 3 will be removed later.
     t = a.copy()
     for k, v in b.iteritems():
         if v in t:
-            # found a chain
-            if t[v] != k:
-                # file wasn't renamed back to itself
-                t[k] = t[v]
-            if v not in dst:
-                # chain was a rename, not a copy
-                del t[v]
-        if v in src:
-            # file is a copy of an existing file
+            # Found a chain, i.e. cases 3 & 4. We'll remove case 3 later.
+            t[k] = t[v]
+        else:
+            # Renamed only in 'b', i.e. cases 5 & 6. We'll remove case 5 later.
             t[k] = v
 
     for k, v in list(t.items()):
-        # remove criss-crossed copies
-        if k in src and v in dst:
+        # remove copies from files that didn't exist, i.e. case 5
+        if v not in src:
             del t[k]
-        # remove copies to files that were then removed
+        # remove criss-crossed copies, i.e. case 3
+        elif k in src and v in dst:
+            del t[k]
+        # remove copies to files that were then removed, i.e. case 1
+        # and file 'y' in cases 3 & 4 (in case of rename)
         elif k not in dst:
             del t[k]
 
     return t
 
-def _tracefile(fctx, am, limit=node.nullrev):
+def _tracefile(fctx, am, limit):
     """return file context that is the ancestor of fctx present in ancestor
     manifest am, stopping after the first ancestor lower than limit"""
 
     for f in fctx.ancestors():
         if am.get(f.path(), None) == f.filenode():
             return f
-        if limit >= 0 and not f.isintroducedafter(limit):
+        if not f.isintroducedafter(limit):
             return None
 
 def _dirstatecopies(repo, match=None):
@@ -204,9 +221,9 @@
     ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
 
     if debug:
-        dbg('debug.copies:      missing file to search: %d\n' % len(missing))
+        dbg('debug.copies:      missing files to search: %d\n' % len(missing))
 
-    for f in missing:
+    for f in sorted(missing):
         if debug:
             dbg('debug.copies:        tracing file: %s\n' % f)
         fctx = b[f]
@@ -353,81 +370,6 @@
     return _chain(x, y, _backwardrenames(x, a, match=match),
                   _forwardcopies(a, y, match=match))
 
-def _computenonoverlap(repo, c1, c2, addedinm1, addedinm2, baselabel=''):
-    """Computes, based on addedinm1 and addedinm2, the files exclusive to c1
-    and c2. This is its own function so extensions can easily wrap this call
-    to see what files mergecopies is about to process.
-
-    Even though c1 and c2 are not used in this function, they are useful in
-    other extensions for being able to read the file nodes of the changed files.
-
-    "baselabel" can be passed to help distinguish the multiple computations
-    done in the graft case.
-    """
-    u1 = sorted(addedinm1 - addedinm2)
-    u2 = sorted(addedinm2 - addedinm1)
-
-    header = "  unmatched files in %s"
-    if baselabel:
-        header += ' (from %s)' % baselabel
-    if u1:
-        repo.ui.debug("%s:\n   %s\n" % (header % 'local', "\n   ".join(u1)))
-    if u2:
-        repo.ui.debug("%s:\n   %s\n" % (header % 'other', "\n   ".join(u2)))
-
-    return u1, u2
-
-def _makegetfctx(ctx):
-    """return a 'getfctx' function suitable for _checkcopies usage
-
-    We have to re-setup the function building 'filectx' for each
-    '_checkcopies' to ensure the linkrev adjustment is properly setup for
-    each. Linkrev adjustment is important to avoid bug in rename
-    detection. Moreover, having a proper '_ancestrycontext' setup ensures
-    the performance impact of this adjustment is kept limited. Without it,
-    each file could do a full dag traversal making the time complexity of
-    the operation explode (see issue4537).
-
-    This function exists here mostly to limit the impact on stable. Feel
-    free to refactor on default.
-    """
-    rev = ctx.rev()
-    repo = ctx._repo
-    ac = getattr(ctx, '_ancestrycontext', None)
-    if ac is None:
-        revs = [rev]
-        if rev is None:
-            revs = [p.rev() for p in ctx.parents()]
-        ac = repo.changelog.ancestors(revs, inclusive=True)
-        ctx._ancestrycontext = ac
-    def makectx(f, n):
-        if n in node.wdirfilenodeids:  # in a working context?
-            if ctx.rev() is None:
-                return ctx.filectx(f)
-            return repo[None][f]
-        fctx = repo.filectx(f, fileid=n)
-        # setup only needed for filectx not create from a changectx
-        fctx._ancestrycontext = ac
-        fctx._descendantrev = rev
-        return fctx
-    return util.lrucachefunc(makectx)
-
-def _combinecopies(copyfrom, copyto, finalcopy, diverge, incompletediverge):
-    """combine partial copy paths"""
-    remainder = {}
-    for f in copyfrom:
-        if f in copyto:
-            finalcopy[copyto[f]] = copyfrom[f]
-            del copyto[f]
-    for f in incompletediverge:
-        assert f not in diverge
-        ic = incompletediverge[f]
-        if ic[0] in copyto:
-            diverge[f] = [copyto[ic[0]], ic[1]]
-        else:
-            remainder[f] = ic
-    return remainder
-
 def mergecopies(repo, c1, c2, base):
     """
     Finds moves and copies between context c1 and c2 that are relevant for
@@ -485,7 +427,14 @@
         return _dirstatecopies(repo, narrowmatch), {}, {}, {}, {}
 
     copytracing = repo.ui.config('experimental', 'copytrace')
-    boolctrace = stringutil.parsebool(copytracing)
+    if stringutil.parsebool(copytracing) is False:
+        # stringutil.parsebool() returns None when it is unable to parse the
+        # value, so we should rely on making sure copytracing is on such cases
+        return {}, {}, {}, {}, {}
+
+    if usechangesetcentricalgo(repo):
+        # The heuristics don't make sense when we need changeset-centric algos
+        return _fullcopytracing(repo, c1, c2, base)
 
     # Copy trace disabling is explicitly below the node == p1 logic above
     # because the logic above is required for a simple copy to be kept across a
@@ -497,10 +446,6 @@
         if _isfullcopytraceable(repo, c1, base):
             return _fullcopytracing(repo, c1, c2, base)
         return _heuristicscopytracing(repo, c1, c2, base)
-    elif boolctrace is False:
-        # stringutil.parsebool() returns None when it is unable to parse the
-        # value, so we should rely on making sure copytracing is on such cases
-        return {}, {}, {}, {}, {}
     else:
         return _fullcopytracing(repo, c1, c2, base)
 
@@ -522,6 +467,23 @@
         return commits < sourcecommitlimit
     return False
 
+def _checksinglesidecopies(src, dsts1, m1, m2, mb, c2, base,
+                           copy, renamedelete):
+    if src not in m2:
+        # deleted on side 2
+        if src not in m1:
+            # renamed on side 1, deleted on side 2
+            renamedelete[src] = dsts1
+    elif m2[src] != mb[src]:
+        if not _related(c2[src], base[src]):
+            return
+        # modified on side 2
+        for dst in dsts1:
+            if dst not in m2:
+                # dst not added on side 2 (handle as regular
+                # "both created" case in manifestmerge otherwise)
+                copy[dst] = src
+
 def _fullcopytracing(repo, c1, c2, base):
     """ The full copytracing algorithm which finds all the new files that were
     added from merge base up to the top commit and for each file it checks if
@@ -530,159 +492,84 @@
     This is pretty slow when a lot of changesets are involved but will track all
     the copies.
     """
-    # In certain scenarios (e.g. graft, update or rebase), base can be
-    # overridden We still need to know a real common ancestor in this case We
-    # can't just compute _c1.ancestor(_c2) and compare it to ca, because there
-    # can be multiple common ancestors, e.g. in case of bidmerge.  Because our
-    # caller may not know if the revision passed in lieu of the CA is a genuine
-    # common ancestor or not without explicitly checking it, it's better to
-    # determine that here.
-    #
-    # base.isancestorof(wc) is False, work around that
-    _c1 = c1.p1() if c1.rev() is None else c1
-    _c2 = c2.p1() if c2.rev() is None else c2
-    # an endpoint is "dirty" if it isn't a descendant of the merge base
-    # if we have a dirty endpoint, we need to trigger graft logic, and also
-    # keep track of which endpoint is dirty
-    dirtyc1 = not base.isancestorof(_c1)
-    dirtyc2 = not base.isancestorof(_c2)
-    graft = dirtyc1 or dirtyc2
-    tca = base
-    if graft:
-        tca = _c1.ancestor(_c2)
-
-    limit = _findlimit(repo, c1, c2)
-    repo.ui.debug("  searching for copies back to rev %d\n" % limit)
-
     m1 = c1.manifest()
     m2 = c2.manifest()
     mb = base.manifest()
 
-    # gather data from _checkcopies:
-    # - diverge = record all diverges in this dict
-    # - copy = record all non-divergent copies in this dict
-    # - fullcopy = record all copies in this dict
-    # - incomplete = record non-divergent partial copies here
-    # - incompletediverge = record divergent partial copies here
-    diverge = {} # divergence data is shared
-    incompletediverge  = {}
-    data1 = {'copy': {},
-             'fullcopy': {},
-             'incomplete': {},
-             'diverge': diverge,
-             'incompletediverge': incompletediverge,
-            }
-    data2 = {'copy': {},
-             'fullcopy': {},
-             'incomplete': {},
-             'diverge': diverge,
-             'incompletediverge': incompletediverge,
-            }
+    copies1 = pathcopies(base, c1)
+    copies2 = pathcopies(base, c2)
+
+    inversecopies1 = {}
+    inversecopies2 = {}
+    for dst, src in copies1.items():
+        inversecopies1.setdefault(src, []).append(dst)
+    for dst, src in copies2.items():
+        inversecopies2.setdefault(src, []).append(dst)
+
+    copy = {}
+    diverge = {}
+    renamedelete = {}
+    allsources = set(inversecopies1) | set(inversecopies2)
+    for src in allsources:
+        dsts1 = inversecopies1.get(src)
+        dsts2 = inversecopies2.get(src)
+        if dsts1 and dsts2:
+            # copied/renamed on both sides
+            if src not in m1 and src not in m2:
+                # renamed on both sides
+                dsts1 = set(dsts1)
+                dsts2 = set(dsts2)
+                # If there's some overlap in the rename destinations, we
+                # consider it not divergent. For example, if side 1 copies 'a'
+                # to 'b' and 'c' and deletes 'a', and side 2 copies 'a' to 'c'
+                # and 'd' and deletes 'a'.
+                if dsts1 & dsts2:
+                    for dst in (dsts1 & dsts2):
+                        copy[dst] = src
+                else:
+                    diverge[src] = sorted(dsts1 | dsts2)
+            elif src in m1 and src in m2:
+                # copied on both sides
+                dsts1 = set(dsts1)
+                dsts2 = set(dsts2)
+                for dst in (dsts1 & dsts2):
+                    copy[dst] = src
+            # TODO: Handle cases where it was renamed on one side and copied
+            # on the other side
+        elif dsts1:
+            # copied/renamed only on side 1
+            _checksinglesidecopies(src, dsts1, m1, m2, mb, c2, base,
+                                   copy, renamedelete)
+        elif dsts2:
+            # copied/renamed only on side 2
+            _checksinglesidecopies(src, dsts2, m2, m1, mb, c1, base,
+                                   copy, renamedelete)
+
+    renamedeleteset = set()
+    divergeset = set()
+    for dsts in diverge.values():
+        divergeset.update(dsts)
+    for dsts in renamedelete.values():
+        renamedeleteset.update(dsts)
 
     # find interesting file sets from manifests
     addedinm1 = m1.filesnotin(mb, repo.narrowmatch())
     addedinm2 = m2.filesnotin(mb, repo.narrowmatch())
-    bothnew = sorted(addedinm1 & addedinm2)
-    if tca == base:
-        # unmatched file from base
-        u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2)
-        u1u, u2u = u1r, u2r
-    else:
-        # unmatched file from base (DAG rotation in the graft case)
-        u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2,
-                                      baselabel='base')
-        # unmatched file from topological common ancestors (no DAG rotation)
-        # need to recompute this for directory move handling when grafting
-        mta = tca.manifest()
-        u1u, u2u = _computenonoverlap(repo, c1, c2,
-                                      m1.filesnotin(mta, repo.narrowmatch()),
-                                      m2.filesnotin(mta, repo.narrowmatch()),
-                                      baselabel='topological common ancestor')
-
-    for f in u1u:
-        _checkcopies(c1, c2, f, base, tca, dirtyc1, limit, data1)
-
-    for f in u2u:
-        _checkcopies(c2, c1, f, base, tca, dirtyc2, limit, data2)
-
-    copy = dict(data1['copy'])
-    copy.update(data2['copy'])
-    fullcopy = dict(data1['fullcopy'])
-    fullcopy.update(data2['fullcopy'])
-
-    if dirtyc1:
-        _combinecopies(data2['incomplete'], data1['incomplete'], copy, diverge,
-                       incompletediverge)
-    if dirtyc2:
-        _combinecopies(data1['incomplete'], data2['incomplete'], copy, diverge,
-                       incompletediverge)
-
-    renamedelete = {}
-    renamedeleteset = set()
-    divergeset = set()
-    for of, fl in list(diverge.items()):
-        if len(fl) == 1 or of in c1 or of in c2:
-            del diverge[of] # not actually divergent, or not a rename
-            if of not in c1 and of not in c2:
-                # renamed on one side, deleted on the other side, but filter
-                # out files that have been renamed and then deleted
-                renamedelete[of] = [f for f in fl if f in c1 or f in c2]
-                renamedeleteset.update(fl) # reverse map for below
-        else:
-            divergeset.update(fl) # reverse map for below
+    u1 = sorted(addedinm1 - addedinm2)
+    u2 = sorted(addedinm2 - addedinm1)
 
-    if bothnew:
-        repo.ui.debug("  unmatched files new in both:\n   %s\n"
-                      % "\n   ".join(bothnew))
-    bothdiverge = {}
-    bothincompletediverge = {}
-    remainder = {}
-    both1 = {'copy': {},
-             'fullcopy': {},
-             'incomplete': {},
-             'diverge': bothdiverge,
-             'incompletediverge': bothincompletediverge
-            }
-    both2 = {'copy': {},
-             'fullcopy': {},
-             'incomplete': {},
-             'diverge': bothdiverge,
-             'incompletediverge': bothincompletediverge
-            }
-    for f in bothnew:
-        _checkcopies(c1, c2, f, base, tca, dirtyc1, limit, both1)
-        _checkcopies(c2, c1, f, base, tca, dirtyc2, limit, both2)
-    if dirtyc1 and dirtyc2:
-        remainder = _combinecopies(both2['incomplete'], both1['incomplete'],
-                                   copy, bothdiverge, bothincompletediverge)
-        remainder1 = _combinecopies(both1['incomplete'], both2['incomplete'],
-                                   copy, bothdiverge, bothincompletediverge)
-        remainder.update(remainder1)
-    elif dirtyc1:
-        # incomplete copies may only be found on the "dirty" side for bothnew
-        assert not both2['incomplete']
-        remainder = _combinecopies({}, both1['incomplete'], copy, bothdiverge,
-                                   bothincompletediverge)
-    elif dirtyc2:
-        assert not both1['incomplete']
-        remainder = _combinecopies({}, both2['incomplete'], copy, bothdiverge,
-                                   bothincompletediverge)
-    else:
-        # incomplete copies and divergences can't happen outside grafts
-        assert not both1['incomplete']
-        assert not both2['incomplete']
-        assert not bothincompletediverge
-    for f in remainder:
-        assert f not in bothdiverge
-        ic = remainder[f]
-        if ic[0] in (m1 if dirtyc1 else m2):
-            # backed-out rename on one side, but watch out for deleted files
-            bothdiverge[f] = ic
-    for of, fl in bothdiverge.items():
-        if len(fl) == 2 and fl[0] == fl[1]:
-            copy[fl[0]] = of # not actually divergent, just matching renames
+    header = "  unmatched files in %s"
+    if u1:
+        repo.ui.debug("%s:\n   %s\n" % (header % 'local', "\n   ".join(u1)))
+    if u2:
+        repo.ui.debug("%s:\n   %s\n" % (header % 'other', "\n   ".join(u2)))
 
-    if fullcopy and repo.ui.debugflag:
+    fullcopy = copies1.copy()
+    fullcopy.update(copies2)
+    if not fullcopy:
+        return copy, {}, diverge, renamedelete, {}
+
+    if repo.ui.debugflag:
         repo.ui.debug("  all copies found (* = to merge, ! = divergent, "
                       "% = renamed and deleted):\n")
         for f in sorted(fullcopy):
@@ -697,9 +584,6 @@
                                                               note))
     del divergeset
 
-    if not fullcopy:
-        return copy, {}, diverge, renamedelete, {}
-
     repo.ui.debug("  checking for directory renames\n")
 
     # generate a directory move map
@@ -746,7 +630,7 @@
 
     movewithdir = {}
     # check unaccounted nonoverlapping files against directory moves
-    for f in u1r + u2r:
+    for f in u1 + u2:
         if f not in fullcopy:
             for d in dirmove:
                 if f.startswith(d):
@@ -893,99 +777,6 @@
     except StopIteration:
         return False
 
-def _checkcopies(srcctx, dstctx, f, base, tca, remotebase, limit, data):
-    """
-    check possible copies of f from msrc to mdst
-
-    srcctx = starting context for f in msrc
-    dstctx = destination context for f in mdst
-    f = the filename to check (as in msrc)
-    base = the changectx used as a merge base
-    tca = topological common ancestor for graft-like scenarios
-    remotebase = True if base is outside tca::srcctx, False otherwise
-    limit = the rev number to not search beyond
-    data = dictionary of dictionary to store copy data. (see mergecopies)
-
-    note: limit is only an optimization, and provides no guarantee that
-    irrelevant revisions will not be visited
-    there is no easy way to make this algorithm stop in a guaranteed way
-    once it "goes behind a certain revision".
-    """
-
-    msrc = srcctx.manifest()
-    mdst = dstctx.manifest()
-    mb = base.manifest()
-    mta = tca.manifest()
-    # Might be true if this call is about finding backward renames,
-    # This happens in the case of grafts because the DAG is then rotated.
-    # If the file exists in both the base and the source, we are not looking
-    # for a rename on the source side, but on the part of the DAG that is
-    # traversed backwards.
-    #
-    # In the case there is both backward and forward renames (before and after
-    # the base) this is more complicated as we must detect a divergence.
-    # We use 'backwards = False' in that case.
-    backwards = not remotebase and base != tca and f in mb
-    getsrcfctx = _makegetfctx(srcctx)
-    getdstfctx = _makegetfctx(dstctx)
-
-    if msrc[f] == mb.get(f) and not remotebase:
-        # Nothing to merge
-        return
-
-    of = None
-    seen = {f}
-    for oc in getsrcfctx(f, msrc[f]).ancestors():
-        of = oc.path()
-        if of in seen:
-            # check limit late - grab last rename before
-            if oc.linkrev() < limit:
-                break
-            continue
-        seen.add(of)
-
-        # remember for dir rename detection
-        if backwards:
-            data['fullcopy'][of] = f # grafting backwards through renames
-        else:
-            data['fullcopy'][f] = of
-        if of not in mdst:
-            continue # no match, keep looking
-        if mdst[of] == mb.get(of):
-            return # no merge needed, quit early
-        c2 = getdstfctx(of, mdst[of])
-        # c2 might be a plain new file on added on destination side that is
-        # unrelated to the droids we are looking for.
-        cr = _related(oc, c2)
-        if cr and (of == f or of == c2.path()): # non-divergent
-            if backwards:
-                data['copy'][of] = f
-            elif of in mb:
-                data['copy'][f] = of
-            elif remotebase: # special case: a <- b <- a -> b "ping-pong" rename
-                data['copy'][of] = f
-                del data['fullcopy'][f]
-                data['fullcopy'][of] = f
-            else: # divergence w.r.t. graft CA on one side of topological CA
-                for sf in seen:
-                    if sf in mb:
-                        assert sf not in data['diverge']
-                        data['diverge'][sf] = [f, of]
-                        break
-            return
-
-    if of in mta:
-        if backwards or remotebase:
-            data['incomplete'][of] = f
-        else:
-            for sf in seen:
-                if sf in mb:
-                    if tca == base:
-                        data['diverge'].setdefault(sf, []).append(f)
-                    else:
-                        data['incompletediverge'][sf] = [of, f]
-                    return
-
 def duplicatecopies(repo, wctx, rev, fromrev, skiprev=None):
     """reproduce copies from fromrev to rev in the dirstate
 
@@ -1005,8 +796,6 @@
         # metadata across the rebase anyway).
         exclude = pathcopies(repo[fromrev], repo[skiprev])
     for dst, src in pathcopies(repo[fromrev], repo[rev]).iteritems():
-        # copies.pathcopies returns backward renames, so dst might not
-        # actually be in the dirstate
         if dst in exclude:
             continue
         wctx[dst].markcopied(src)
--- a/mercurial/discovery.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/discovery.py	Thu May 09 18:37:37 2019 -0400
@@ -343,10 +343,19 @@
     # 1. Check for new branches on the remote.
     if newbranches and not newbranch:  # new branch requires --new-branch
         branchnames = ', '.join(sorted(newbranches))
-        raise error.Abort(_("push creates new remote branches: %s!")
-                           % branchnames,
-                         hint=_("use 'hg push --new-branch' to create"
-                                " new remote branches"))
+        # Calculate how many of the new branches are closed branches
+        closedbranches = set()
+        for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
+            if isclosed:
+                closedbranches.add(tag)
+        closedbranches = (closedbranches & set(newbranches))
+        if closedbranches:
+            errmsg = (_("push creates new remote branches: %s (%d closed)!")
+                        % (branchnames, len(closedbranches)))
+        else:
+            errmsg = (_("push creates new remote branches: %s!")% branchnames)
+        hint=_("use 'hg push --new-branch' to create new remote branches")
+        raise error.Abort(errmsg, hint=hint)
 
     # 2. Find heads that we need not warn about
     nowarnheads = _nowarnheads(pushop)
--- a/mercurial/help/config.txt	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/help/config.txt	Thu May 09 18:37:37 2019 -0400
@@ -438,6 +438,10 @@
 ``commands``
 ------------
 
+``commit.post-status``
+    Show status of files in the working directory after successful commit.
+    (default: False)
+
 ``resolve.confirm``
     Confirm before performing action if no filename is passed.
     (default: False)
--- a/mercurial/hg.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/hg.py	Thu May 09 18:37:37 2019 -0400
@@ -1092,9 +1092,9 @@
     recurse()
     return 0 # exit code is zero since we found outgoing changes
 
-def verify(repo):
+def verify(repo, level=None):
     """verify the consistency of a repository"""
-    ret = verifymod.verify(repo)
+    ret = verifymod.verify(repo, level=level)
 
     # Broken subrepo references in hidden csets don't seem worth worrying about,
     # since they can't be pushed/pulled, and --hidden can be used if they are a
--- a/mercurial/hgweb/__init__.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/hgweb/__init__.py	Thu May 09 18:37:37 2019 -0400
@@ -38,6 +38,9 @@
     - list of virtual:real tuples (multi-repo view)
     '''
 
+    if isinstance(config, pycompat.unicode):
+        raise error.ProgrammingError(
+            'Mercurial only supports encoded strings: %r' % config)
     if ((isinstance(config, bytes) and not os.path.isdir(config)) or
         isinstance(config, dict) or isinstance(config, list)):
         # create a multi-dir interface
--- a/mercurial/httppeer.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/httppeer.py	Thu May 09 18:37:37 2019 -0400
@@ -382,6 +382,7 @@
         self._path = path
         self._url = url
         self._caps = caps
+        self.limitedarguments = caps is not None and 'httppostargs' not in caps
         self._urlopener = opener
         self._requestbuilder = requestbuilder
 
@@ -750,6 +751,9 @@
 
 @interfaceutil.implementer(repository.ipeerv2)
 class httpv2peer(object):
+
+    limitedarguments = False
+
     def __init__(self, ui, repourl, apipath, opener, requestbuilder,
                  apidescriptor):
         self.ui = ui
--- a/mercurial/localrepo.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/localrepo.py	Thu May 09 18:37:37 2019 -0400
@@ -1050,6 +1050,8 @@
         # Signature to cached matcher instance.
         self._sparsematchercache = {}
 
+        self._extrafilterid = repoview.extrafilter(ui)
+
     def _getvfsward(self, origfunc):
         """build a ward for self.vfs"""
         rref = weakref.ref(self)
@@ -1197,6 +1199,9 @@
 
         In other word, there is always only one level of `repoview` "filtering".
         """
+        if self._extrafilterid is not None and '%' not in name:
+            name = name + '%'  + self._extrafilterid
+
         cls = repoview.newtype(self.unfiltered().__class__)
         return cls(self, name, visibilityexceptions)
 
@@ -2127,6 +2132,8 @@
             for ctx in self['.'].parents():
                 ctx.manifest()  # accessing the manifest is enough
 
+            # accessing fnode cache warms the cache
+            tagsmod.fnoderevs(self.ui, unfi, unfi.changelog.revs())
             # accessing tags warm the cache
             self.tags()
             self.filtered('served').tags()
@@ -2531,12 +2538,12 @@
                         _('note: commit message saved in %s\n') % msgfn)
                 raise
 
-        def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
+        def commithook():
             # hack for command that use a temporary commit (eg: histedit)
             # temporary commit got stripped before hook release
             if self.changelog.hasnode(ret):
-                self.hook("commit", node=node, parent1=parent1,
-                          parent2=parent2)
+                self.hook("commit", node=hex(ret), parent1=hookp1,
+                          parent2=hookp2)
         self._afterlock(commithook)
         return ret
 
--- a/mercurial/match.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/match.py	Thu May 09 18:37:37 2019 -0400
@@ -305,9 +305,6 @@
 
     def __call__(self, fn):
         return self.matchfn(fn)
-    def __iter__(self):
-        for f in self._files:
-            yield f
     # Callbacks related to how the matcher is used by dirstate.walk.
     # Subscribers to these events must monkeypatch the matcher object.
     def bad(self, f, msg):
@@ -484,7 +481,7 @@
     """Matches a set of (kind, pat, source) against a 'root' directory.
 
     >>> kindpats = [
-    ...     (b're', b'.*\.c$', b''),
+    ...     (b're', br'.*\.c$', b''),
     ...     (b'path', b'foo/a', b''),
     ...     (b'relpath', b'b', b''),
     ...     (b'glob', b'*.h', b''),
@@ -651,7 +648,7 @@
     r'''Matches the input files exactly. They are interpreted as paths, not
     patterns (so no kind-prefixes).
 
-    >>> m = exactmatcher([b'a.txt', b're:.*\.c$'])
+    >>> m = exactmatcher([b'a.txt', br're:.*\.c$'])
     >>> m(b'a.txt')
     True
     >>> m(b'b.txt')
@@ -664,7 +661,7 @@
     So pattern 're:.*\.c$' is not considered as a regex, but as a file name
     >>> m(b'main.c')
     False
-    >>> m(b're:.*\.c$')
+    >>> m(br're:.*\.c$')
     True
     '''
 
@@ -1075,7 +1072,7 @@
 def patkind(pattern, default=None):
     '''If pattern is 'kind:pat' with a known kind, return kind.
 
-    >>> patkind(b're:.*\.c$')
+    >>> patkind(br're:.*\.c$')
     're'
     >>> patkind(b'glob:*.c')
     'glob'
--- a/mercurial/merge.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/merge.py	Thu May 09 18:37:37 2019 -0400
@@ -1380,7 +1380,6 @@
         # Pick the best bid for each file
         repo.ui.note(_('\nauction for merging merge bids\n'))
         actions = {}
-        dms = [] # filenames that have dm actions
         for f, bids in sorted(fbids.items()):
             # bids is a mapping from action method to list af actions
             # Consensus?
@@ -1389,8 +1388,6 @@
                 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
                     repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
                     actions[f] = l[0]
-                    if m == ACTION_DIR_RENAME_MOVE_LOCAL:
-                        dms.append(f)
                     continue
             # If keep is an option, just do it.
             if ACTION_KEEP in bids:
@@ -1415,18 +1412,7 @@
             repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
                          (f, m))
             actions[f] = l[0]
-            if m == ACTION_DIR_RENAME_MOVE_LOCAL:
-                dms.append(f)
             continue
-        # Work around 'dm' that can cause multiple actions for the same file
-        for f in dms:
-            dm, (f0, flags), msg = actions[f]
-            assert dm == ACTION_DIR_RENAME_MOVE_LOCAL, dm
-            if f0 in actions and actions[f0][0] == ACTION_REMOVE:
-                # We have one bid for removing a file and another for moving it.
-                # These two could be merged as first move and then delete ...
-                # but instead drop moving and just delete.
-                del actions[f]
         repo.ui.note(_('end of auction\n\n'))
 
     if wctx.rev() is None:
--- a/mercurial/minirst.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/minirst.py	Thu May 09 18:37:37 2019 -0400
@@ -44,6 +44,9 @@
 def subsubsubsection(s):
     return "%s\n%s\n\n" % (s, "." * encoding.colwidth(s))
 
+def subsubsubsubsection(s):
+    return "%s\n%s\n\n" % (s, "'" * encoding.colwidth(s))
+
 def replace(text, substs):
     '''
     Apply a list of (find, replace) pairs to a text.
--- a/mercurial/narrowspec.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/narrowspec.py	Thu May 09 18:37:37 2019 -0400
@@ -15,6 +15,7 @@
     match as matchmod,
     merge,
     repository,
+    scmutil,
     sparse,
     util,
 )
@@ -292,8 +293,8 @@
     removedmatch = matchmod.differencematcher(oldmatch, newmatch)
 
     ds = repo.dirstate
-    lookup, status = ds.status(removedmatch, subrepos=[], ignored=False,
-                               clean=True, unknown=False)
+    lookup, status = ds.status(removedmatch, subrepos=[], ignored=True,
+                               clean=True, unknown=True)
     trackeddirty = status.modified + status.added
     clean = status.clean
     if assumeclean:
@@ -302,8 +303,13 @@
     else:
         trackeddirty.extend(lookup)
     _deletecleanfiles(repo, clean)
+    uipathfn = scmutil.getuipathfn(repo)
     for f in sorted(trackeddirty):
-        repo.ui.status(_('not deleting possibly dirty file %s\n') % f)
+        repo.ui.status(_('not deleting possibly dirty file %s\n') % uipathfn(f))
+    for f in sorted(status.unknown):
+        repo.ui.status(_('not deleting unknown file %s\n') % uipathfn(f))
+    for f in sorted(status.ignored):
+        repo.ui.status(_('not deleting ignored file %s\n') % uipathfn(f))
     for f in clean + trackeddirty:
         ds.drop(f)
 
--- a/mercurial/obsolete.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/obsolete.py	Thu May 09 18:37:37 2019 -0400
@@ -93,10 +93,6 @@
 _calcsize = struct.calcsize
 propertycache = util.propertycache
 
-# the obsolete feature is not mature enough to be enabled by default.
-# you have to rely on third party extension extension to enable this.
-_enabled = False
-
 # Options for obsolescence
 createmarkersopt = 'createmarkers'
 allowunstableopt = 'allowunstable'
@@ -124,11 +120,6 @@
         if 'all' in result:
             return True
 
-        # For migration purposes, temporarily return true if the config hasn't
-        # been set but _enabled is true.
-        if len(result) == 0 and _enabled:
-            return True
-
         # Temporary hack for next check
         newconfig = repo.ui.config('experimental', 'evolution.createmarkers')
         if newconfig:
--- a/mercurial/repair.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/repair.py	Thu May 09 18:37:37 2019 -0400
@@ -279,7 +279,9 @@
         if rev in tostrip:
             updatebm.append(m)
     newbmtarget = None
-    if updatebm: # don't compute anything is there is no bookmark to move anyway
+    # If we need to move bookmarks, compute bookmark
+    # targets. Otherwise we can skip doing this logic.
+    if updatebm:
         # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)),
         # but is much faster
         newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
@@ -364,8 +366,9 @@
     striptrees(repo, tr, striprev, files)
 
 def striptrees(repo, tr, striprev, files):
-    if 'treemanifest' in repo.requirements: # safe but unnecessary
-                                            # otherwise
+    if 'treemanifest' in repo.requirements:
+        # This logic is safe if treemanifest isn't enabled, but also
+        # pointless, so we skip it if treemanifest isn't enabled.
         for unencoded, encoded, size in repo.store.datafiles():
             if (unencoded.startswith('meta/') and
                 unencoded.endswith('00manifest.i')):
@@ -416,7 +419,9 @@
 
         progress.complete()
 
-        if 'treemanifest' in repo.requirements: # safe but unnecessary otherwise
+        if 'treemanifest' in repo.requirements:
+            # This logic is safe if treemanifest isn't enabled, but also
+            # pointless, so we skip it if treemanifest isn't enabled.
             for dir in util.dirs(seenfiles):
                 i = 'meta/%s/00manifest.i' % dir
                 d = 'meta/%s/00manifest.d' % dir
--- a/mercurial/repository.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/repository.py	Thu May 09 18:37:37 2019 -0400
@@ -291,6 +291,10 @@
 class ipeerrequests(interfaceutil.Interface):
     """Interface for executing commands on a peer."""
 
+    limitedarguments = interfaceutil.Attribute(
+        """True if the peer cannot receive large argument value for commands."""
+    )
+
     def commandexecutor():
         """A context manager that resolves to an ipeercommandexecutor.
 
@@ -329,6 +333,8 @@
 class peer(object):
     """Base class for peer repositories."""
 
+    limitedarguments = False
+
     def capable(self, name):
         caps = self.capabilities()
         if name in caps:
--- a/mercurial/repoview.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/repoview.py	Thu May 09 18:37:37 2019 -0400
@@ -17,6 +17,10 @@
     phases,
     pycompat,
     tags as tagsmod,
+    util,
+)
+from .utils import (
+    repoviewutil,
 )
 
 def hideablerevs(repo):
@@ -154,6 +158,35 @@
                'immutable':  computemutable,
                'base':  computeimpactable}
 
+_basefiltername = list(filtertable)
+
+def extrafilter(ui):
+    """initialize extra filter and return its id
+
+    If extra filtering is configured, we make sure the associated filtered view
+    are declared and return the associated id.
+    """
+    frevs = ui.config('experimental', 'extra-filter-revs')
+    if frevs is None:
+        return None
+
+    fid = pycompat.sysbytes(util.DIGESTS['sha1'](frevs).hexdigest())[:12]
+
+    combine = lambda fname: fname + '%' + fid
+
+    subsettable = repoviewutil.subsettable
+
+    if combine('base') not in filtertable:
+        for name in _basefiltername:
+            def extrafilteredrevs(repo, *args, **kwargs):
+                baserevs = filtertable[name](repo, *args, **kwargs)
+                extrarevs = frozenset(repo.revs(frevs))
+                return baserevs | extrarevs
+            filtertable[combine(name)] = extrafilteredrevs
+            if name in subsettable:
+                subsettable[combine(name)] = combine(subsettable[name])
+    return fid
+
 def filterrevs(repo, filtername, visibilityexceptions=None):
     """returns set of filtered revision for this filter name
 
--- a/mercurial/revset.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/revset.py	Thu May 09 18:37:37 2019 -0400
@@ -52,6 +52,9 @@
 spanset = smartset.spanset
 fullreposet = smartset.fullreposet
 
+# revisions not included in all(), but populated if specified
+_virtualrevs = (node.nullrev, node.wdirrev)
+
 # Constants for ordering requirement, used in getset():
 #
 # If 'define', any nested functions and operations MAY change the ordering of
@@ -120,8 +123,7 @@
     if not x:
         raise error.ParseError(_("empty string is not a valid revision"))
     x = scmutil.intrev(scmutil.revsymbol(repo, x))
-    if (x in subset
-        or x == node.nullrev and isinstance(subset, fullreposet)):
+    if x in subset or x in _virtualrevs and isinstance(subset, fullreposet):
         return baseset([x])
     return baseset()
 
@@ -1847,7 +1849,7 @@
     except (TypeError, ValueError):
         # i18n: "rev" is a keyword
         raise error.ParseError(_("rev expects a number"))
-    if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
+    if l not in repo.changelog and l not in _virtualrevs:
         return baseset()
     return subset & baseset([l])
 
@@ -2262,7 +2264,7 @@
             if r in seen:
                 continue
             if (r in subset
-                or r == node.nullrev and isinstance(subset, fullreposet)):
+                or r in _virtualrevs and isinstance(subset, fullreposet)):
                 ls.append(r)
             seen.add(r)
     return baseset(ls)
--- a/mercurial/setdiscovery.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/setdiscovery.py	Thu May 09 18:37:37 2019 -0400
@@ -119,13 +119,13 @@
         self._childrenmap = None
 
     def addcommons(self, commons):
-        """registrer nodes known as common"""
+        """register nodes known as common"""
         self._common.addbases(commons)
         if self._undecided is not None:
             self._common.removeancestorsfrom(self._undecided)
 
     def addmissings(self, missings):
-        """registrer some nodes as missing"""
+        """register some nodes as missing"""
         newmissing = self._repo.revs('%ld::%ld', missings, self.undecided)
         if newmissing:
             self.missing.update(newmissing)
@@ -275,9 +275,63 @@
     # early exit if we know all the specified remote heads already
     ui.debug("query 1; heads\n")
     roundtrips += 1
-    sample = _limitsample(ownheads, initialsamplesize)
-    # indices between sample and externalized version must match
-    sample = list(sample)
+    # We also ask remote about all the local heads. That set can be arbitrarily
+    # large, so we used to limit it size to `initialsamplesize`. We no longer
+    # do as it proved counter productive. The skipped heads could lead to a
+    # large "undecided" set, slower to be clarified than if we asked the
+    # question for all heads right away.
+    #
+    # We are already fetching all server heads using the `heads` commands,
+    # sending a equivalent number of heads the other way should not have a
+    # significant impact.  In addition, it is very likely that we are going to
+    # have to issue "known" request for an equivalent amount of revisions in
+    # order to decide if theses heads are common or missing.
+    #
+    # find a detailled analysis below.
+    #
+    # Case A: local and server both has few heads
+    #
+    #     Ownheads is below initialsamplesize, limit would not have any effect.
+    #
+    # Case B: local has few heads and server has many
+    #
+    #     Ownheads is below initialsamplesize, limit would not have any effect.
+    #
+    # Case C: local and server both has many heads
+    #
+    #     We now transfert some more data, but not significantly more than is
+    #     already transfered to carry the server heads.
+    #
+    # Case D: local has many heads, server has few
+    #
+    #   D.1 local heads are mostly known remotely
+    #
+    #     All the known head will have be part of a `known` request at some
+    #     point for the discovery to finish. Sending them all earlier is
+    #     actually helping.
+    #
+    #     (This case is fairly unlikely, it requires the numerous heads to all
+    #     be merged server side in only a few heads)
+    #
+    #   D.2 local heads are mostly missing remotely
+    #
+    #     To determine that the heads are missing, we'll have to issue `known`
+    #     request for them or one of their ancestors. This amount of `known`
+    #     request will likely be in the same order of magnitude than the amount
+    #     of local heads.
+    #
+    #     The only case where we can be more efficient using `known` request on
+    #     ancestors are case were all the "missing" local heads are based on a
+    #     few changeset, also "missing".  This means we would have a "complex"
+    #     graph (with many heads) attached to, but very independant to a the
+    #     "simple" graph on the server. This is a fairly usual case and have
+    #     not been met in the wild so far.
+    if remote.limitedarguments:
+        sample = _limitsample(ownheads, initialsamplesize)
+        # indices between sample and externalized version must match
+        sample = list(sample)
+    else:
+        sample = ownheads
 
     with remote.commandexecutor() as e:
         fheads = e.callcommand('heads', {})
--- a/mercurial/statichttprepo.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/statichttprepo.py	Thu May 09 18:37:37 2019 -0400
@@ -155,6 +155,7 @@
 
         self.names = namespaces.namespaces()
         self.filtername = None
+        self._extrafilterid = None
 
         try:
             requirements = set(self.vfs.read(b'requires').splitlines())
--- a/mercurial/tags.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/tags.py	Thu May 09 18:37:37 2019 -0400
@@ -18,6 +18,7 @@
     bin,
     hex,
     nullid,
+    nullrev,
     short,
 )
 from .i18n import _
@@ -89,7 +90,7 @@
     unfi = repo.unfiltered()
     tonode = unfi.changelog.node
     nodes = [tonode(r) for r in revs]
-    fnodes = _getfnodes(ui, repo, nodes[::-1]) # reversed help the cache
+    fnodes = _getfnodes(ui, repo, nodes)
     fnodes = _filterfnodes(fnodes, nodes)
     return fnodes
 
@@ -457,7 +458,8 @@
     # This is the most expensive part of finding tags, so performance
     # depends primarily on the size of newheads.  Worst case: no cache
     # file, so newheads == repoheads.
-    cachefnode = _getfnodes(ui, repo, repoheads)
+    # Reversed order helps the cache ('repoheads' is in descending order)
+    cachefnode = _getfnodes(ui, repo, reversed(repoheads))
 
     # Caller has to iterate over all heads, but can use the filenodes in
     # cachefnode to get to each .hgtags revision quickly.
@@ -472,7 +474,7 @@
     starttime = util.timer()
     fnodescache = hgtagsfnodescache(repo.unfiltered())
     cachefnode = {}
-    for node in reversed(nodes):
+    for node in nodes:
         fnode = fnodescache.getfnode(node)
         if fnode != nullid:
             cachefnode[node] = fnode
@@ -691,6 +693,9 @@
         If an .hgtags does not exist at the specified revision, nullid is
         returned.
         """
+        if node == nullid:
+            return nullid
+
         ctx = self._repo[node]
         rev = ctx.rev()
 
@@ -715,12 +720,33 @@
         if not computemissing:
             return None
 
-        # Populate missing entry.
-        try:
-            fnode = ctx.filenode('.hgtags')
-        except error.LookupError:
-            # No .hgtags file on this revision.
-            fnode = nullid
+        fnode = None
+        cl = self._repo.changelog
+        p1rev, p2rev = cl._uncheckedparentrevs(rev)
+        p1node = cl.node(p1rev)
+        p1fnode = self.getfnode(p1node, computemissing=False)
+        if p2rev != nullrev:
+            # There is some no-merge changeset where p1 is null and p2 is set
+            # Processing them as merge is just slower, but still gives a good
+            # result.
+            p2node = cl.node(p1rev)
+            p2fnode = self.getfnode(p2node, computemissing=False)
+            if p1fnode != p2fnode:
+                # we cannot rely on readfast because we don't know against what
+                # parent the readfast delta is computed
+                p1fnode = None
+        if p1fnode is not None:
+            mctx = ctx.manifestctx()
+            fnode = mctx.readfast().get('.hgtags')
+            if fnode is None:
+                fnode = p1fnode
+        if fnode is None:
+            # Populate missing entry.
+            try:
+                fnode = ctx.filenode('.hgtags')
+            except error.LookupError:
+                # No .hgtags file on this revision.
+                fnode = nullid
 
         self._writeentry(offset, properprefix, fnode)
         return fnode
--- a/mercurial/unionrepo.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/unionrepo.py	Thu May 09 18:37:37 2019 -0400
@@ -128,9 +128,10 @@
 
     def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
         raise NotImplementedError
-    def addgroup(self, deltas, transaction, addrevisioncb=None):
+    def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None,
+                 maybemissingparents=False):
         raise NotImplementedError
-    def strip(self, rev, minlink):
+    def strip(self, minlink, transaction):
         raise NotImplementedError
     def checksize(self):
         raise NotImplementedError
--- a/mercurial/verify.py	Wed May 08 16:09:50 2019 -0400
+++ b/mercurial/verify.py	Thu May 09 18:37:37 2019 -0400
@@ -22,9 +22,13 @@
     util,
 )
 
-def verify(repo):
+VERIFY_DEFAULT = 0
+VERIFY_FULL = 1
+
+def verify(repo, level=None):
     with repo.lock():
-        return verifier(repo).verify()
+        v = verifier(repo, level)
+        return v.verify()
 
 def _normpath(f):
     # under hg < 2.4, convert didn't sanitize paths properly, so a
@@ -34,10 +38,13 @@
     return f
 
 class verifier(object):
-    def __init__(self, repo):
+    def __init__(self, repo, level=None):
         self.repo = repo.unfiltered()
         self.ui = repo.ui
         self.match = repo.narrowmatch()
+        if level is None:
+            level = VERIFY_DEFAULT
+        self._level = level
         self.badrevs = set()
         self.errors = 0
         self.warnings = 0
@@ -330,6 +337,16 @@
                         filenodes.setdefault(fullpath, {}).setdefault(fn, lr)
             except Exception as inst:
                 self._exc(lr, _("reading delta %s") % short(n), inst, label)
+            if self._level >= VERIFY_FULL:
+                try:
+                    # Various issues can affect manifest. So we read each full
+                    # text from storage. This triggers the checks from the core
+                    # code (eg: hash verification, filename are ordered, etc.)
+                    mfdelta = mfl.get(dir, n).read()
+                except Exception as inst:
+                    self._exc(lr, _("reading full manifest %s") % short(n),
+                              inst, label)
+
         if not dir:
             progress.complete()
 
--- a/rust/hg-core/src/dagops.rs	Wed May 08 16:09:50 2019 -0400
+++ b/rust/hg-core/src/dagops.rs	Thu May 09 18:37:37 2019 -0400
@@ -13,7 +13,8 @@
 //! - Similarly *relative roots* of a collection of `Revision`, we mean
 //!   those whose parents, if any, don't belong to the collection.
 use super::{Graph, GraphError, Revision, NULL_REVISION};
-use std::collections::HashSet;
+use crate::ancestors::AncestorsIterator;
+use std::collections::{BTreeSet, HashSet};
 
 fn remove_parents(
     graph: &impl Graph,
@@ -80,6 +81,92 @@
     Ok(())
 }
 
+/// Roots of `revs`, passed as a `HashSet`
+///
+/// They are returned in arbitrary order
+pub fn roots<G: Graph>(
+    graph: &G,
+    revs: &HashSet<Revision>,
+) -> Result<Vec<Revision>, GraphError> {
+    let mut roots: Vec<Revision> = Vec::new();
+    for rev in revs {
+        if graph
+            .parents(*rev)?
+            .iter()
+            .filter(|p| **p != NULL_REVISION)
+            .all(|p| !revs.contains(p))
+        {
+            roots.push(*rev);
+        }
+    }
+    Ok(roots)
+}
+
+/// Compute the topological range between two collections of revisions
+///
+/// This is equivalent to the revset `<roots>::<heads>`.
+///
+/// Currently, the given `Graph` has to implement `Clone`, which means
+/// actually cloning just a reference-counted Python pointer if
+/// it's passed over through `rust-cpython`. This is due to the internal
+/// use of `AncestorsIterator`
+///
+/// # Algorithmic details
+///
+/// This is a two-pass swipe inspired from what `reachableroots2` from
+/// `mercurial.cext.parsers` does to obtain the same results.
+///
+/// - first, we climb up the DAG from `heads` in topological order, keeping
+///   them in the vector `heads_ancestors` vector, and adding any element of
+///   `roots` we find among them to the resulting range.
+/// - Then, we iterate on that recorded vector so that a revision is always
+///   emitted after its parents and add all revisions whose parents are already
+///   in the range to the results.
+///
+/// # Performance notes
+///
+/// The main difference with the C implementation is that
+/// the latter uses a flat array with bit flags, instead of complex structures
+/// like `HashSet`, making it faster in most scenarios. In theory, it's
+/// possible that the present implementation could be more memory efficient
+/// for very large repositories with many branches.
+pub fn range(
+    graph: &(impl Graph + Clone),
+    roots: impl IntoIterator<Item = Revision>,
+    heads: impl IntoIterator<Item = Revision>,
+) -> Result<BTreeSet<Revision>, GraphError> {
+    let mut range = BTreeSet::new();
+    let roots: HashSet<Revision> = roots.into_iter().collect();
+    let min_root: Revision = match roots.iter().cloned().min() {
+        None => {
+            return Ok(range);
+        }
+        Some(r) => r,
+    };
+
+    // Internally, AncestorsIterator currently maintains a `HashSet`
+    // of all seen revision, which is also what we record, albeit in an ordered
+    // way. There's room for improvement on this duplication.
+    let ait = AncestorsIterator::new(graph.clone(), heads, min_root, true)?;
+    let mut heads_ancestors: Vec<Revision> = Vec::new();
+    for revres in ait {
+        let rev = revres?;
+        if roots.contains(&rev) {
+            range.insert(rev);
+        }
+        heads_ancestors.push(rev);
+    }
+
+    for rev in heads_ancestors.into_iter().rev() {
+        for parent in graph.parents(rev)?.iter() {
+            if *parent != NULL_REVISION && range.contains(parent) {
+                range.insert(rev);
+            }
+        }
+    }
+    Ok(range)
+}
+
 #[cfg(test)]
 mod tests {
 
@@ -137,4 +224,53 @@
         Ok(())
     }
 
+    /// Apply `roots()` and sort the result for easier comparison
+    fn roots_sorted(
+        graph: &impl Graph,
+        revs: &[Revision],
+    ) -> Result<Vec<Revision>, GraphError> {
+        let mut as_vec = roots(graph, &revs.iter().cloned().collect())?;
+        as_vec.sort();
+        Ok(as_vec)
+    }
+
+    #[test]
+    fn test_roots() -> Result<(), GraphError> {
+        assert_eq!(roots_sorted(&SampleGraph, &[4, 5, 6])?, vec![4]);
+        assert_eq!(
+            roots_sorted(&SampleGraph, &[4, 1, 6, 12, 0])?,
+            vec![0, 4, 12]
+        );
+        assert_eq!(
+            roots_sorted(&SampleGraph, &[1, 2, 3, 4, 5, 6, 7, 8, 9])?,
+            vec![1, 8]
+        );
+        Ok(())
+    }
+
+    /// Apply `range()` and convert the result into a Vec for easier comparison
+    fn range_vec(
+        graph: impl Graph + Clone,
+        roots: &[Revision],
+        heads: &[Revision],
+    ) -> Result<Vec<Revision>, GraphError> {
+        range(&graph, roots.iter().cloned(), heads.iter().cloned())
+            .map(|bs| bs.into_iter().collect())
+    }
+
+    #[test]
+    fn test_range() -> Result<(), GraphError> {
+        assert_eq!(range_vec(SampleGraph, &[0], &[4])?, vec![0, 1, 2, 4]);
+        assert_eq!(range_vec(SampleGraph, &[0], &[8])?, vec![]);
+        assert_eq!(
+            range_vec(SampleGraph, &[5, 6], &[10, 11, 13])?,
+            vec![5, 10]
+        );
+        assert_eq!(
+            range_vec(SampleGraph, &[5, 6], &[10, 12])?,
+            vec![5, 6, 9, 10, 12]
+        );
+        Ok(())
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-core/src/discovery.rs	Thu May 09 18:37:37 2019 -0400
@@ -0,0 +1,209 @@
+// discovery.rs
+//
+// Copyright 2019 Georges Racinet <georges.racinet@octobus.net>
+//
+// This software may be used and distributed according to the terms of the
+// GNU General Public License version 2 or any later version.
+
+//! Discovery operations
+//!
+//! This is a Rust counterpart to the `partialdiscovery` class of
+//! `mercurial.setdiscovery`
+
+use super::{Graph, GraphError, Revision};
+use crate::ancestors::MissingAncestors;
+use crate::dagops;
+use std::collections::HashSet;
+
+pub struct PartialDiscovery<G: Graph + Clone> {
+    target_heads: Option<Vec<Revision>>,
+    graph: G, // plays the role of self._repo
+    common: MissingAncestors<G>,
+    undecided: Option<HashSet<Revision>>,
+    missing: HashSet<Revision>,
+}
+
+pub struct DiscoveryStats {
+    pub undecided: Option<usize>,
+}
+
+impl<G: Graph + Clone> PartialDiscovery<G> {
+    /// Create a PartialDiscovery object, with the intent
+    /// of comparing our `::<target_heads>` revset to the contents of another
+    /// repo.
+    ///
+    /// For now `target_heads` is passed as a vector, and will be used
+    /// at the first call to `ensure_undecided()`.
+    ///
+    /// If we want to make the signature more flexible,
+    /// we'll have to make it a type argument of `PartialDiscovery` or a trait
+    /// object since we'll keep it in the meanwhile
+    pub fn new(graph: G, target_heads: Vec<Revision>) -> Self {
+        PartialDiscovery {
+            undecided: None,
+            target_heads: Some(target_heads),
+            graph: graph.clone(),
+            common: MissingAncestors::new(graph, vec![]),
+            missing: HashSet::new(),
+        }
+    }
+
+    /// Register revisions known as being common
+    pub fn add_common_revisions(
+        &mut self,
+        common: impl IntoIterator<Item = Revision>,
+    ) -> Result<(), GraphError> {
+        self.common.add_bases(common);
+        if let Some(ref mut undecided) = self.undecided {
+            self.common.remove_ancestors_from(undecided)?;
+        }
+        Ok(())
+    }
+
+    /// Register revisions known as being missing
+    pub fn add_missing_revisions(
+        &mut self,
+        missing: impl IntoIterator<Item = Revision>,
+    ) -> Result<(), GraphError> {
+        self.ensure_undecided()?;
+        let range = dagops::range(
+            &self.graph,
+            missing,
+            self.undecided.as_ref().unwrap().iter().cloned(),
+        )?;
+        let undecided_mut = self.undecided.as_mut().unwrap();
+        for missrev in range {
+            self.missing.insert(missrev);
+            undecided_mut.remove(&missrev);
+        }
+        Ok(())
+    }
+
+    /// Do we have any information about the peer?
+    pub fn has_info(&self) -> bool {
+        self.common.has_bases()
+    }
+
+    /// Did we acquire full knowledge of our Revisions that the peer has?
+    pub fn is_complete(&self) -> bool {
+        self.undecided.as_ref().map_or(false, |s| s.is_empty())
+    }
+
+    /// Return the heads of the currently known common set of revisions.
+    ///
+    /// If the discovery process is not complete (see `is_complete()`), the
+    /// caller must be aware that this is an intermediate state.
+    ///
+    /// On the other hand, if it is complete, then this is currently
+    /// the only way to retrieve the end results of the discovery process.
+    ///
+    /// We may introduce in the future an `into_common_heads` call that
+    /// would be more appropriate for normal Rust callers, dropping `self`
+    /// if it is complete.
+    pub fn common_heads(&self) -> Result<HashSet<Revision>, GraphError> {
+        self.common.bases_heads()
+    }
+
+    /// Force first computation of `self.undecided`
+    ///
+    /// After this, `self.undecided.as_ref()` and `.as_mut()` can be
+    /// unwrapped to get workable immutable or mutable references without
+    /// any panic.
+    ///
+    /// This is an imperative call instead of an access with added lazyness
+    /// to reduce easily the scope of mutable borrow for the caller,
+    /// compared to undecided(&'a mut self) -> &'a… that would keep it
+    /// as long as the resulting immutable one.
+    fn ensure_undecided(&mut self) -> Result<(), GraphError> {
+        if self.undecided.is_some() {
+            return Ok(());
+        }
+        let tgt = self.target_heads.take().unwrap();
+        self.undecided =
+            Some(self.common.missing_ancestors(tgt)?.into_iter().collect());
+        Ok(())
+    }
+
+    /// Provide statistics about the current state of the discovery process
+    pub fn stats(&self) -> DiscoveryStats {
+        DiscoveryStats {
+            undecided: self.undecided.as_ref().map(|s| s.len()),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::testing::SampleGraph;
+
+    /// A PartialDiscovery as for pushing all the heads of `SampleGraph`
+    fn full_disco() -> PartialDiscovery<SampleGraph> {
+        PartialDiscovery::new(SampleGraph, vec![10, 11, 12, 13])
+    }
+
+    fn sorted_undecided(
+        disco: &PartialDiscovery<SampleGraph>,
+    ) -> Vec<Revision> {
+        let mut as_vec: Vec<Revision> =
+            disco.undecided.as_ref().unwrap().iter().cloned().collect();
+        as_vec.sort();
+        as_vec
+    }
+
+    fn sorted_missing(disco: &PartialDiscovery<SampleGraph>) -> Vec<Revision> {
+        let mut as_vec: Vec<Revision> =
+            disco.missing.iter().cloned().collect();
+        as_vec.sort();
+        as_vec
+    }
+
+    fn sorted_common_heads(
+        disco: &PartialDiscovery<SampleGraph>,
+    ) -> Result<Vec<Revision>, GraphError> {
+        let mut as_vec: Vec<Revision> =
+            disco.common_heads()?.iter().cloned().collect();
+        as_vec.sort();
+        Ok(as_vec)
+    }
+
+    #[test]
+    fn test_add_common_get_undecided() -> Result<(), GraphError> {
+        let mut disco = full_disco();
+        assert_eq!(disco.undecided, None);
+        assert!(!disco.has_info());
+        assert_eq!(disco.stats().undecided, None);
+
+        disco.add_common_revisions(vec![11, 12])?;
+        assert!(disco.has_info());
+        assert!(!disco.is_complete());
+        assert!(disco.missing.is_empty());
+
+        // add_common_revisions did not trigger a premature computation
+        // of `undecided`, let's check that and ask for them
+        assert_eq!(disco.undecided, None);
+        disco.ensure_undecided()?;
+        assert_eq!(sorted_undecided(&disco), vec![5, 8, 10, 13]);
+        assert_eq!(disco.stats().undecided, Some(4));
+        Ok(())
+    }
+
+    /// in this test, we pretend that our peer misses exactly (8+10)::
+    /// and we're comparing all our repo to it (as in a bare push)
+    #[test]
+    fn test_discovery() -> Result<(), GraphError> {
+        let mut disco = full_disco();
+        disco.add_common_revisions(vec![11, 12])?;
+        disco.add_missing_revisions(vec![8, 10])?;
+        assert_eq!(sorted_undecided(&disco), vec![5]);
+        assert_eq!(sorted_missing(&disco), vec![8, 10, 13]);
+        assert!(!disco.is_complete());
+
+        disco.add_common_revisions(vec![5])?;
+        assert_eq!(sorted_undecided(&disco), vec![]);
+        assert_eq!(sorted_missing(&disco), vec![8, 10, 13]);
+        assert!(disco.is_complete());
+        assert_eq!(sorted_common_heads(&disco)?, vec![5, 11, 12]);
+        Ok(())
+    }
+}
--- a/rust/hg-core/src/lib.rs	Wed May 08 16:09:50 2019 -0400
+++ b/rust/hg-core/src/lib.rs	Thu May 09 18:37:37 2019 -0400
@@ -6,6 +6,7 @@
 pub mod dagops;
 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
 pub mod testing;  // unconditionally built, for use from integration tests
+pub mod discovery;
 
 /// Mercurial revision numbers
 ///
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-cpython/src/discovery.rs	Thu May 09 18:37:37 2019 -0400
@@ -0,0 +1,125 @@
+// discovery.rs
+//
+// Copyright 2018 Georges Racinet <gracinet@anybox.fr>
+//
+// This software may be used and distributed according to the terms of the
+// GNU General Public License version 2 or any later version.
+
+//! Bindings for the `hg::discovery` module provided by the
+//! `hg-core` crate. From Python, this will be seen as `rustext.discovery`
+//!
+//! # Classes visible from Python:
+//! - [`PartialDiscover`] is the Rust implementation of
+//!   `mercurial.setdiscovery.partialdiscovery`.
+
+use crate::conversion::{py_set, rev_pyiter_collect};
+use cindex::Index;
+use cpython::{
+    ObjectProtocol, PyDict, PyModule, PyObject, PyResult, Python, ToPyObject,
+};
+use exceptions::GraphError;
+use hg::discovery::PartialDiscovery as CorePartialDiscovery;
+use hg::Revision;
+
+use std::cell::RefCell;
+
+py_class!(pub class PartialDiscovery |py| {
+    data inner: RefCell<Box<CorePartialDiscovery<Index>>>;
+
+    def __new__(
+        _cls,
+        index: PyObject,
+        targetheads: PyObject
+    ) -> PyResult<PartialDiscovery> {
+        Self::create_instance(
+            py,
+            RefCell::new(Box::new(CorePartialDiscovery::new(
+                Index::new(py, index)?,
+                rev_pyiter_collect(py, &targetheads)?,
+            )))
+        )
+    }
+
+    def addcommons(&self, commons: PyObject) -> PyResult<PyObject> {
+        let mut inner = self.inner(py).borrow_mut();
+        let commons_vec: Vec<Revision> = rev_pyiter_collect(py, &commons)?;
+        inner.add_common_revisions(commons_vec)
+            .map_err(|e| GraphError::pynew(py, e))?;
+        Ok(py.None())
+    }
+
+    def addmissings(&self, missings: PyObject) -> PyResult<PyObject> {
+        let mut inner = self.inner(py).borrow_mut();
+        let missings_vec: Vec<Revision> = rev_pyiter_collect(py, &missings)?;
+        inner.add_missing_revisions(missings_vec)
+            .map_err(|e| GraphError::pynew(py, e))?;
+        Ok(py.None())
+    }
+
+    def addinfo(&self, sample: PyObject) -> PyResult<PyObject> {
+        let mut missing: Vec<Revision> = Vec::new();
+        let mut common: Vec<Revision> = Vec::new();
+        for info in sample.iter(py)? { // info is a pair (Revision, bool)
+            let mut revknown = info?.iter(py)?;
+            let rev: Revision = revknown.next().unwrap()?.extract(py)?;
+            let known: bool = revknown.next().unwrap()?.extract(py)?;
+            if known {
+                common.push(rev);
+            } else {
+                missing.push(rev);
+            }
+        }
+        let mut inner = self.inner(py).borrow_mut();
+        inner.add_common_revisions(common)
+            .map_err(|e| GraphError::pynew(py, e))?;
+        inner.add_missing_revisions(missing)
+            .map_err(|e| GraphError::pynew(py, e))?;
+        Ok(py.None())
+    }
+
+    def hasinfo(&self) -> PyResult<bool> {
+        Ok(self.inner(py).borrow().has_info())
+    }
+
+    def iscomplete(&self) -> PyResult<bool> {
+        Ok(self.inner(py).borrow().is_complete())
+    }
+
+    def stats(&self) -> PyResult<PyDict> {
+        let stats = self.inner(py).borrow().stats();
+        let as_dict: PyDict = PyDict::new(py);
+        as_dict.set_item(py, "undecided",
+                         stats.undecided.map(|l| l.to_py_object(py))
+                              .unwrap_or_else(|| py.None()))?;
+        Ok(as_dict)
+    }
+
+    def commonheads(&self) -> PyResult<PyObject> {
+        py_set(
+            py,
+            &self.inner(py).borrow().common_heads()
+                .map_err(|e| GraphError::pynew(py, e))?
+        )
+    }
+});
+
+/// Create the module, with __package__ given from parent
+pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
+    let dotted_name = &format!("{}.discovery", package);
+    let m = PyModule::new(py, dotted_name)?;
+    m.add(py, "__package__", package)?;
+    m.add(
+        py,
+        "__doc__",
+        "Discovery of common node sets - Rust implementation",
+    )?;
+    m.add_class::<PartialDiscovery>(py)?;
+
+    let sys = PyModule::import(py, "sys")?;
+    let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
+    sys_modules.set_item(py, dotted_name, &m)?;
+    // Example C code (see pyexpat.c and import.c) will "give away the
+    // reference", but we won't because it will be consumed once the
+    // Rust PyObject is dropped.
+    Ok(m)
+}
--- a/rust/hg-cpython/src/lib.rs	Wed May 08 16:09:50 2019 -0400
+++ b/rust/hg-cpython/src/lib.rs	Thu May 09 18:37:37 2019 -0400
@@ -28,6 +28,7 @@
 mod cindex;
 mod conversion;
 pub mod dagops;
+pub mod discovery;
 pub mod exceptions;
 
 py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
@@ -40,6 +41,7 @@
     let dotted_name: String = m.get(py, "__name__")?.extract(py)?;
     m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?;
     m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?;
+    m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?;
     m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
     Ok(())
 });
--- a/tests/test-absorb.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-absorb.t	Thu May 09 18:37:37 2019 -0400
@@ -176,7 +176,7 @@
      b2                        2:c9b20c925790
    * ba                        2:c9b20c925790
 
-Non-mofified files are ignored:
+Non-modified files are ignored:
 
   $ touch b
   $ hg commit -A b -m b
@@ -225,10 +225,15 @@
   2: 4d
   2: insert aftert 4d
 
+  $ hg co -qC 1
+  $ sedi 's/Insert/insert/' a
+  $ hg absorb --apply-changes
+  abort: no mutable changeset to change
+  [255]
+
 Make working copy clean:
 
-  $ hg revert -q -C a b
-  $ hg forget c
+  $ hg co -qC ba
   $ rm c
   $ hg status
 
@@ -261,7 +266,7 @@
   $ echo 2 >> m1
   $ echo 2 >> m2
   $ hg absorb --apply-changes
-  abort: no mutable changeset to change
+  abort: cannot absorb into a merge
   [255]
   $ hg revert -q -C m1 m2
 
--- a/tests/test-annotate.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-annotate.t	Thu May 09 18:37:37 2019 -0400
@@ -1,4 +1,7 @@
-  $ HGMERGE=true; export HGMERGE
+  $ cat >> "$HGRCPATH" << EOF
+  > [ui]
+  > merge = :merge3
+  > EOF
 
 init
 
@@ -210,8 +213,34 @@
   created new head
   $ hg merge
   merging b
-  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
+  warning: conflicts while merging b! (edit, 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 merge --abort' to abandon
+  [1]
+  $ cat b
+  a
+  a
+  a
+  <<<<<<< working copy: 5fbdc1152d97 - test: b2.1
+  b4
+  c
+  b5
+  ||||||| base
+  =======
+  b4
+  b5
+  b6
+  >>>>>>> merge rev:    37ec9f5c3d1f - test: b2
+  $ cat <<EOF > b
+  > a
+  > a
+  > a
+  > b4
+  > c
+  > b5
+  > EOF
+  $ hg resolve --mark -q
+  $ rm b.orig
   $ hg ci -mmergeb -d '3 0'
 
 annotate after merge
@@ -244,15 +273,31 @@
   > EOF
   $ hg ci -mc -d '3 0'
   created new head
+Work around the pure version not resolving the conflict like native code
+#if pure
+  $ hg merge
+  merging b
+  warning: conflicts while merging b! (edit, 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 merge --abort' to abandon
+  [1]
+  $ cat <<EOF > b
+  > a
+  > z
+  > a
+  > b4
+  > c
+  > b5
+  > EOF
+  $ hg resolve -m b
+  (no more unresolved files)
+  $ rm b.orig
+#else
   $ hg merge
   merging b
   0 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
-  $ cat <<EOF >> b
-  > b4
-  > c
-  > b5
-  > EOF
+#endif
   $ echo d >> b
   $ hg ci -mmerge2 -d '4 0'
 
@@ -695,8 +740,41 @@
   27: baz:3+->3-
   $ hg merge 25
   merging baz and qux to qux
-  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
+  warning: conflicts while merging qux! (edit, 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 merge --abort' to abandon
+  [1]
+  $ cat qux
+  0
+  0
+  1 baz:1
+  2 baz:2
+  <<<<<<< working copy: 863de62655ef - test: baz:3+->3-
+  3- baz:3
+  4 baz:4
+  ||||||| base
+  3+ baz:3
+  4 baz:4
+  =======
+  3+ baz:3
+  4+ baz:4
+  >>>>>>> merge rev:    cb8df70ae185 - test: qux:4->4+
+  5
+  6
+  7
+  $ cat > qux <<EOF
+  > 0
+  > 0
+  > 1 baz:1
+  > 2 baz:2
+  > 3- baz:3
+  > 4 baz:4
+  > 5
+  > 6
+  > 7
+  > EOF
+  $ hg resolve --mark -q
+  $ rm qux.orig
   $ hg ci -m merge
   $ hg log -T '{rev}: {desc}\n' -r 'followlines(qux, 5:7)'
   16: baz:0
@@ -709,8 +787,40 @@
   $ hg up 25 --quiet
   $ hg merge 27
   merging qux and baz to qux
-  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
+  warning: conflicts while merging qux! (edit, 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 merge --abort' to abandon
+  [1]
+  $ cat qux
+  0
+  0
+  1 baz:1
+  2 baz:2
+  <<<<<<< working copy: cb8df70ae185 - test: qux:4->4+
+  3+ baz:3
+  4+ baz:4
+  ||||||| base
+  3+ baz:3
+  4 baz:4
+  =======
+  3- baz:3
+  4 baz:4
+  >>>>>>> merge rev:    863de62655ef - test: baz:3+->3-
+  5
+  6
+  7
+  $ cat > qux <<EOF
+  > 0
+  > 0
+  > 1 baz:1
+  > 2 baz:2
+  > 3+ baz:3
+  > 4+ baz:4
+  > 5
+  > 6
+  > EOF
+  $ hg resolve --mark -q
+  $ rm qux.orig
   $ hg ci -m 'merge from other side'
   created new head
   $ hg log -T '{rev}: {desc}\n' -r 'followlines(qux, 5:7)'
@@ -1061,6 +1171,19 @@
   $ echo 3 >> a
   $ hg commit -m 3 -q
   $ hg merge 2 -q
+  warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
+  [1]
+  $ cat a
+  <<<<<<< working copy: 0a068f0261cf - test: 3
+  1
+  2
+  3
+  ||||||| base
+  1
+  2
+  =======
+  a
+  >>>>>>> merge rev:    9409851bc20a - test: a
   $ cat > a << EOF
   > b
   > 1
@@ -1069,6 +1192,7 @@
   > a
   > EOF
   $ hg resolve --mark -q
+  $ rm a.orig
   $ hg commit -m m
   $ hg annotate a
   4: b
--- a/tests/test-branches.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-branches.t	Thu May 09 18:37:37 2019 -0400
@@ -940,3 +940,35 @@
   0010: 56 46 78 69 00 00 00 01                         |VFxi....|
 
   $ cd ..
+
+Test to make sure that `--close-branch` only works on a branch head:
+--------------------------------------------------------------------
+  $ hg init closebranch
+  $ cd closebranch
+  $ for ch in a b c; do
+  > echo $ch > $ch
+  > hg add $ch
+  > hg ci -m "added "$ch
+  > done;
+
+  $ hg up -r "desc('added b')"
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
+trying to close branch from a cset which is not a branch head
+it should abort:
+  $ hg ci -m "closing branch" --close-branch
+  abort: can only close branch heads
+  [255]
+
+  $ hg up 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg log -GT "{rev}: {node|short} {desc|firstline}\n\t{branch}\n\n"
+  o  2: 155349b645be added c
+  |  	default
+  |
+  o  1: 5f6d8a4bf34a added b
+  |  	default
+  |
+  @  0: 9092f1db7931 added a
+     	default
+  
--- a/tests/test-commit-amend.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-commit-amend.t	Thu May 09 18:37:37 2019 -0400
@@ -783,6 +783,20 @@
   
   $ hg debugrename aaa
   aaa renamed from aa:37d9b5d994eab34eda9c16b195ace52c7b129980
+
+Update to p1 with 'aaa' modified. 'aaa' was renamed from 'aa' in p2. 'aa' exists
+in p1 too, but it was recorded as copied from p2.
+  $ echo modified >> aaa
+  $ hg co -m '.^' -t :merge3
+  file 'aaa' was deleted in other [destination] but was modified in local [working copy].
+  What do you want to do?
+  use (c)hanged version, (d)elete, or leave (u)nresolved? u
+  1 files updated, 0 files merged, 1 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges
+  [1]
+  $ hg co -C tip
+  2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+
   $ hg mv aaa aa
   $ hg ci --amend -m 'merge bar again (undo rename)'
   $ hg log --config diff.git=1 -pr .
--- a/tests/test-commit.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-commit.t	Thu May 09 18:37:37 2019 -0400
@@ -708,6 +708,8 @@
   abort: path contains illegal component: HG8B6C~2/hgrc
   [255]
 
+  $ cd ..
+
 # test that an unmodified commit template message aborts
 
   $ hg init unmodified_commit_template
@@ -734,6 +736,8 @@
   abort: commit message unchanged
   [255]
 
+  $ cd ..
+
 test that text below the --- >8 --- special string is ignored
 
   $ cat <<'EOF' > $TESTTMP/lowercaseline.sh
@@ -834,3 +838,42 @@
   second line
 
   $ cd ..
+
+testing commands.commit.post-status config option
+
+  $ hg init ci-post-st
+  $ cd ci-post-st
+  $ echo '[commands]' > .hg/hgrc
+  $ echo 'commit.post-status = 1' >> .hg/hgrc
+
+  $ echo 'ignored-file' > .hgignore
+  $ hg ci -qAm 0
+
+  $ echo 'c' > clean-file
+  $ echo 'a' > added-file
+  $ echo '?' > unknown-file
+  $ echo 'i' > ignored-file
+  $ hg add clean-file added-file
+  $ hg ci -m 1 clean-file
+  A added-file
+  ? unknown-file
+  $ hg st -mardu
+  A added-file
+  ? unknown-file
+
+  $ touch modified-file
+  $ hg add modified-file
+  $ hg ci -m 2 modified-file -q
+
+  $ echo 'm' > modified-file
+  $ hg ci --amend -m 'reworded' -X 're:'
+  saved backup bundle to $TESTTMP/ci-post-st/.hg/strip-backup/*-amend.hg (glob)
+  M modified-file
+  A added-file
+  ? unknown-file
+  $ hg st -mardu
+  M modified-file
+  A added-file
+  ? unknown-file
+
+  $ cd ..
--- a/tests/test-completion.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-completion.t	Thu May 09 18:37:37 2019 -0400
@@ -347,7 +347,7 @@
   tip: patch, git, style, template
   unbundle: update
   update: clean, check, merge, date, rev, tool
-  verify: 
+  verify: full
   version: template
 
   $ hg init a
--- a/tests/test-copies.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-copies.t	Thu May 09 18:37:37 2019 -0400
@@ -549,9 +549,6 @@
 
 Grafting revision 4 on top of revision 2, showing that it respect the rename:
 
-TODO: Make this work with copy info in changesets (probably by writing a
-changeset-centric version of copies.mergecopies())
-#if no-changeset
   $ hg up 2 -q
   $ hg graft -r 4 --base 3 --hidden
   grafting 4:af28412ec03c "added d, modified b" (tip)
@@ -560,15 +557,14 @@
   $ hg l -l1 -p
   @  5 added d, modified b
   |  b1
-  ~  diff -r 5a4825cc2926 -r 94a2f1a0e8e2 b1
+  ~  diff -r 5a4825cc2926 -r 94a2f1a0e8e2 b1 (no-changeset !)
+  ~  diff -r f5474f5023a8 -r ef7c02d69f3d b1 (changeset !)
      --- a/b1	Thu Jan 01 00:00:00 1970 +0000
      +++ b/b1	Thu Jan 01 00:00:00 1970 +0000
      @@ -1,1 +1,2 @@
       b
      +baba
   
-#endif
-
 Test to make sure that fullcopytracing algorithm don't fail when both the merging csets are dirty
 (a dirty cset is one who is not the descendant of merge base)
 -------------------------------------------------------------------------------------------------
--- a/tests/test-copy-move-merge.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-copy-move-merge.t	Thu May 09 18:37:37 2019 -0400
@@ -23,7 +23,6 @@
   $ hg ci -qAm "other"
 
   $ hg merge --debug
-    searching for copies back to rev 1
     unmatched files in other:
      b
      c
--- a/tests/test-copytrace-heuristics.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-copytrace-heuristics.t	Thu May 09 18:37:37 2019 -0400
@@ -16,6 +16,9 @@
   > [extensions]
   > rebase=
   > shelve=
+  > [alias]
+  > l = log -G -T 'rev: {rev}\ndesc: {desc}\n'
+  > pl = log -G -T 'rev: {rev}, phase: {phase}\ndesc: {desc}\n'
   > EOF
 
 NOTE: calling initclient() set copytrace.sourcecommitlimit=-1 as we want to
@@ -43,13 +46,13 @@
   $ echo b > dir/file.txt
   $ hg ci -qm 'mod a, mod dir/file.txt'
 
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
-  @  changeset: 557f403c0afd2a3cf15d7e2fb1f1001a8b85e081
-  |   desc: mod a, mod dir/file.txt
-  | o  changeset: 928d74bc9110681920854d845c06959f6dfc9547
-  |/    desc: mv a b, mv dir/ dir2/
-  o  changeset: 3c482b16e54596fed340d05ffaf155f156cda7ee
-      desc: initial
+  $ hg l
+  @  rev: 2
+  |  desc: mod a, mod dir/file.txt
+  | o  rev: 1
+  |/   desc: mv a b, mv dir/ dir2/
+  o  rev: 0
+     desc: initial
 
   $ hg rebase -s . -d 1
   rebasing 2:557f403c0afd "mod a, mod dir/file.txt" (tip)
@@ -76,13 +79,13 @@
   $ printf 'somecontent\nmoarcontent' > a
   $ hg ci -qm 'mode a'
 
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
-  @  changeset: d526312210b9e8f795d576a77dc643796384d86e
-  |   desc: mode a
-  | o  changeset: 46985f76c7e5e5123433527f5c8526806145650b
-  |/    desc: rm a, add b
-  o  changeset: e5b71fb099c29d9172ef4a23485aaffd497e4cc0
-      desc: initial
+  $ hg l
+  @  rev: 2
+  |  desc: mode a
+  | o  rev: 1
+  |/   desc: rm a, add b
+  o  rev: 0
+     desc: initial
 
   $ hg rebase -s . -d 1
   rebasing 2:d526312210b9 "mode a" (tip)
@@ -113,15 +116,15 @@
   $ echo b > a
   $ hg ci -qm 'mod a'
 
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
-  @  changeset: 9d5cf99c3d9f8e8b05ba55421f7f56530cfcf3bc
-  |   desc: mod a, phase: draft
-  | o  changeset: d760186dd240fc47b91eb9f0b58b0002aaeef95d
-  |/    desc: mv a b, phase: draft
-  o  changeset: 48e1b6ba639d5d7fb313fa7989eebabf99c9eb83
-  |   desc: randomcommit, phase: draft
-  o  changeset: e5b71fb099c29d9172ef4a23485aaffd497e4cc0
-      desc: initial, phase: draft
+  $ hg pl
+  @  rev: 3, phase: draft
+  |  desc: mod a
+  | o  rev: 2, phase: draft
+  |/   desc: mv a b
+  o  rev: 1, phase: draft
+  |  desc: randomcommit
+  o  rev: 0, phase: draft
+     desc: initial
 
   $ hg rebase -s . -d 2
   rebasing 3:9d5cf99c3d9f "mod a" (tip)
@@ -148,15 +151,15 @@
   $ echo b > b
   $ hg ci -qm 'mod b'
 
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
-  @  changeset: fbe97126b3969056795c462a67d93faf13e4d298
-  |   desc: mod b
-  o  changeset: d760186dd240fc47b91eb9f0b58b0002aaeef95d
-  |   desc: mv a b
-  o  changeset: 48e1b6ba639d5d7fb313fa7989eebabf99c9eb83
-  |   desc: randomcommit
-  o  changeset: e5b71fb099c29d9172ef4a23485aaffd497e4cc0
-      desc: initial
+  $ hg l
+  @  rev: 3
+  |  desc: mod b
+  o  rev: 2
+  |  desc: mv a b
+  o  rev: 1
+  |  desc: randomcommit
+  o  rev: 0
+     desc: initial
 
   $ hg rebase -s . -d 0
   rebasing 3:fbe97126b396 "mod b" (tip)
@@ -185,15 +188,15 @@
   $ echo b > dir/a
   $ hg ci -qm 'mod dir/a'
 
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
-  @  changeset: 6b2f4cece40fd320f41229f23821256ffc08efea
-  |   desc: mod dir/a
-  | o  changeset: 4494bf7efd2e0dfdd388e767fb913a8a3731e3fa
-  | |   desc: create dir2/a
-  | o  changeset: b1784dfab6ea6bfafeb11c0ac50a2981b0fe6ade
-  |/    desc: mv dir/a dir/b
-  o  changeset: 36859b8907c513a3a87ae34ba5b1e7eea8c20944
-      desc: initial
+  $ hg l
+  @  rev: 3
+  |  desc: mod dir/a
+  | o  rev: 2
+  | |  desc: create dir2/a
+  | o  rev: 1
+  |/   desc: mv dir/a dir/b
+  o  rev: 0
+     desc: initial
 
   $ hg rebase -s . -d 2
   rebasing 3:6b2f4cece40f "mod dir/a" (tip)
@@ -230,13 +233,13 @@
   $ hg ci -m 'mod a'
   created new head
 
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
-  @  changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
-  |   desc: mod a
-  | o  changeset: 8329d5c6bf479ec5ca59b9864f3f45d07213f5a4
-  |/    desc: mv a foo, add many files
-  o  changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
-      desc: initial
+  $ hg l
+  @  rev: 2
+  |  desc: mod a
+  | o  rev: 1
+  |/   desc: mv a foo, add many files
+  o  rev: 0
+     desc: initial
 
 With small limit
 
@@ -278,13 +281,13 @@
   $ hg ci -m 'del a'
   created new head
 
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
-  @  changeset: 7d61ee3b1e48577891a072024968428ba465c47b
-  |   desc: del a, phase: draft
-  | o  changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
-  |/    desc: mv a b, phase: draft
-  o  changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
-      desc: initial, phase: draft
+  $ hg pl
+  @  rev: 2, phase: draft
+  |  desc: del a
+  | o  rev: 1, phase: draft
+  |/   desc: mv a b
+  o  rev: 0, phase: draft
+     desc: initial
 
   $ hg rebase -s 1 -d 2
   rebasing 1:472e38d57782 "mv a b"
@@ -311,13 +314,13 @@
   $ hg mv -q dir/ dir2
   $ hg ci -qm 'mv dir/ dir2/'
 
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
-  @  changeset: a33d80b6e352591dfd82784e1ad6cdd86b25a239
-  |   desc: mv dir/ dir2/
-  | o  changeset: 6b2f4cece40fd320f41229f23821256ffc08efea
-  |/    desc: mod dir/a
-  o  changeset: 36859b8907c513a3a87ae34ba5b1e7eea8c20944
-      desc: initial
+  $ hg l
+  @  rev: 2
+  |  desc: mv dir/ dir2/
+  | o  rev: 1
+  |/   desc: mod dir/a
+  o  rev: 0
+     desc: initial
 
   $ hg rebase -s . -d 1
   rebasing 2:a33d80b6e352 "mv dir/ dir2/" (tip)
@@ -345,15 +348,15 @@
   $ hg ci -m 'mod a'
   created new head
 
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
-  @  changeset: d413169422167a3fa5275fc5d71f7dea9f5775f3
-  |   desc: mod a
-  | o  changeset: d3efd280421d24f9f229997c19e654761c942a71
-  | |   desc: mv b c
-  | o  changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
-  |/    desc: mv a b
-  o  changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
-      desc: initial
+  $ hg l
+  @  rev: 3
+  |  desc: mod a
+  | o  rev: 2
+  | |  desc: mv b c
+  | o  rev: 1
+  |/   desc: mv a b
+  o  rev: 0
+     desc: initial
   $ hg rebase -s . -d 2
   rebasing 3:d41316942216 "mod a" (tip)
   merging c and a to c
@@ -379,15 +382,15 @@
   $ echo c > a
   $ hg ci -m 'mod a'
   created new head
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
-  @  changeset: d413169422167a3fa5275fc5d71f7dea9f5775f3
-  |   desc: mod a
-  | o  changeset: d3efd280421d24f9f229997c19e654761c942a71
-  | |   desc: mv b c
-  | o  changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
-  |/    desc: mv a b
-  o  changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
-      desc: initial
+  $ hg l
+  @  rev: 3
+  |  desc: mod a
+  | o  rev: 2
+  | |  desc: mv b c
+  | o  rev: 1
+  |/   desc: mv a b
+  o  rev: 0
+     desc: initial
   $ hg rebase -s 1 -d .
   rebasing 1:472e38d57782 "mv a b"
   merging a and b to b
@@ -417,15 +420,15 @@
   $ hg ci -m 'mod a'
   created new head
 
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
-  @  changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
-  |   desc: mod a
-  | o  changeset: b1a6187e79fbce851bb584eadcb0cc4a80290fd9
-  | |   desc: add c
-  | o  changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
-  |/    desc: mv a b
-  o  changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
-      desc: initial
+  $ hg l
+  @  rev: 3
+  |  desc: mod a
+  | o  rev: 2
+  | |  desc: add c
+  | o  rev: 1
+  |/   desc: mv a b
+  o  rev: 0
+     desc: initial
 
   $ hg rebase -s . -d 2
   rebasing 3:ef716627c70b "mod a" (tip)
@@ -455,13 +458,13 @@
   created new head
   $ hg up -q 2
 
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
-  @  changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
-  |   desc: mv a b
-  | o  changeset: b0357b07f79129a3d08a68621271ca1352ae8a09
-  |/    desc: modify a
-  o  changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
-      desc: initial
+  $ hg l
+  @  rev: 2
+  |  desc: mv a b
+  | o  rev: 1
+  |/   desc: modify a
+  o  rev: 0
+     desc: initial
 
   $ hg merge 1
   merging b and a to b
@@ -490,13 +493,13 @@
   $ hg ci -m 'mod a'
   created new head
 
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
-  @  changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
-  |   desc: mod a
-  | o  changeset: 4fc3fd13fbdb89ada6b75bfcef3911a689a0dde8
-  |/    desc: cp a c, mv a b
-  o  changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
-      desc: initial
+  $ hg l
+  @  rev: 2
+  |  desc: mod a
+  | o  rev: 1
+  |/   desc: cp a c, mv a b
+  o  rev: 0
+     desc: initial
 
   $ hg rebase -s . -d 1
   rebasing 2:ef716627c70b "mod a" (tip)
@@ -530,32 +533,32 @@
   $ hg mv b c
   $ hg ci -qm 'mv b c'
   $ hg up -q 1
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
-  o  changeset: d3efd280421d24f9f229997c19e654761c942a71
-  |   desc: mv b c
-  o  changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
-  |   desc: mv a b
-  | @  changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
-  |/    desc: mod a
-  o  changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
-      desc: initial
+  $ hg l
+  o  rev: 3
+  |  desc: mv b c
+  o  rev: 2
+  |  desc: mv a b
+  | @  rev: 1
+  |/   desc: mod a
+  o  rev: 0
+     desc: initial
 
   $ hg merge 3
   merging a and c to c
   0 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
   $ hg ci -qm 'merge'
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
-  @    changeset: cd29b0d08c0f39bfed4cde1b40e30f419db0c825
-  |\    desc: merge, phase: draft
-  | o  changeset: d3efd280421d24f9f229997c19e654761c942a71
-  | |   desc: mv b c, phase: draft
-  | o  changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
-  | |   desc: mv a b, phase: draft
-  o |  changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
-  |/    desc: mod a, phase: draft
-  o  changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
-      desc: initial, phase: draft
+  $ hg pl
+  @    rev: 4, phase: draft
+  |\   desc: merge
+  | o  rev: 3, phase: draft
+  | |  desc: mv b c
+  | o  rev: 2, phase: draft
+  | |  desc: mv a b
+  o |  rev: 1, phase: draft
+  |/   desc: mod a
+  o  rev: 0, phase: draft
+     desc: initial
   $ ls
   c
   $ cd ..
@@ -577,11 +580,11 @@
   $ hg mv a b
   $ hg ci -m 'mv a b'
 
-  $ hg log -G -T 'changeset: {node}\n desc: {desc}\n'
-  @  changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
-  |   desc: mv a b
-  o  changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
-      desc: initial
+  $ hg l
+  @  rev: 1
+  |  desc: mv a b
+  o  rev: 0
+     desc: initial
   $ hg unshelve
   unshelving change 'default'
   rebasing shelved changes
@@ -614,13 +617,13 @@
   $ cd ..
   $ hg ci -qm 'mod a'
 
-  $ hg log -G -T 'changeset {node}\n desc {desc}, phase: {phase}\n'
-  @  changeset 6207d2d318e710b882e3d5ada2a89770efc42c96
-  |   desc mod a, phase: draft
-  | o  changeset abffdd4e3dfc04bc375034b970299b2a309a1cce
-  |/    desc mv a b; mv dir1 dir2, phase: draft
-  o  changeset 81973cd24b58db2fdf18ce3d64fb2cc3284e9ab3
-      desc initial, phase: draft
+  $ hg pl
+  @  rev: 2, phase: draft
+  |  desc: mod a
+  | o  rev: 1, phase: draft
+  |/   desc: mv a b; mv dir1 dir2
+  o  rev: 0, phase: draft
+     desc: initial
 
   $ hg rebase -s . -d 1 --config experimental.copytrace.sourcecommitlimit=100
   rebasing 2:6207d2d318e7 "mod a" (tip)
@@ -652,13 +655,13 @@
   $ hg mv -q dir1 dir2
   $ hg ci -qm 'mv dir1 dir2'
 
-  $ hg log -G -T 'changeset {node}\n desc {desc}, phase: {phase}\n'
-  @  changeset e8919e7df8d036e07b906045eddcd4a42ff1915f
-  |   desc mv dir1 dir2, phase: draft
-  | o  changeset 7c7c6f339be00f849c3cb2df738ca91db78b32c8
-  |/    desc hg add dir1/a, phase: draft
-  o  changeset a235dcce55dcf42034c4e374cb200662d0bb4a13
-      desc initial, phase: draft
+  $ hg pl
+  @  rev: 2, phase: draft
+  |  desc: mv dir1 dir2
+  | o  rev: 1, phase: draft
+  |/   desc: hg add dir1/a
+  o  rev: 0, phase: draft
+     desc: initial
 
   $ hg rebase -s . -d 1 --config experimental.copytrace.sourcecommitlimit=100
   rebasing 2:e8919e7df8d0 "mv dir1 dir2" (tip)
@@ -685,19 +688,19 @@
   $ mkdir foo
   $ hg mv a foo/bar
   $ hg ci -m "Moved a to foo/bar"
-  $ hg log -G -T 'changeset {node}\n desc {desc}, phase: {phase}\n'
-  @  changeset b4b0f7880e500b5c364a5f07b4a2b167de7a6fb0
-  |   desc Moved a to foo/bar, phase: draft
-  o  changeset 5f6d8a4bf34ab274ccc9f631c2536964b8a3666d
-  |   desc added b, phase: draft
-  | o  changeset 8b6e13696c38e8445a759516474640c2f8dddef6
-  |/    desc added more things to a, phase: draft
-  o  changeset 9092f1db7931481f93b37d5c9fbcfc341bcd7318
-      desc added a, phase: draft
+  $ hg pl
+  @  rev: 3, phase: draft
+  |  desc: Moved a to foo/bar
+  o  rev: 2, phase: draft
+  |  desc: added b
+  | o  rev: 1, phase: draft
+  |/   desc: added more things to a
+  o  rev: 0, phase: draft
+     desc: added a
 
 When the sourcecommitlimit is small and we have more drafts, we use heuristics only
 
-  $ hg rebase -s 8b6e13696 -d .
+  $ hg rebase -s 1 -d .
   rebasing 1:8b6e13696c38 "added more things to a"
   file 'a' was deleted in local [dest] but was modified in other [source].
   What do you want to do?
@@ -710,7 +713,7 @@
 
   $ hg rebase --abort
   rebase aborted
-  $ hg rebase -s 8b6e13696 -d . --config experimental.copytrace.sourcecommitlimit=100
+  $ hg rebase -s 1 -d . --config experimental.copytrace.sourcecommitlimit=100
   rebasing 1:8b6e13696c38 "added more things to a"
   merging foo/bar and a to foo/bar
   saved backup bundle to $TESTTMP/repo/repo/repo/.hg/strip-backup/8b6e13696c38-fc14ac83-rebase.hg
--- a/tests/test-debugcommands.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-debugcommands.t	Thu May 09 18:37:37 2019 -0400
@@ -545,6 +545,7 @@
   .hg/cache/tags2
   .hg/cache/rbc-revs-v1
   .hg/cache/rbc-names-v1
+  .hg/cache/hgtagsfnodes1
   .hg/cache/branch2-served
 
 Test debugcolor
--- a/tests/test-double-merge.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-double-merge.t	Thu May 09 18:37:37 2019 -0400
@@ -26,7 +26,6 @@
   summary:     cp foo bar; change both
   
   $ hg merge --debug
-    searching for copies back to rev 1
     unmatched files in other:
      bar
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
--- a/tests/test-fastannotate-hg.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-fastannotate-hg.t	Thu May 09 18:37:37 2019 -0400
@@ -1,6 +1,8 @@
 (this file is backported from core hg tests/test-annotate.t)
 
   $ cat >> $HGRCPATH << EOF
+  > [ui]
+  > merge = :merge3
   > [diff]
   > git=1
   > [extensions]
@@ -11,8 +13,6 @@
   > mainbranch=.
   > EOF
 
-  $ HGMERGE=true; export HGMERGE
-
 init
 
   $ hg init repo
@@ -157,8 +157,34 @@
   created new head
   $ hg merge
   merging b
-  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
-  (branch merge, don't forget to commit)
+  warning: conflicts while merging b! (edit, 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 merge --abort' to abandon
+  [1]
+  $ cat b
+  a
+  a
+  a
+  <<<<<<< working copy: 5fbdc1152d97 - test: b2.1
+  b4
+  c
+  b5
+  ||||||| base
+  =======
+  b4
+  b5
+  b6
+  >>>>>>> merge rev:    37ec9f5c3d1f - test: b2
+  $ cat <<EOF > b
+  > a
+  > a
+  > a
+  > b4
+  > c
+  > b5
+  > EOF
+  $ hg resolve --mark -q
+  $ rm b.orig
   $ hg ci -mmergeb -d '3 0'
 
 annotate after merge
@@ -247,15 +273,31 @@
   > EOF
   $ hg ci -mc -d '3 0'
   created new head
+Work around the pure version not resolving the conflict like native code
+#if pure
+  $ hg merge
+  merging b
+  warning: conflicts while merging b! (edit, 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 merge --abort' to abandon
+  [1]
+  $ cat <<EOF > b
+  > a
+  > z
+  > a
+  > b4
+  > c
+  > b5
+  > EOF
+  $ hg resolve -m b
+  (no more unresolved files)
+  $ rm b.orig
+#else
   $ hg merge
   merging b
   0 files updated, 1 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
-  $ cat <<EOF >> b
-  > b4
-  > c
-  > b5
-  > EOF
+#endif
   $ echo d >> b
   $ hg ci -mmerge2 -d '4 0'
 
@@ -745,6 +787,19 @@
   $ echo 3 >> a
   $ hg commit -m 3 -q
   $ hg merge 2 -q
+  warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
+  [1]
+  $ cat a
+  <<<<<<< working copy: 0a068f0261cf - test: 3
+  1
+  2
+  3
+  ||||||| base
+  1
+  2
+  =======
+  a
+  >>>>>>> merge rev:    9409851bc20a - test: a
   $ cat > a << EOF
   > b
   > 1
@@ -753,6 +808,7 @@
   > a
   > EOF
   $ hg resolve --mark -q
+  $ rm a.orig
   $ hg commit -m m
   $ hg annotate a
   4: b
--- a/tests/test-fastannotate-perfhack.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-fastannotate-perfhack.t	Thu May 09 18:37:37 2019 -0400
@@ -5,8 +5,6 @@
   > perfhack=1
   > EOF
 
-  $ HGMERGE=true; export HGMERGE
-
   $ hg init repo
   $ cd repo
 
--- a/tests/test-fastannotate-protocol.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-fastannotate-protocol.t	Thu May 09 18:37:37 2019 -0400
@@ -7,8 +7,6 @@
   > mainbranch=@
   > EOF
 
-  $ HGMERGE=true; export HGMERGE
-
 setup the server repo
 
   $ hg init repo-server
--- a/tests/test-fastannotate.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-fastannotate.t	Thu May 09 18:37:37 2019 -0400
@@ -3,8 +3,6 @@
   > fastannotate=
   > EOF
 
-  $ HGMERGE=true; export HGMERGE
-
   $ hg init repo
   $ cd repo
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-fix-metadata.t	Thu May 09 18:37:37 2019 -0400
@@ -0,0 +1,86 @@
+A python hook for "hg fix" that prints out the number of files and revisions
+that were affected, along with which fixer tools were applied. Also checks how
+many times it sees a specific key generated by one of the fixer tools defined
+below.
+
+  $ cat >> $TESTTMP/postfixhook.py <<EOF
+  > import collections
+  > def file(ui, repo, rev=None, path='', metadata=None, **kwargs):
+  >   ui.status('fixed %s in revision %d using %s\n' %
+  >             (path, rev, ', '.join(metadata.keys())))
+  > def summarize(ui, repo, replacements=None, wdirwritten=False,
+  >               metadata=None, **kwargs):
+  >     counts = collections.defaultdict(int)
+  >     keys = 0
+  >     for fixername, metadatalist in metadata.items():
+  >         for metadata in metadatalist:
+  >             if metadata is None:
+  >                 continue
+  >             counts[fixername] += 1
+  >             if 'key' in metadata:
+  >                 keys += 1
+  >     ui.status('saw "key" %d times\n' % (keys,))
+  >     for name, count in sorted(counts.items()):
+  >         ui.status('fixed %d files with %s\n' % (count, name))
+  >     if replacements:
+  >         ui.status('fixed %d revisions\n' % (len(replacements),))
+  >     if wdirwritten:
+  >         ui.status('fixed the working copy\n')
+  > EOF
+
+Some mock output for fixer tools that demonstrate what could go wrong with
+expecting the metadata output format.
+
+  $ printf 'new content\n' > $TESTTMP/missing
+  $ printf 'not valid json\0new content\n' > $TESTTMP/invalid
+  $ printf '{"key": "value"}\0new content\n' > $TESTTMP/valid
+
+Configure some fixer tools based on the output defined above, and enable the
+hooks defined above. Disable parallelism to make output of the parallel file
+processing phase stable.
+
+  $ cat >> $HGRCPATH <<EOF
+  > [extensions]
+  > fix =
+  > [fix]
+  > missing:command=cat $TESTTMP/missing
+  > missing:pattern=missing
+  > missing:metadata=true
+  > invalid:command=cat $TESTTMP/invalid
+  > invalid:pattern=invalid
+  > invalid:metadata=true
+  > valid:command=cat $TESTTMP/valid
+  > valid:pattern=valid
+  > valid:metadata=true
+  > [hooks]
+  > postfixfile = python:$TESTTMP/postfixhook.py:file
+  > postfix = python:$TESTTMP/postfixhook.py:summarize
+  > [worker]
+  > enabled=false
+  > EOF
+
+See what happens when we execute each of the fixer tools. Some print warnings,
+some write back to the file.
+
+  $ hg init repo
+  $ cd repo
+
+  $ printf "old content\n" > invalid
+  $ printf "old content\n" > missing
+  $ printf "old content\n" > valid
+  $ hg add -q
+
+  $ hg fix -w
+  ignored invalid output from fixer tool: invalid
+  ignored invalid output from fixer tool: missing
+  fixed valid in revision 2147483647 using valid
+  saw "key" 1 times
+  fixed 1 files with valid
+  fixed the working copy
+
+  $ cat missing invalid valid
+  old content
+  old content
+  new content
+
+  $ cd ..
--- a/tests/test-fix.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-fix.t	Thu May 09 18:37:37 2019 -0400
@@ -185,6 +185,36 @@
   tool may see different values for the arguments added by the :linerange
   suboption.
   
+  Each fixer tool is allowed to return some metadata in addition to the fixed
+  file content. The metadata must be placed before the file content on stdout,
+  separated from the file content by a zero byte. The metadata is parsed as a
+  JSON value (so, it should be UTF-8 encoded and contain no zero bytes). A fixer
+  tool is expected to produce this metadata encoding if and only if the
+  :metadata suboption is true:
+  
+    [fix]
+    tool:command = tool --prepend-json-metadata
+    tool:metadata = true
+  
+  The metadata values are passed to hooks, which can be used to print summaries
+  or perform other post-fixing work. The supported hooks are:
+  
+    "postfixfile"
+      Run once for each file in each revision where any fixer tools made changes
+      to the file content. Provides "$HG_REV" and "$HG_PATH" to identify the file,
+      and "$HG_METADATA" with a map of fixer names to metadata values from fixer
+      tools that affected the file. Fixer tools that didn't affect the file have a
+      valueof None. Only fixer tools that executed are present in the metadata.
+  
+    "postfix"
+      Run once after all files and revisions have been handled. Provides
+      "$HG_REPLACEMENTS" with information about what revisions were created and
+      made obsolete. Provides a boolean "$HG_WDIRWRITTEN" to indicate whether any
+      files in the working copy were updated. Provides a list "$HG_METADATA"
+      mapping fixer tool names to lists of metadata values returned from
+      executions that modified a file. This aggregates the same metadata
+      previously passed to the "postfixfile" hook.
+  
   list of commands:
   
    fix           rewrite file content in changesets or working directory
--- a/tests/test-graft.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-graft.t	Thu May 09 18:37:37 2019 -0400
@@ -75,6 +75,8 @@
 
   $ hg graft -r 2 --base 3
   grafting 2:5c095ad7e90f "2"
+  note: possible conflict - c was deleted and renamed to:
+   a
   note: graft of 2:5c095ad7e90f created no changes to commit
 
 Can't continue without starting:
@@ -199,7 +201,6 @@
   scanning for duplicate grafts
   skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
   grafting 1:5d205f8b35b6 "1"
-    searching for copies back to rev 1
     unmatched files in local:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
@@ -221,9 +222,9 @@
   committing changelog
   updating the branch cache
   grafting 5:97f8bfe72746 "5"
-    searching for copies back to rev 1
-    unmatched files in other (from topological common ancestor):
-     c
+    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: 'c' -> dst: 'b' 
+    checking for directory renames
   resolving manifests
    branchmerge: True, force: True, partial: False
    ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
@@ -237,9 +238,9 @@
   $ HGEDITOR=cat hg graft 4 3 --log --debug
   scanning for duplicate grafts
   grafting 4:9c233e8e184d "4"
-    searching for copies back to rev 1
-    unmatched files in other (from topological common ancestor):
-     c
+    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: 'c' -> dst: 'b' 
+    checking for directory renames
   resolving manifests
    branchmerge: True, force: True, partial: False
    ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
@@ -744,11 +745,9 @@
   $ hg graft -q 13 --debug
   scanning for duplicate grafts
   grafting 13:7a4785234d87 "2"
-    searching for copies back to rev 12
-    unmatched files in other (from topological common ancestor):
-     g
-    unmatched files new in both:
-     b
+    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: 'a' -> dst: 'b' *
+    checking for directory renames
   resolving manifests
    branchmerge: True, force: True, partial: False
    ancestor: b592ea63bb0c, local: 7e61b508e709+, remote: 7a4785234d87
@@ -969,7 +968,6 @@
 A.4 has a degenerate case a<-b<-a->a, where checkcopies isn't needed at all.
 A.5 has a special case a<-b<-b->a, which is treated like a<-b->a in a merge.
 A.5 has issue5343 as a special case.
-TODO: add test coverage for A.5
 A.6 has a special case a<-a<-b->a. Here, checkcopies will find a spurious
 incomplete divergence, which is in fact complete. This is handled later in
 mergecopies.
@@ -1072,13 +1070,32 @@
   $ hg mv f4a f4e
   $ hg mv f5a f5b
   $ hg ci -qAm "E0"
+  $ hg up -q "min(desc("A0"))"
+  $ hg cp f1a f1f
+  $ hg ci -qAm "F0"
+  $ hg up -q "min(desc("A0"))"
+  $ hg cp f1a f1g
+  $ echo c1g > f1g
+  $ hg ci -qAm "G0"
   $ hg log -G
-  @  changeset:   6:6bd1736cab86
+  @  changeset:   8:ba67f08fb15a
   |  tag:         tip
   |  parent:      0:11f7a1b56675
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  summary:     E0
+  |  summary:     G0
+  |
+  | o  changeset:   7:d376ab0d7fda
+  |/   parent:      0:11f7a1b56675
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    summary:     F0
+  |
+  | o  changeset:   6:6bd1736cab86
+  |/   parent:      0:11f7a1b56675
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    summary:     E0
   |
   | o  changeset:   5:560daee679da
   | |  user:        test
@@ -1115,11 +1132,11 @@
 Test the cases A.4 (f1x), the "ping-pong" special case of A.7 (f5x),
 and A.3 with a local content change to be preserved (f2x).
 
+  $ hg up -q "desc("E0")"
   $ HGEDITOR="echo C2 >" hg graft -r 'desc("C0")' --edit
   grafting 2:f58c7e2b28fa "C0"
   merging f1e and f1b to f1e
   merging f2a and f2c to f2c
-  merging f5b and f5a to f5a
 
 Test the cases A.1 (f4x) and A.7 (f3x).
 
@@ -1131,93 +1148,129 @@
   merging f4e and f4a to f4e
   warning: can't find ancestor for 'f3d' copied from 'f3b'!
 
+  $ hg cat f2c
+  c2e
+
+Test the case A.5 (move case, f1x).
+
+  $ hg up -q "desc("C0")"
+BROKEN: Shouldn't get the warning about missing ancestor
+  $ HGEDITOR="echo E1 >" hg graft -r 'desc("E0")' --edit
+  grafting 6:6bd1736cab86 "E0"
+  note: possible conflict - f1a was renamed multiple times to:
+   f1b
+   f1e
+  note: possible conflict - f3a was renamed multiple times to:
+   f3b
+   f3e
+  merging f2c and f2a to f2c
+  merging f5a and f5b to f5b
+  warning: can't find ancestor for 'f1e' copied from 'f1a'!
+  warning: can't find ancestor for 'f3e' copied from 'f3a'!
+  $ cat f1e
+  c1a
+
+Test the case A.5 (copy case, f1x).
+
+  $ hg up -q "desc("C0")"
+BROKEN: Shouldn't get the warning about missing ancestor
+  $ HGEDITOR="echo F1 >" hg graft -r 'desc("F0")' --edit
+  grafting 7:d376ab0d7fda "F0"
+  warning: can't find ancestor for 'f1f' copied from 'f1a'!
+BROKEN: f1f should be marked a copy from f1b
+  $ hg st --copies --change .
+  A f1f
+BROKEN: f1f should have the new content from f1b (i.e. "c1c")
+  $ cat f1f
+  c1a
+
+Test the case A.5 (copy+modify case, f1x).
+
+  $ hg up -q "desc("C0")"
+BROKEN: We should get a merge conflict from the 3-way merge between f1b in C0
+(content "c1c") and f1g in G0 (content "c1g") with f1a in A0 as base (content
+"c1a")
+  $ HGEDITOR="echo G1 >" hg graft -r 'desc("G0")' --edit
+  grafting 8:ba67f08fb15a "G0"
+  warning: can't find ancestor for 'f1g' copied from 'f1a'!
+
 Check the results of the grafts tested
 
   $ hg log -CGv --patch --git
-  @  changeset:   8:93ee502e8b0a
+  @  changeset:   13:ef3adf6c20a4
   |  tag:         tip
+  |  parent:      2:f58c7e2b28fa
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  files:       f3d f4e
+  |  files:       f1g
   |  description:
-  |  D2
+  |  G1
   |
   |
-  |  diff --git a/f3d b/f3d
+  |  diff --git a/f1g b/f1g
   |  new file mode 100644
   |  --- /dev/null
-  |  +++ b/f3d
+  |  +++ b/f1g
   |  @@ -0,0 +1,1 @@
-  |  +c3a
-  |  diff --git a/f4e b/f4e
-  |  --- a/f4e
-  |  +++ b/f4e
-  |  @@ -1,1 +1,1 @@
-  |  -c4a
-  |  +c4d
+  |  +c1g
   |
-  o  changeset:   7:539cf145f496
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  files:       f1e f2a f2c f5a f5b
-  |  copies:      f2c (f2a) f5a (f5b)
-  |  description:
-  |  C2
+  | o  changeset:   12:b5542d755b54
+  |/   parent:      2:f58c7e2b28fa
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    files:       f1f
+  |    description:
+  |    F1
   |
   |
-  |  diff --git a/f1e b/f1e
-  |  --- a/f1e
-  |  +++ b/f1e
-  |  @@ -1,1 +1,1 @@
-  |  -c1a
-  |  +c1c
-  |  diff --git a/f2a b/f2c
-  |  rename from f2a
-  |  rename to f2c
-  |  diff --git a/f5b b/f5a
-  |  rename from f5b
-  |  rename to f5a
-  |  --- a/f5b
-  |  +++ b/f5a
-  |  @@ -1,1 +1,1 @@
-  |  -c5a
-  |  +c5c
+  |    diff --git a/f1f b/f1f
+  |    new file mode 100644
+  |    --- /dev/null
+  |    +++ b/f1f
+  |    @@ -0,0 +1,1 @@
+  |    +c1a
   |
-  o  changeset:   6:6bd1736cab86
-  |  parent:      0:11f7a1b56675
-  |  user:        test
-  |  date:        Thu Jan 01 00:00:00 1970 +0000
-  |  files:       f1a f1e f2a f3a f3e f4a f4e f5a f5b
-  |  copies:      f1e (f1a) f3e (f3a) f4e (f4a) f5b (f5a)
-  |  description:
-  |  E0
+  | o  changeset:   11:f8a162271246
+  |/   parent:      2:f58c7e2b28fa
+  |    user:        test
+  |    date:        Thu Jan 01 00:00:00 1970 +0000
+  |    files:       f1e f2c f3e f4a f4e f5a f5b
+  |    copies:      f4e (f4a) f5b (f5a)
+  |    description:
+  |    E1
   |
   |
-  |  diff --git a/f1a b/f1e
-  |  rename from f1a
-  |  rename to f1e
-  |  diff --git a/f2a b/f2a
-  |  --- a/f2a
-  |  +++ b/f2a
-  |  @@ -1,1 +1,1 @@
-  |  -c2a
-  |  +c2e
-  |  diff --git a/f3a b/f3e
-  |  rename from f3a
-  |  rename to f3e
-  |  diff --git a/f4a b/f4e
-  |  rename from f4a
-  |  rename to f4e
-  |  diff --git a/f5a b/f5b
-  |  rename from f5a
-  |  rename to f5b
+  |    diff --git a/f1e b/f1e
+  |    new file mode 100644
+  |    --- /dev/null
+  |    +++ b/f1e
+  |    @@ -0,0 +1,1 @@
+  |    +c1a
+  |    diff --git a/f2c b/f2c
+  |    --- a/f2c
+  |    +++ b/f2c
+  |    @@ -1,1 +1,1 @@
+  |    -c2a
+  |    +c2e
+  |    diff --git a/f3e b/f3e
+  |    new file mode 100644
+  |    --- /dev/null
+  |    +++ b/f3e
+  |    @@ -0,0 +1,1 @@
+  |    +c3a
+  |    diff --git a/f4a b/f4e
+  |    rename from f4a
+  |    rename to f4e
+  |    diff --git a/f5a b/f5b
+  |    rename from f5a
+  |    rename to f5b
   |
-  | o  changeset:   5:560daee679da
+  | o  changeset:   10:93ee502e8b0a
   | |  user:        test
   | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  files:       f3d f4a
+  | |  files:       f3d f4e
   | |  description:
-  | |  D1
+  | |  D2
   | |
   | |
   | |  diff --git a/f3d b/f3d
@@ -1226,59 +1279,170 @@
   | |  +++ b/f3d
   | |  @@ -0,0 +1,1 @@
   | |  +c3a
-  | |  diff --git a/f4a b/f4a
-  | |  --- a/f4a
-  | |  +++ b/f4a
+  | |  diff --git a/f4e b/f4e
+  | |  --- a/f4e
+  | |  +++ b/f4e
   | |  @@ -1,1 +1,1 @@
   | |  -c4a
   | |  +c4d
   | |
-  | o  changeset:   4:c9763722f9bd
-  |/   parent:      0:11f7a1b56675
-  |    user:        test
-  |    date:        Thu Jan 01 00:00:00 1970 +0000
-  |    files:       f1a f2a f2c f5a
-  |    copies:      f2c (f2a)
-  |    description:
-  |    C1
-  |
-  |
-  |    diff --git a/f1a b/f1a
-  |    --- a/f1a
-  |    +++ b/f1a
-  |    @@ -1,1 +1,1 @@
-  |    -c1a
-  |    +c1c
-  |    diff --git a/f2a b/f2c
-  |    rename from f2a
-  |    rename to f2c
-  |    diff --git a/f5a b/f5a
-  |    --- a/f5a
-  |    +++ b/f5a
-  |    @@ -1,1 +1,1 @@
-  |    -c5a
-  |    +c5c
-  |
-  | o  changeset:   3:b69f5839d2d9
+  | o  changeset:   9:539cf145f496
+  | |  parent:      6:6bd1736cab86
   | |  user:        test
   | |  date:        Thu Jan 01 00:00:00 1970 +0000
-  | |  files:       f3b f3d f4a
-  | |  copies:      f3d (f3b)
+  | |  files:       f1e f2a f2c f5a f5b
+  | |  copies:      f2c (f2a) f5a (f5b)
   | |  description:
-  | |  D0
+  | |  C2
+  | |
+  | |
+  | |  diff --git a/f1e b/f1e
+  | |  --- a/f1e
+  | |  +++ b/f1e
+  | |  @@ -1,1 +1,1 @@
+  | |  -c1a
+  | |  +c1c
+  | |  diff --git a/f2a b/f2c
+  | |  rename from f2a
+  | |  rename to f2c
+  | |  diff --git a/f5b b/f5a
+  | |  rename from f5b
+  | |  rename to f5a
+  | |  --- a/f5b
+  | |  +++ b/f5a
+  | |  @@ -1,1 +1,1 @@
+  | |  -c5a
+  | |  +c5c
+  | |
+  | | o  changeset:   8:ba67f08fb15a
+  | | |  parent:      0:11f7a1b56675
+  | | |  user:        test
+  | | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | | |  files:       f1g
+  | | |  copies:      f1g (f1a)
+  | | |  description:
+  | | |  G0
+  | | |
+  | | |
+  | | |  diff --git a/f1a b/f1g
+  | | |  copy from f1a
+  | | |  copy to f1g
+  | | |  --- a/f1a
+  | | |  +++ b/f1g
+  | | |  @@ -1,1 +1,1 @@
+  | | |  -c1a
+  | | |  +c1g
+  | | |
+  | | | o  changeset:   7:d376ab0d7fda
+  | | |/   parent:      0:11f7a1b56675
+  | | |    user:        test
+  | | |    date:        Thu Jan 01 00:00:00 1970 +0000
+  | | |    files:       f1f
+  | | |    copies:      f1f (f1a)
+  | | |    description:
+  | | |    F0
+  | | |
+  | | |
+  | | |    diff --git a/f1a b/f1f
+  | | |    copy from f1a
+  | | |    copy to f1f
+  | | |
+  | o |  changeset:   6:6bd1736cab86
+  | |/   parent:      0:11f7a1b56675
+  | |    user:        test
+  | |    date:        Thu Jan 01 00:00:00 1970 +0000
+  | |    files:       f1a f1e f2a f3a f3e f4a f4e f5a f5b
+  | |    copies:      f1e (f1a) f3e (f3a) f4e (f4a) f5b (f5a)
+  | |    description:
+  | |    E0
   | |
   | |
-  | |  diff --git a/f3b b/f3d
-  | |  rename from f3b
-  | |  rename to f3d
-  | |  diff --git a/f4a b/f4a
-  | |  --- a/f4a
-  | |  +++ b/f4a
-  | |  @@ -1,1 +1,1 @@
-  | |  -c4a
-  | |  +c4d
+  | |    diff --git a/f1a b/f1e
+  | |    rename from f1a
+  | |    rename to f1e
+  | |    diff --git a/f2a b/f2a
+  | |    --- a/f2a
+  | |    +++ b/f2a
+  | |    @@ -1,1 +1,1 @@
+  | |    -c2a
+  | |    +c2e
+  | |    diff --git a/f3a b/f3e
+  | |    rename from f3a
+  | |    rename to f3e
+  | |    diff --git a/f4a b/f4e
+  | |    rename from f4a
+  | |    rename to f4e
+  | |    diff --git a/f5a b/f5b
+  | |    rename from f5a
+  | |    rename to f5b
   | |
-  | o  changeset:   2:f58c7e2b28fa
+  | | o  changeset:   5:560daee679da
+  | | |  user:        test
+  | | |  date:        Thu Jan 01 00:00:00 1970 +0000
+  | | |  files:       f3d f4a
+  | | |  description:
+  | | |  D1
+  | | |
+  | | |
+  | | |  diff --git a/f3d b/f3d
+  | | |  new file mode 100644
+  | | |  --- /dev/null
+  | | |  +++ b/f3d
+  | | |  @@ -0,0 +1,1 @@
+  | | |  +c3a
+  | | |  diff --git a/f4a b/f4a
+  | | |  --- a/f4a
+  | | |  +++ b/f4a
+  | | |  @@ -1,1 +1,1 @@
+  | | |  -c4a
+  | | |  +c4d
+  | | |
+  | | o  changeset:   4:c9763722f9bd
+  | |/   parent:      0:11f7a1b56675
+  | |    user:        test
+  | |    date:        Thu Jan 01 00:00:00 1970 +0000
+  | |    files:       f1a f2a f2c f5a
+  | |    copies:      f2c (f2a)
+  | |    description:
+  | |    C1
+  | |
+  | |
+  | |    diff --git a/f1a b/f1a
+  | |    --- a/f1a
+  | |    +++ b/f1a
+  | |    @@ -1,1 +1,1 @@
+  | |    -c1a
+  | |    +c1c
+  | |    diff --git a/f2a b/f2c
+  | |    rename from f2a
+  | |    rename to f2c
+  | |    diff --git a/f5a b/f5a
+  | |    --- a/f5a
+  | |    +++ b/f5a
+  | |    @@ -1,1 +1,1 @@
+  | |    -c5a
+  | |    +c5c
+  | |
+  +---o  changeset:   3:b69f5839d2d9
+  | |    user:        test
+  | |    date:        Thu Jan 01 00:00:00 1970 +0000
+  | |    files:       f3b f3d f4a
+  | |    copies:      f3d (f3b)
+  | |    description:
+  | |    D0
+  | |
+  | |
+  | |    diff --git a/f3b b/f3d
+  | |    rename from f3b
+  | |    rename to f3d
+  | |    diff --git a/f4a b/f4a
+  | |    --- a/f4a
+  | |    +++ b/f4a
+  | |    @@ -1,1 +1,1 @@
+  | |    -c4a
+  | |    +c4d
+  | |
+  o |  changeset:   2:f58c7e2b28fa
   | |  user:        test
   | |  date:        Thu Jan 01 00:00:00 1970 +0000
   | |  files:       f1b f2a f2c f5a f5b
@@ -1305,7 +1469,7 @@
   | |  -c5a
   | |  +c5c
   | |
-  | o  changeset:   1:3d7bba921b5d
+  o |  changeset:   1:3d7bba921b5d
   |/   user:        test
   |    date:        Thu Jan 01 00:00:00 1970 +0000
   |    files:       f1a f1b f3a f3b f5a f5b
@@ -1363,9 +1527,6 @@
      @@ -0,0 +1,1 @@
      +c5a
   
-  $ hg cat f2c
-  c2e
-
 Check superfluous filemerge of files renamed in the past but untouched by graft
 
   $ echo a > a
--- a/tests/test-help.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-help.t	Thu May 09 18:37:37 2019 -0400
@@ -615,6 +615,8 @@
   
       Returns 0 on success, 1 if errors are encountered.
   
+  options:
+  
   (some details hidden, use --verbose to show complete help)
 
   $ hg help diff
--- a/tests/test-issue1802.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-issue1802.t	Thu May 09 18:37:37 2019 -0400
@@ -52,7 +52,6 @@
 Simulate a Windows merge:
 
   $ hg --config extensions.n=$TESTTMP/noexec.py merge --debug
-    searching for copies back to rev 1
     unmatched files in local:
      b
   resolving manifests
--- a/tests/test-issue522.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-issue522.t	Thu May 09 18:37:37 2019 -0400
@@ -25,7 +25,6 @@
   $ hg ci -qAm 'add bar'
 
   $ hg merge --debug
-    searching for copies back to rev 1
     unmatched files in local:
      bar
   resolving manifests
--- a/tests/test-issue672.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-issue672.t	Thu May 09 18:37:37 2019 -0400
@@ -25,7 +25,6 @@
   created new head
 
   $ hg merge --debug 1
-    searching for copies back to rev 1
     unmatched files in other:
      1a
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
@@ -54,7 +53,6 @@
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 
   $ hg merge -y --debug 4
-    searching for copies back to rev 1
     unmatched files in local:
      1a
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
@@ -77,7 +75,6 @@
   1 files updated, 0 files merged, 1 files removed, 0 files unresolved
 
   $ hg merge -y --debug 3
-    searching for copies back to rev 1
     unmatched files in other:
      1a
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
--- a/tests/test-log.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-log.t	Thu May 09 18:37:37 2019 -0400
@@ -1081,7 +1081,7 @@
   $ cd ..
 
 log --follow --patch FILE in repository where linkrev isn't trustworthy
-(issue5376)
+(issue5376, issue6124)
 
   $ hg init follow-dup
   $ cd follow-dup
@@ -1129,6 +1129,16 @@
   @@ -0,0 +1,1 @@
   +0
   
+  $ hg log -pr . a
+  === 3: a3
+  diff -r 4ea02ba94d66 -r e7a6331a34f0 a
+  --- a/a
+  +++ b/a
+  @@ -1,2 +1,3 @@
+   0
+   1
+  +3
+  
 
  fctx.introrev() == 2, but fctx.linkrev() == 1
 
@@ -1150,6 +1160,9 @@
   +0
   
 
+BROKEN: should show the same diff as for rev 2 above
+  $ hg log -pr . a
+
   $ cd ..
 
 Multiple copy sources of a file:
--- a/tests/test-merge-commit.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-merge-commit.t	Thu May 09 18:37:37 2019 -0400
@@ -67,7 +67,6 @@
 This should use bar@rev2 as the ancestor:
 
   $ hg --debug merge 3
-    searching for copies back to rev 1
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 0f2ff26688b9, local: 2263c1be0967+, remote: 0555950ead28
@@ -155,7 +154,6 @@
 This should use bar@rev2 as the ancestor:
 
   $ hg --debug merge 3
-    searching for copies back to rev 1
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 0f2ff26688b9, local: 2263c1be0967+, remote: 3ffa6b9e35f0
--- a/tests/test-merge-criss-cross.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-merge-criss-cross.t	Thu May 09 18:37:37 2019 -0400
@@ -11,15 +11,9 @@
 
   $ hg up -qr0
   $ echo '2 first change' > f2
-  $ mkdir d1
-  $ echo '0 base' > d1/f3
-  $ echo '0 base' > d1/f4
-  $ hg add -q d1
   $ hg ci -qm '2 first change f2'
 
   $ hg merge -qr 1
-  $ hg rm d1/f3
-  $ hg mv -q d1 d2
   $ hg ci -m '3 merge'
 
   $ hg up -qr2
@@ -30,38 +24,38 @@
   $ hg ci -m '5 second change f1'
 
   $ hg up -r3
-  2 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ echo '6 second change' > f2
   $ hg ci -m '6 second change f2'
 
   $ hg log -G
-  @  changeset:   6:6373bbfdae1d
+  @  changeset:   6:3b08d01b0ab5
   |  tag:         tip
-  |  parent:      3:c202c8af058d
+  |  parent:      3:cf89f02107e5
   |  user:        test
   |  date:        Thu Jan 01 00:00:00 1970 +0000
   |  summary:     6 second change f2
   |
-  | o  changeset:   5:e673248094b1
+  | o  changeset:   5:adfe50279922
   | |  user:        test
   | |  date:        Thu Jan 01 00:00:00 1970 +0000
   | |  summary:     5 second change f1
   | |
-  | o    changeset:   4:177f58377c06
-  | |\   parent:      2:d1d156401c1b
+  | o    changeset:   4:7d3e55501ae6
+  | |\   parent:      2:40663881a6dd
   | | |  parent:      1:0f6b37dbe527
   | | |  user:        test
   | | |  date:        Thu Jan 01 00:00:00 1970 +0000
   | | |  summary:     4 merge
   | | |
-  o---+  changeset:   3:c202c8af058d
-  | | |  parent:      2:d1d156401c1b
+  o---+  changeset:   3:cf89f02107e5
+  | | |  parent:      2:40663881a6dd
   |/ /   parent:      1:0f6b37dbe527
   | |    user:        test
   | |    date:        Thu Jan 01 00:00:00 1970 +0000
   | |    summary:     3 merge
   | |
-  | o  changeset:   2:d1d156401c1b
+  | o  changeset:   2:40663881a6dd
   | |  parent:      0:40494bf2444c
   | |  user:        test
   | |  date:        Thu Jan 01 00:00:00 1970 +0000
@@ -79,51 +73,26 @@
   
 
   $ hg merge -v --debug --tool internal:dump 5 --config merge.preferancestor='!'
-  note: using 0f6b37dbe527 as ancestor of 6373bbfdae1d and e673248094b1
-        alternatively, use --config merge.preferancestor=d1d156401c1b
-    searching for copies back to rev 3
-    unmatched files in local:
-     d2/f4
-    unmatched files in other:
-     d1/f3
-     d1/f4
-    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     src: 'd1/f4' -> dst: 'd2/f4' 
-    checking for directory renames
-     discovered dir src: 'd1/' -> dst: 'd2/'
-     pending file src: 'd1/f3' -> dst: 'd2/f3'
-     pending file src: 'd1/f4' -> dst: 'd2/f4'
+  note: using 0f6b37dbe527 as ancestor of 3b08d01b0ab5 and adfe50279922
+        alternatively, use --config merge.preferancestor=40663881a6dd
   resolving manifests
    branchmerge: True, force: False, partial: False
-   ancestor: 0f6b37dbe527, local: 6373bbfdae1d+, remote: e673248094b1
-   preserving d2/f4 for resolve of d2/f4
+   ancestor: 0f6b37dbe527, local: 3b08d01b0ab5+, remote: adfe50279922
    preserving f2 for resolve of f2
    f1: remote is newer -> g
   getting f1
-   d2/f3: local directory rename - get from d1/f3 -> dg
-  getting d1/f3 to d2/f3
-   d2/f4: local directory rename, both created -> m (premerge)
    f2: versions differ -> m (premerge)
   picked tool ':dump' for f2 (binary False symlink False changedelete False)
   merging f2
-  my f2@6373bbfdae1d+ other f2@e673248094b1 ancestor f2@0f6b37dbe527
+  my f2@3b08d01b0ab5+ other f2@adfe50279922 ancestor f2@0f6b37dbe527
    f2: versions differ -> m (merge)
   picked tool ':dump' for f2 (binary False symlink False changedelete False)
-  my f2@6373bbfdae1d+ other f2@e673248094b1 ancestor f2@0f6b37dbe527
-  3 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  my f2@3b08d01b0ab5+ other f2@adfe50279922 ancestor f2@0f6b37dbe527
+  1 files updated, 0 files merged, 0 files removed, 1 files unresolved
   use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
   [1]
 
-  $ f --dump --recurse *
-  d2: directory with 2 files
-  d2/f3:
-  >>>
-  0 base
-  <<<
-  d2/f4:
-  >>>
-  0 base
-  <<<
+  $ f --dump *
   f1:
   >>>
   5 second change
@@ -151,13 +120,11 @@
 
   $ hg up -qC .
   $ hg merge -v --tool internal:dump 5 --config merge.preferancestor="null 40663881 3b08d"
-  note: using 0f6b37dbe527 as ancestor of 6373bbfdae1d and e673248094b1
-        alternatively, use --config merge.preferancestor=d1d156401c1b
+  note: using 40663881a6dd as ancestor of 3b08d01b0ab5 and adfe50279922
+        alternatively, use --config merge.preferancestor=0f6b37dbe527
   resolving manifests
-  getting f1
-  getting d1/f3 to d2/f3
-  merging f2
-  3 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  merging f1
+  0 files updated, 0 files merged, 0 files removed, 1 files unresolved
   use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
   [1]
 
@@ -166,70 +133,34 @@
   $ rm f*
   $ hg up -qC .
   $ hg merge -v --debug --tool internal:dump 5 --config merge.preferancestor="*"
-  note: merging 6373bbfdae1d+ and e673248094b1 using bids from ancestors 0f6b37dbe527 and d1d156401c1b
+  note: merging 3b08d01b0ab5+ and adfe50279922 using bids from ancestors 0f6b37dbe527 and 40663881a6dd
   
   calculating bids for ancestor 0f6b37dbe527
-    searching for copies back to rev 3
-    unmatched files in local:
-     d2/f4
-    unmatched files in other:
-     d1/f3
-     d1/f4
-    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     src: 'd1/f4' -> dst: 'd2/f4' 
-    checking for directory renames
-     discovered dir src: 'd1/' -> dst: 'd2/'
-     pending file src: 'd1/f3' -> dst: 'd2/f3'
-     pending file src: 'd1/f4' -> dst: 'd2/f4'
   resolving manifests
    branchmerge: True, force: False, partial: False
-   ancestor: 0f6b37dbe527, local: 6373bbfdae1d+, remote: e673248094b1
-   d2/f3: local directory rename - get from d1/f3 -> dg
-   d2/f4: local directory rename, both created -> m
+   ancestor: 0f6b37dbe527, local: 3b08d01b0ab5+, remote: adfe50279922
    f1: remote is newer -> g
    f2: versions differ -> m
   
-  calculating bids for ancestor d1d156401c1b
-    searching for copies back to rev 3
-    unmatched files in local:
-     d2/f4
-    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     src: 'd1/f4' -> dst: 'd2/f4' 
-    checking for directory renames
-     discovered dir src: 'd1/' -> dst: 'd2/'
+  calculating bids for ancestor 40663881a6dd
   resolving manifests
    branchmerge: True, force: False, partial: False
-   ancestor: d1d156401c1b, local: 6373bbfdae1d+, remote: e673248094b1
+   ancestor: 40663881a6dd, local: 3b08d01b0ab5+, remote: adfe50279922
    f1: versions differ -> m
    f2: remote unchanged -> k
   
   auction for merging merge bids
-   d2/f3: consensus for dg
-   d2/f4: consensus for m
    f1: picking 'get' action
    f2: picking 'keep' action
   end of auction
   
-   preserving d2/f4 for resolve of d2/f4
    f1: remote is newer -> g
   getting f1
    f2: remote unchanged -> k
-   d2/f3: local directory rename - get from d1/f3 -> dg
-  getting d1/f3 to d2/f3
-   d2/f4: local directory rename, both created -> m (premerge)
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
-  $ f --dump --recurse *
-  d2: directory with 2 files
-  d2/f3:
-  >>>
-  0 base
-  <<<
-  d2/f4:
-  >>>
-  0 base
-  <<<
+  $ f --dump *
   f1:
   >>>
   5 second change
@@ -243,79 +174,36 @@
 The other way around:
 
   $ hg up -C -r5
-  4 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg merge -v --debug --config merge.preferancestor="*"
-  note: merging e673248094b1+ and 6373bbfdae1d using bids from ancestors 0f6b37dbe527 and d1d156401c1b
+  note: merging adfe50279922+ and 3b08d01b0ab5 using bids from ancestors 0f6b37dbe527 and 40663881a6dd
   
   calculating bids for ancestor 0f6b37dbe527
-    searching for copies back to rev 3
-    unmatched files in local:
-     d1/f3
-     d1/f4
-    unmatched files in other:
-     d2/f4
-    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     src: 'd1/f4' -> dst: 'd2/f4' 
-    checking for directory renames
-     discovered dir src: 'd1/' -> dst: 'd2/'
-     pending file src: 'd1/f3' -> dst: 'd2/f3'
-     pending file src: 'd1/f4' -> dst: 'd2/f4'
   resolving manifests
    branchmerge: True, force: False, partial: False
-   ancestor: 0f6b37dbe527, local: e673248094b1+, remote: 6373bbfdae1d
-   d2/f3: remote directory rename - move from d1/f3 -> dm
-   d2/f4: remote directory rename, both created -> m
+   ancestor: 0f6b37dbe527, local: adfe50279922+, remote: 3b08d01b0ab5
    f1: remote unchanged -> k
    f2: versions differ -> m
   
-  calculating bids for ancestor d1d156401c1b
-    searching for copies back to rev 3
-    unmatched files in other:
-     d2/f4
-    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     src: 'd1/f4' -> dst: 'd2/f4' 
-    checking for directory renames
-     discovered dir src: 'd1/' -> dst: 'd2/'
+  calculating bids for ancestor 40663881a6dd
   resolving manifests
    branchmerge: True, force: False, partial: False
-   ancestor: d1d156401c1b, local: e673248094b1+, remote: 6373bbfdae1d
-   d1/f3: other deleted -> r
-   d1/f4: other deleted -> r
-   d2/f4: remote created -> g
+   ancestor: 40663881a6dd, local: adfe50279922+, remote: 3b08d01b0ab5
    f1: versions differ -> m
    f2: remote is newer -> g
   
   auction for merging merge bids
-   d1/f3: consensus for r
-   d1/f4: consensus for r
-   d2/f3: consensus for dm
-   d2/f4: picking 'get' action
    f1: picking 'keep' action
    f2: picking 'get' action
   end of auction
   
-   d1/f3: other deleted -> r
-  removing d1/f3
-   d1/f4: other deleted -> r
-  removing d1/f4
-   d2/f4: remote created -> g
-  getting d2/f4
    f2: remote is newer -> g
   getting f2
    f1: remote unchanged -> k
-  2 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
-  $ f --dump --recurse *
-  d2: directory with 2 files
-  d2/f3:
-  >>>
-  0 base
-  <<<
-  d2/f4:
-  >>>
-  0 base
-  <<<
+  $ f --dump *
   f1:
   >>>
   5 second change
@@ -329,85 +217,55 @@
 
   $ hg up -qC
   $ hg merge
-  2 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
   $ hg up -qC tip
   $ hg merge -v
-  note: merging 6373bbfdae1d+ and e673248094b1 using bids from ancestors 0f6b37dbe527 and d1d156401c1b
+  note: merging 3b08d01b0ab5+ and adfe50279922 using bids from ancestors 0f6b37dbe527 and 40663881a6dd
   
   calculating bids for ancestor 0f6b37dbe527
   resolving manifests
   
-  calculating bids for ancestor d1d156401c1b
+  calculating bids for ancestor 40663881a6dd
   resolving manifests
   
   auction for merging merge bids
-   d2/f3: consensus for dg
-   d2/f4: consensus for m
    f1: picking 'get' action
    f2: picking 'keep' action
   end of auction
   
   getting f1
-  getting d1/f3 to d2/f3
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
   $ hg up -qC
   $ hg merge -v --debug --config merge.preferancestor="*"
-  note: merging 6373bbfdae1d+ and e673248094b1 using bids from ancestors 0f6b37dbe527 and d1d156401c1b
+  note: merging 3b08d01b0ab5+ and adfe50279922 using bids from ancestors 0f6b37dbe527 and 40663881a6dd
   
   calculating bids for ancestor 0f6b37dbe527
-    searching for copies back to rev 3
-    unmatched files in local:
-     d2/f4
-    unmatched files in other:
-     d1/f3
-     d1/f4
-    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     src: 'd1/f4' -> dst: 'd2/f4' 
-    checking for directory renames
-     discovered dir src: 'd1/' -> dst: 'd2/'
-     pending file src: 'd1/f3' -> dst: 'd2/f3'
-     pending file src: 'd1/f4' -> dst: 'd2/f4'
   resolving manifests
    branchmerge: True, force: False, partial: False
-   ancestor: 0f6b37dbe527, local: 6373bbfdae1d+, remote: e673248094b1
-   d2/f3: local directory rename - get from d1/f3 -> dg
-   d2/f4: local directory rename, both created -> m
+   ancestor: 0f6b37dbe527, local: 3b08d01b0ab5+, remote: adfe50279922
    f1: remote is newer -> g
    f2: versions differ -> m
   
-  calculating bids for ancestor d1d156401c1b
-    searching for copies back to rev 3
-    unmatched files in local:
-     d2/f4
-    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
-     src: 'd1/f4' -> dst: 'd2/f4' 
-    checking for directory renames
-     discovered dir src: 'd1/' -> dst: 'd2/'
+  calculating bids for ancestor 40663881a6dd
   resolving manifests
    branchmerge: True, force: False, partial: False
-   ancestor: d1d156401c1b, local: 6373bbfdae1d+, remote: e673248094b1
+   ancestor: 40663881a6dd, local: 3b08d01b0ab5+, remote: adfe50279922
    f1: versions differ -> m
    f2: remote unchanged -> k
   
   auction for merging merge bids
-   d2/f3: consensus for dg
-   d2/f4: consensus for m
    f1: picking 'get' action
    f2: picking 'keep' action
   end of auction
   
-   preserving d2/f4 for resolve of d2/f4
    f1: remote is newer -> g
   getting f1
    f2: remote unchanged -> k
-   d2/f3: local directory rename - get from d1/f3 -> dg
-  getting d1/f3 to d2/f3
-   d2/f4: local directory rename, both created -> m (premerge)
-  3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   (branch merge, don't forget to commit)
 
 Test the greatest common ancestor returning multiple changesets
@@ -418,7 +276,7 @@
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     1 first change f1
   
-  changeset:   2:d1d156401c1b
+  changeset:   2:40663881a6dd
   parent:      0:40494bf2444c
   user:        test
   date:        Thu Jan 01 00:00:00 1970 +0000
@@ -513,3 +371,80 @@
   2
 
   $ cd ..
+
+  $ hg init issue5020
+  $ cd issue5020
+
+  $ echo a > noop
+  $ hg ci -qAm initial
+
+  $ echo b > noop
+  $ hg ci -qAm 'uninteresting change'
+
+  $ hg up -q 0
+  $ mkdir d1
+  $ echo a > d1/a
+  $ echo b > d1/b
+  $ hg ci -qAm 'add d1/a and d1/b'
+
+  $ hg merge -q 1
+  $ hg rm d1/a
+  $ hg mv -q d1 d2
+  $ hg ci -qm 'merge while removing d1/a and moving d1/b to d2/b'
+
+  $ hg up -q 1
+  $ hg merge -q 2
+  $ hg ci -qm 'merge (no changes while merging)'
+  $ hg log -G -T '{rev}:{node|short} {desc}'
+  @    4:c0ef19750a22 merge (no changes while merging)
+  |\
+  +---o  3:6ca01f7342b9 merge while removing d1/a and moving d1/b to d2/b
+  | |/
+  | o  2:154e6000f54e add d1/a and d1/b
+  | |
+  o |  1:11b5b303e36c uninteresting change
+  |/
+  o  0:7b54db1ebf33 initial
+  
+  $ hg merge 3 --debug
+  note: merging c0ef19750a22+ and 6ca01f7342b9 using bids from ancestors 11b5b303e36c and 154e6000f54e
+  
+  calculating bids for ancestor 11b5b303e36c
+    unmatched files in local:
+     d1/a
+     d1/b
+    unmatched files in other:
+     d2/b
+  resolving manifests
+   branchmerge: True, force: False, partial: False
+   ancestor: 11b5b303e36c, local: c0ef19750a22+, remote: 6ca01f7342b9
+   d2/b: remote created -> g
+  
+  calculating bids for ancestor 154e6000f54e
+    unmatched files in other:
+     d2/b
+    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: 'd1/b' -> dst: 'd2/b' 
+    checking for directory renames
+     discovered dir src: 'd1/' -> dst: 'd2/'
+  resolving manifests
+   branchmerge: True, force: False, partial: False
+   ancestor: 154e6000f54e, local: c0ef19750a22+, remote: 6ca01f7342b9
+   d1/a: other deleted -> r
+   d1/b: other deleted -> r
+   d2/b: remote created -> g
+  
+  auction for merging merge bids
+   d1/a: consensus for r
+   d1/b: consensus for r
+   d2/b: consensus for g
+  end of auction
+  
+   d1/a: other deleted -> r
+  removing d1/a
+   d1/b: other deleted -> r
+  removing d1/b
+   d2/b: remote created -> g
+  getting d2/b
+  1 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
--- a/tests/test-merge-types.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-merge-types.t	Thu May 09 18:37:37 2019 -0400
@@ -30,7 +30,6 @@
 Symlink is local parent, executable is other:
 
   $ hg merge --debug
-    searching for copies back to rev 1
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: c334dc3be0da, local: 521a1e40188f+, remote: 3574f3e69b1c
@@ -63,7 +62,6 @@
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
   $ hg merge --debug --tool :union
-    searching for copies back to rev 1
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
@@ -86,7 +84,6 @@
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
   $ hg merge --debug --tool :merge3
-    searching for copies back to rev 1
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
@@ -109,7 +106,6 @@
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
   $ hg merge --debug --tool :merge-local
-    searching for copies back to rev 1
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
@@ -131,7 +127,6 @@
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
   $ hg merge --debug --tool :merge-other
-    searching for copies back to rev 1
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: c334dc3be0da, local: 3574f3e69b1c+, remote: 521a1e40188f
@@ -165,7 +160,6 @@
   $ hg up -Cq 0
   $ echo data > a
   $ HGMERGE= hg up -y --debug --config ui.merge=
-    searching for copies back to rev 2
   resolving manifests
    branchmerge: False, force: False, partial: False
    ancestor: c334dc3be0da, local: c334dc3be0da+, remote: 521a1e40188f
--- a/tests/test-merge7.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-merge7.t	Thu May 09 18:37:37 2019 -0400
@@ -81,7 +81,6 @@
   new changesets 40d11a4173a8
   (run 'hg heads' to see heads, 'hg merge' to merge)
   $ hg merge --debug
-    searching for copies back to rev 1
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 96b70246a118, local: 50c3a7e29886+, remote: 40d11a4173a8
--- a/tests/test-mv-cp-st-diff.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-mv-cp-st-diff.t	Thu May 09 18:37:37 2019 -0400
@@ -1624,6 +1624,13 @@
   $ echo change > f
   $ hg ci -m 'change f'
 
+Make a rename because we want to track renames. It is also important that the
+faulty linkrev is not only the "start" commit to ensure the linkrev will be
+used.
+
+  $ hg mv f renamed
+  $ hg ci -m renamed
+
 Make a second branch, we use a named branch to create a simple commit
 that does not touch f.
 
@@ -1631,31 +1638,28 @@
   $ hg branch -q dev
   $ hg ci -Aqm dev
 
-Graft the initial change, as f was untouched, we reuse the same entry and the
-linkrev point to the older branch.
+Graft the initial change and the rename. As f was untouched, we reuse the same
+entry and the linkrev point to the older branch.
 
   $ hg graft -q 'desc(change)'
-
-Make a rename because we want to track renames. It is also important that the
-faulty linkrev is not the "start" commit to ensure the linkrev will be used.
-
-  $ hg mv f renamed
-  $ hg ci -m renamed
+  $ hg graft -q 'desc(renamed)'
 
   $ hg log -G -T '{rev} {desc}'
-  @  4 renamed
+  @  5 renamed
+  |
+  o  4 change f
   |
-  o  3 change f
+  o  3 dev
   |
-  o  2 dev
-  |
+  | o  2 renamed
+  | |
   | o  1 change f
   |/
   o  0 empty f
   
 
-The copy tracking should still reach rev 2 (branch creation).
-accessing the parent of 4 (renamed) should not jump use to revision 1.
+The copy tracking should still reach rev 3 (branch creation).
+accessing the parent of 5 (renamed) should not jump use to revision 1.
 
   $ hg diff --git -r 'desc(dev)' -r .
   diff --git a/f b/renamed
@@ -1669,11 +1673,11 @@
 Check debug output for copy tracing
 
   $ hg status --copies --rev 'desc(dev)' --rev . --config devel.debug.copies=yes --debug
-  debug.copies: searching copies from a51f36ab1704 to 7935fd48a8f9
+  debug.copies: searching copies from a51f36ab1704 to 1f4aa1fd627b
   debug.copies: search mode: forward
-  debug.copies:    looking into rename from a51f36ab1704 to 7935fd48a8f9
-  debug.copies:      search limit: 2
-  debug.copies:      missing file to search: 1
+  debug.copies:    looking into rename from a51f36ab1704 to 1f4aa1fd627b
+  debug.copies:      search limit: 3
+  debug.copies:      missing files to search: 1
   debug.copies:        tracing file: renamed
   debug.copies:          rename of: f
   debug.copies:          time: * seconds (glob)
@@ -1681,4 +1685,11 @@
     f
   R f
 
+Check that merging across the rename works
+
+  $ echo modified >> renamed
+  $ hg co -m 4
+  merging renamed and f to f
+  0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+
   $ cd ..
--- a/tests/test-narrow-share.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-narrow-share.t	Thu May 09 18:37:37 2019 -0400
@@ -28,6 +28,9 @@
   $ hg clone --narrow ssh://user@dummy/remote main -q \
   > --include d1 --include d3 --include d5 --include d7
 
+Ignore file called "ignored"
+  $ echo ignored > main/.hgignore
+
   $ hg share main share
   updating working directory
   4 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -55,15 +58,19 @@
 # Make d3/f dirty
   $ echo x >> main/d3/f
   $ echo y >> main/d3/g
+  $ touch main/d3/ignored
+  $ touch main/d3/untracked
   $ hg add main/d3/g
   $ hg -R main st
   M d3/f
   A d3/g
+  ? d3/untracked
 # Make d5/f not match the dirstate timestamp even though it's clean
   $ sleep 2
   $ hg -R main st
   M d3/f
   A d3/g
+  ? d3/untracked
   $ hg -R main debugdirstate --no-dates
   n 644          2 set                 d1/f
   n 644          2 set                 d3/f
@@ -91,6 +98,8 @@
   not deleting possibly dirty file d3/f
   not deleting possibly dirty file d3/g
   not deleting possibly dirty file d5/f
+  not deleting unknown file d3/untracked
+  not deleting ignored file d3/ignored
 # d1/f, d3/f, d3/g and d5/f should no longer be reported
   $ hg -R main files
   main/d7/f
@@ -99,6 +108,8 @@
   $ find main/d* -type f | sort
   main/d3/f
   main/d3/g
+  main/d3/ignored
+  main/d3/untracked
   main/d5/f
   main/d7/f
 
@@ -131,6 +142,8 @@
   $ hg -R main st --all
   M d3/f
   ? d3/g
+  ? d3/untracked
+  I d3/ignored
   C d1/f
   C d7/f
 
--- a/tests/test-narrow-update.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-narrow-update.t	Thu May 09 18:37:37 2019 -0400
@@ -72,5 +72,5 @@
 
   $ hg mv inside/f1 inside/f2
   $ hg update -q 'desc("modify outside")'
-  $ hg update -q 'desc("initial")'
+  $ hg update -q 'desc("add inside and outside")'
   $ hg update -q 'desc("modify inside")'
--- a/tests/test-phabricator.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-phabricator.t	Thu May 09 18:37:37 2019 -0400
@@ -32,6 +32,8 @@
 Basic phabread:
   $ hg phabread --test-vcr "$VCR/phabread-4480.json" D4480 | head
   # HG changeset patch
+  # Date 1536771503 0
+  # Parent  a5de21c9e3703f8e8eb064bd7d893ff2f703c66a
   exchangev2: start to implement pull with wire protocol v2
   
   Wire protocol version 2 will take a substantially different
@@ -39,8 +41,6 @@
   is concerned).
   
   This commit establishes a new exchangev2 module for holding
-  code related to exchange using wire protocol v2. I could have
-  added things to the existing exchange module. But it is already
 
 phabupdate with an accept:
   $ hg phabupdate --accept D4564 \
--- a/tests/test-push-warn.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-push-warn.t	Thu May 09 18:37:37 2019 -0400
@@ -791,3 +791,57 @@
   [255]
 
   $ cd ..
+
+Test regarding pushing of closed branch/branches(Issue6080)
+
+  $ hg init x
+  $ cd x
+  $ hg -q branch a
+  $ echo 0 > foo
+  $ hg -q ci -Am 0
+  $ hg -q up 0
+  $ cd ..
+
+  $ hg -q clone x z
+  $ cd z
+
+When there is a single closed branch
+
+  $ hg -q branch foo
+  $ echo 0 > foo
+  $ hg -q ci -Am 0
+  $ hg ci --close-branch -m 'closing branch foo'
+  $ hg -q up 0
+  $ hg push ../x
+  pushing to ../x
+  searching for changes
+  abort: push creates new remote branches: foo (1 closed)!
+  (use 'hg push --new-branch' to create new remote branches)
+  [255]
+
+When there is more than one closed branches
+  $ hg -q branch bar
+  $ echo 0 > bar
+  $ hg -q ci -Am 0
+  $ hg ci --close-branch -m 'closing branch bar'
+  $ hg -q up 0
+  $ hg push ../x
+  pushing to ../x
+  searching for changes
+  abort: push creates new remote branches: bar, foo (2 closed)!
+  (use 'hg push --new-branch' to create new remote branches)
+  [255]
+
+When there are more than one new branches and not all are closed
+  $ hg -q branch bar1
+  $ echo 0 > bar1
+  $ hg -q ci -Am 0
+  $ hg -q up 0
+  $ hg push ../x
+  pushing to ../x
+  searching for changes
+  abort: push creates new remote branches: bar, bar1, foo (2 closed)!
+  (use 'hg push --new-branch' to create new remote branches)
+  [255]
+
+  $ cd ..
--- a/tests/test-rebase-conflicts.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-rebase-conflicts.t	Thu May 09 18:37:37 2019 -0400
@@ -248,9 +248,6 @@
   getting f1.txt
    merge against 9:e31216eec445
      detach base 8:8e4e2c1a07ae
-    searching for copies back to rev 3
-    unmatched files in other (from topological common ancestor):
-     f2.txt
   resolving manifests
    branchmerge: True, force: True, partial: False
    ancestor: 8e4e2c1a07ae, local: 4bc80088dc6b+, remote: e31216eec445
@@ -268,9 +265,6 @@
    already in destination
    merge against 10:2f2496ddf49d
      detach base 9:e31216eec445
-    searching for copies back to rev 3
-    unmatched files in other (from topological common ancestor):
-     f2.txt
   resolving manifests
    branchmerge: True, force: True, partial: False
    ancestor: e31216eec445, local: 19c888675e13+, remote: 2f2496ddf49d
--- a/tests/test-remotefilelog-sparse.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-remotefilelog-sparse.t	Thu May 09 18:37:37 2019 -0400
@@ -98,12 +98,5 @@
   $ clearcache
   $ hg prefetch -r '. + .^' -I x -I z
   4 files fetched over 1 fetches - (4 misses, 0.00% hit ratio) over * (glob)
-Originally this was testing that the rebase doesn't fetch pointless
-blobs. Right now it fails because core's sparse can't load a spec from
-the working directory. Presumably there's a fix, but I'm not sure what it is.
   $ hg rebase -d 2 --keep
   rebasing 1:876b1317060d "x2" (foo)
-  transaction abort!
-  rollback completed
-  abort: cannot parse sparse patterns from working directory
-  [255]
--- a/tests/test-rename-dir-merge.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-rename-dir-merge.t	Thu May 09 18:37:37 2019 -0400
@@ -24,7 +24,6 @@
   created new head
 
   $ hg merge --debug 1
-    searching for copies back to rev 1
     unmatched files in local:
      a/c
     unmatched files in other:
@@ -70,7 +69,6 @@
   $ hg co -C 1
   0 files updated, 0 files merged, 1 files removed, 0 files unresolved
   $ hg merge --debug 2
-    searching for copies back to rev 1
     unmatched files in local:
      b/a
      b/b
--- a/tests/test-rename-merge1.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-rename-merge1.t	Thu May 09 18:37:37 2019 -0400
@@ -22,7 +22,6 @@
   created new head
 
   $ hg merge -y --debug
-    searching for copies back to rev 1
     unmatched files in local:
      c2
     unmatched files in other:
@@ -76,7 +75,7 @@
   $ hg cp b b3
   $ hg cp b b4
   $ hg ci -A -m 'copy b twice'
-  $ hg up eb92d88a9712
+  $ hg up '.^'
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
   $ hg up
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -88,7 +87,7 @@
   $ hg cp b b3
   $ hg mv b b4
   $ hg ci -A -m 'divergent renames in same changeset'
-  $ hg up c761c6948de0
+  $ hg up '.^'
   1 files updated, 0 files merged, 2 files removed, 0 files unresolved
   $ hg up
   2 files updated, 0 files merged, 1 files removed, 0 files unresolved
@@ -168,7 +167,6 @@
   $ hg commit -m "deleted file"
   created new head
   $ hg merge --debug
-    searching for copies back to rev 1
     unmatched files in other:
      newfile
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
@@ -186,3 +184,52 @@
   $ hg status
   M newfile
   $ cd ..
+
+Check that file is considered unrelated when deleted and recreated
+
+  $ hg init unrelated
+  $ cd unrelated
+  $ echo foo > file
+  $ hg add file
+  $ hg commit -m "added file"
+  $ hg cp file newfile
+  $ hg commit -m "copy file"
+  $ hg update 0
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg rm file
+  $ hg commit -m "deleted file"
+  created new head
+  $ echo bar > file
+  $ hg add file
+  $ hg ci -m 'recreate file'
+  $ hg log -G -T '{rev} {desc}\n'
+  @  3 recreate file
+  |
+  o  2 deleted file
+  |
+  | o  1 copy file
+  |/
+  o  0 added file
+  
+BROKEN: this is inconsistent with `hg merge` (below), which doesn't consider
+'file' renamed same since it was deleted for a while
+  $ hg st --copies --rev 3 --rev 1
+  M file
+  A newfile
+    file
+  $ hg merge --debug 1
+    unmatched files in other:
+     newfile
+    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: 'file' -> dst: 'newfile' 
+    checking for directory renames
+  resolving manifests
+   branchmerge: True, force: False, partial: False
+   ancestor: 19d7f95df299, local: 4e4a42b1cbdf+, remote: 45b14aae7432
+   newfile: remote created -> g
+  getting newfile
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ hg status
+  M newfile
+  $ cd ..
--- a/tests/test-rename-merge2.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-rename-merge2.t	Thu May 09 18:37:37 2019 -0400
@@ -68,7 +68,6 @@
   >         hg add $2 2> /dev/null
   >     fi
   > }
-  $ uc() { up $1; hg cp $1 $2; } # update + copy
   $ um() { up $1; hg mv $1 $2; }
   $ nc() { hg cp $1 $2; } # just copy
   $ nm() { hg mv $1 $2; } # just move
@@ -77,7 +76,6 @@
   --------------
   test L:up a   R:nc a b W:       - 1  get local a to b
   --------------
-    searching for copies back to rev 1
     unmatched files in other:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
@@ -116,7 +114,6 @@
   --------------
   test L:nc a b R:up a   W:       - 2  get rem change to a and b
   --------------
-    searching for copies back to rev 1
     unmatched files in local:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
@@ -156,7 +153,6 @@
   --------------
   test L:up a   R:nm a b W:       - 3  get local a change to b, remove a
   --------------
-    searching for copies back to rev 1
     unmatched files in other:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
@@ -195,7 +191,6 @@
   --------------
   test L:nm a b R:up a   W:       - 4  get remote change to b
   --------------
-    searching for copies back to rev 1
     unmatched files in local:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
@@ -233,7 +228,6 @@
   --------------
   test L:       R:nc a b W:       - 5  get b
   --------------
-    searching for copies back to rev 1
     unmatched files in other:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
@@ -266,7 +260,6 @@
   --------------
   test L:nc a b R:       W:       - 6  nothing
   --------------
-    searching for copies back to rev 1
     unmatched files in local:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
@@ -298,7 +291,6 @@
   --------------
   test L:       R:nm a b W:       - 7  get b
   --------------
-    searching for copies back to rev 1
     unmatched files in other:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
@@ -332,7 +324,6 @@
   --------------
   test L:nm a b R:       W:       - 8  nothing
   --------------
-    searching for copies back to rev 1
     unmatched files in local:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
@@ -363,9 +354,9 @@
   --------------
   test L:um a b R:um a b W:       - 9  do merge with ancestor in a
   --------------
-    searching for copies back to rev 1
-    unmatched files new in both:
-     b
+    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: 'a' -> dst: 'b' *
+    checking for directory renames
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 62e7bf090eba+, remote: 49b6d8032493
@@ -404,7 +395,6 @@
   --------------
   test L:nm a b R:nm a c W:       - 11 get c, keep b
   --------------
-    searching for copies back to rev 1
     unmatched files in local:
      b
     unmatched files in other:
@@ -443,9 +433,9 @@
   --------------
   test L:nc a b R:up b   W:       - 12 merge b no ancestor
   --------------
-    searching for copies back to rev 1
-    unmatched files new in both:
-     b
+    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: 'a' -> dst: 'b' 
+    checking for directory renames
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 86a2aa42fc76+, remote: af30c7647fc7
@@ -482,9 +472,9 @@
   --------------
   test L:up b   R:nm a b W:       - 13 merge b no ancestor
   --------------
-    searching for copies back to rev 1
-    unmatched files new in both:
-     b
+    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: 'a' -> dst: 'b' 
+    checking for directory renames
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
@@ -522,9 +512,9 @@
   --------------
   test L:nc a b R:up a b W:       - 14 merge b no ancestor
   --------------
-    searching for copies back to rev 1
-    unmatched files new in both:
-     b
+    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: 'a' -> dst: 'b' 
+    checking for directory renames
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
@@ -562,9 +552,9 @@
   --------------
   test L:up b   R:nm a b W:       - 15 merge b no ancestor, remove a
   --------------
-    searching for copies back to rev 1
-    unmatched files new in both:
-     b
+    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: 'a' -> dst: 'b' 
+    checking for directory renames
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a
@@ -602,9 +592,9 @@
   --------------
   test L:nc a b R:up a b W:       - 16 get a, merge b no ancestor
   --------------
-    searching for copies back to rev 1
-    unmatched files new in both:
-     b
+    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: 'a' -> dst: 'b' 
+    checking for directory renames
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a
@@ -642,9 +632,9 @@
   --------------
   test L:up a b R:nc a b W:       - 17 keep a, merge b no ancestor
   --------------
-    searching for copies back to rev 1
-    unmatched files new in both:
-     b
+    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: 'a' -> dst: 'b' 
+    checking for directory renames
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 0b76e65c8289+, remote: 4ce40f5aca24
@@ -681,9 +671,9 @@
   --------------
   test L:nm a b R:up a b W:       - 18 merge b no ancestor
   --------------
-    searching for copies back to rev 1
-    unmatched files new in both:
-     b
+    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: 'a' -> dst: 'b' 
+    checking for directory renames
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 02963e448370+, remote: 8dbce441892a
@@ -726,9 +716,9 @@
   --------------
   test L:up a b R:nm a b W:       - 19 merge b no ancestor, prompt remove a
   --------------
-    searching for copies back to rev 1
-    unmatched files new in both:
-     b
+    all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: 'a' -> dst: 'b' 
+    checking for directory renames
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 924404dff337, local: 0b76e65c8289+, remote: bdb19105162a
@@ -772,7 +762,6 @@
   --------------
   test L:up a   R:um a b W:       - 20 merge a and b to b, remove a
   --------------
-    searching for copies back to rev 1
     unmatched files in other:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
@@ -815,7 +804,6 @@
   --------------
   test L:um a b R:up a   W:       - 21 merge a and b to b
   --------------
-    searching for copies back to rev 1
     unmatched files in local:
      b
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
@@ -860,7 +848,6 @@
   --------------
   test L:nm a b R:up a c W:       - 23 get c, keep b
   --------------
-    searching for copies back to rev 1
     unmatched files in local:
      b
     unmatched files in other:
@@ -941,7 +928,6 @@
   $ echo m > 7/f
   $ echo m > 8/f
   $ hg merge -f --tool internal:dump -v --debug -r2 | sed '/^resolving manifests/,$d' 2> /dev/null
-    searching for copies back to rev 1
     unmatched files in local:
      5/g
      6/g
@@ -949,10 +935,8 @@
      3/g
      4/g
      7/f
-    unmatched files new in both:
-     0/f
-     1/g
     all copies found (* = to merge, ! = divergent, % = renamed and deleted):
+     src: '1/f' -> dst: '1/g' *
      src: '3/f' -> dst: '3/g' *
      src: '4/f' -> dst: '4/g' *
      src: '5/f' -> dst: '5/g' *
--- a/tests/test-revset.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-revset.t	Thu May 09 18:37:37 2019 -0400
@@ -1962,25 +1962,26 @@
   2147483647
 
 Test working-directory integer revision and node id
-(BUG: '0:wdir()' is still needed to populate wdir revision)
 
-  $ hg debugrevspec '0:wdir() & 2147483647'
+  $ hg debugrevspec '2147483647'
   2147483647
-  $ hg debugrevspec '0:wdir() & rev(2147483647)'
+  $ hg debugrevspec 'rev(2147483647)'
+  2147483647
+  $ hg debugrevspec 'ffffffffffffffffffffffffffffffffffffffff'
   2147483647
-  $ hg debugrevspec '0:wdir() & ffffffffffffffffffffffffffffffffffffffff'
+  $ hg debugrevspec 'ffffffffffff'
   2147483647
-  $ hg debugrevspec '0:wdir() & ffffffffffff'
+  $ hg debugrevspec 'id(ffffffffffffffffffffffffffffffffffffffff)'
   2147483647
-  $ hg debugrevspec '0:wdir() & id(ffffffffffffffffffffffffffffffffffffffff)'
+  $ hg debugrevspec 'id(ffffffffffff)'
   2147483647
-  $ hg debugrevspec '0:wdir() & id(ffffffffffff)'
+  $ hg debugrevspec 'ffffffffffff+000000000000'
   2147483647
+  -1
 
   $ cd ..
 
 Test short 'ff...' hash collision
-(BUG: '0:wdir()' is still needed to populate wdir revision)
 
   $ hg init wdir-hashcollision
   $ cd wdir-hashcollision
@@ -2006,21 +2007,21 @@
   $ hg debugobsolete fffbae3886c8fbb2114296380d276fd37715d571
   obsoleted 1 changesets
 
-  $ hg debugrevspec '0:wdir() & fff'
+  $ hg debugrevspec 'fff'
   abort: 00changelog.i@fff: ambiguous identifier!
   [255]
-  $ hg debugrevspec '0:wdir() & ffff'
+  $ hg debugrevspec 'ffff'
   abort: 00changelog.i@ffff: ambiguous identifier!
   [255]
-  $ hg debugrevspec '0:wdir() & fffb'
+  $ hg debugrevspec 'fffb'
   abort: 00changelog.i@fffb: ambiguous identifier!
   [255]
 BROKEN should be '2' (node lookup uses unfiltered repo)
-  $ hg debugrevspec '0:wdir() & id(fffb)'
+  $ hg debugrevspec 'id(fffb)'
 BROKEN should be '2' (node lookup uses unfiltered repo)
-  $ hg debugrevspec '0:wdir() & ffff8'
+  $ hg debugrevspec 'ffff8'
   4
-  $ hg debugrevspec '0:wdir() & fffff'
+  $ hg debugrevspec 'fffff'
   2147483647
 
   $ cd ..
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-rust-discovery.py	Thu May 09 18:37:37 2019 -0400
@@ -0,0 +1,111 @@
+from __future__ import absolute_import
+import unittest
+
+try:
+    from mercurial import rustext
+    rustext.__name__  # trigger immediate actual import
+except ImportError:
+    rustext = None
+else:
+    # this would fail already without appropriate ancestor.__package__
+    from mercurial.rustext.discovery import (
+        PartialDiscovery,
+    )
+
+try:
+    from mercurial.cext import parsers as cparsers
+except ImportError:
+    cparsers = None
+
+# picked from test-parse-index2, copied rather than imported
+# so that it stays stable even if test-parse-index2 changes or disappears.
+data_non_inlined = (
+    b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01D\x19'
+    b'\x00\x07e\x12\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff'
+    b'\xff\xff\xff\xff\xd1\xf4\xbb\xb0\xbe\xfc\x13\xbd\x8c\xd3\x9d'
+    b'\x0f\xcd\xd9;\x8c\x07\x8cJ/\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+    b'\x00\x00\x00\x00\x00\x00\x01D\x19\x00\x00\x00\x00\x00\xdf\x00'
+    b'\x00\x01q\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\xff'
+    b'\xff\xff\xff\xc1\x12\xb9\x04\x96\xa4Z1t\x91\xdfsJ\x90\xf0\x9bh'
+    b'\x07l&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+    b'\x00\x01D\xf8\x00\x00\x00\x00\x01\x1b\x00\x00\x01\xb8\x00\x00'
+    b'\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\xff\xff\xff\xff\x02\n'
+    b'\x0e\xc6&\xa1\x92\xae6\x0b\x02i\xfe-\xe5\xbao\x05\xd1\xe7\x00'
+    b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01F'
+    b'\x13\x00\x00\x00\x00\x01\xec\x00\x00\x03\x06\x00\x00\x00\x01'
+    b'\x00\x00\x00\x03\x00\x00\x00\x02\xff\xff\xff\xff\x12\xcb\xeby1'
+    b'\xb6\r\x98B\xcb\x07\xbd`\x8f\x92\xd9\xc4\x84\xbdK\x00\x00\x00'
+    b'\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+    )
+
+
+@unittest.skipIf(rustext is None or cparsers is None,
+                 "rustext or the C Extension parsers module "
+                 "discovery relies on is not available")
+class rustdiscoverytest(unittest.TestCase):
+    """Test the correctness of binding to Rust code.
+
+    This test is merely for the binding to Rust itself: extraction of
+    Python variable, giving back the results etc.
+
+    It is not meant to test the algorithmic correctness of the provided
+    methods. Hence the very simple embedded index data is good enough.
+
+    Algorithmic correctness is asserted by the Rust unit tests.
+    """
+
+    def parseindex(self):
+        return cparsers.parse_index2(data_non_inlined, False)[0]
+
+    def testindex(self):
+        idx = self.parseindex()
+        # checking our assumptions about the index binary data:
+        self.assertEqual({i: (r[5], r[6]) for i, r in enumerate(idx)},
+                         {0: (-1, -1),
+                          1: (0, -1),
+                          2: (1, -1),
+                          3: (2, -1)})
+
+    def testaddcommonsmissings(self):
+        idx = self.parseindex()
+        disco = PartialDiscovery(idx, [3])
+        self.assertFalse(disco.hasinfo())
+        self.assertFalse(disco.iscomplete())
+
+        disco.addcommons([1])
+        self.assertTrue(disco.hasinfo())
+        self.assertFalse(disco.iscomplete())
+
+        disco.addmissings([2])
+        self.assertTrue(disco.hasinfo())
+        self.assertTrue(disco.iscomplete())
+
+        self.assertEqual(disco.commonheads(), {1})
+
+    def testaddmissingsstats(self):
+        idx = self.parseindex()
+        disco = PartialDiscovery(idx, [3])
+        self.assertIsNone(disco.stats()['undecided'], None)
+
+        disco.addmissings([2])
+        self.assertEqual(disco.stats()['undecided'], 2)
+
+    def testaddinfocommonfirst(self):
+        idx = self.parseindex()
+        disco = PartialDiscovery(idx, [3])
+        disco.addinfo([(1, True), (2, False)])
+        self.assertTrue(disco.hasinfo())
+        self.assertTrue(disco.iscomplete())
+        self.assertEqual(disco.commonheads(), {1})
+
+    def testaddinfomissingfirst(self):
+        idx = self.parseindex()
+        disco = PartialDiscovery(idx, [3])
+        disco.addinfo([(2, False), (1, True)])
+        self.assertTrue(disco.hasinfo())
+        self.assertTrue(disco.iscomplete())
+        self.assertEqual(disco.commonheads(), {1})
+
+if __name__ == '__main__':
+    import silenttestrunner
+    silenttestrunner.main(__name__)
--- a/tests/test-server-view.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-server-view.t	Thu May 09 18:37:37 2019 -0400
@@ -34,5 +34,30 @@
   date:        Thu Jan 01 00:00:00 1970 +0000
   summary:     r0
   
+
+Check same result using `experimental.extra-filter-revs`
+
+  $ hg -R test --config experimental.extra-filter-revs='not public()' serve -p $HGPORT1 -d --pid-file=hg2.pid -E errors.log
+  $ cat hg2.pid >> $DAEMON_PIDS
+  $ hg -R test2 incoming http://foo:xyzzy@localhost:$HGPORT1/
+  comparing with http://foo:***@localhost:$HGPORT1/
+  changeset:   0:1ea73414a91b
+  tag:         tip
+  user:        debugbuilddag
+  date:        Thu Jan 01 00:00:00 1970 +0000
+  summary:     r0
+  
+  $ hg -R test --config experimental.extra-filter-revs='not public()' debugupdatecache
+  $ ls -1 test/.hg/cache/
+  branch2-base%89c45d2fa07e
+  branch2-served
+  hgtagsfnodes1
+  rbc-names-v1
+  rbc-revs-v1
+  tags2
+  tags2-served%89c45d2fa07e
+
+cleanup
+
   $ cat errors.log
   $ killdaemons.py
--- a/tests/test-setdiscovery.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-setdiscovery.t	Thu May 09 18:37:37 2019 -0400
@@ -926,7 +926,7 @@
   common heads: 7ead0cba2838
 
 
-One with >200 heads, which used to use up all of the sample:
+One with >200 heads. We now switch to send them all in the initial roundtrip, but still do sampling for the later request.
 
   $ hg init manyheads
   $ cd manyheads
@@ -974,20 +974,17 @@
   searching for changes
   taking quick initial sample
   searching: 2 queries
-  query 2; still undecided: 1240, sample size is: 100
+  query 2; still undecided: 1080, sample size is: 100
   sampling from both directions
   searching: 3 queries
-  query 3; still undecided: 1140, sample size is: 200
+  query 3; still undecided: 980, sample size is: 200
   sampling from both directions
   searching: 4 queries
   query 4; still undecided: \d+, sample size is: 200 (re)
   sampling from both directions
   searching: 5 queries
-  query 5; still undecided: \d+, sample size is: 200 (re)
-  sampling from both directions
-  searching: 6 queries
-  query 6; still undecided: \d+, sample size is: \d+ (re)
-  6 total queries in *.????s (glob)
+  query 5; still undecided: 195, sample size is: 195
+  5 total queries in *.????s (glob)
   elapsed time:  * seconds (glob)
   heads summary:
     total common heads:          1
@@ -1116,6 +1113,6 @@
   $ hg -R r1 --config extensions.blackbox= blackbox --config blackbox.track=
   * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> serve --cmdserver chgunix * (glob) (chg !)
   * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> -R r1 outgoing r2 *-T{rev} * --config *extensions.blackbox=* (glob)
-  * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> found 101 common and 1 unknown server heads, 2 roundtrips in *.????s (glob)
+  * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> found 101 common and 1 unknown server heads, 1 roundtrips in *.????s (glob)
   * @5d0b986a083e0d91f116de4691e2aaa54d5bbec0 (*)> -R r1 outgoing r2 *-T{rev} * --config *extensions.blackbox=* exited 0 after *.?? seconds (glob)
   $ cd ..
--- a/tests/test-subrepo.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-subrepo.t	Thu May 09 18:37:37 2019 -0400
@@ -274,7 +274,6 @@
   $ hg ci -m9
   created new head
   $ hg merge 6 --debug # test change
-    searching for copies back to rev 2
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 1f14a2e2d3ec, local: f0d2028bf86d+, remote: 1831e14459c4
@@ -301,7 +300,6 @@
   $ hg ci -m10
   committing subrepository t
   $ HGMERGE=internal:merge hg merge --debug 7 # test conflict
-    searching for copies back to rev 2
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 1831e14459c4, local: e45c8b14af55+, remote: f94576341bcf
@@ -313,7 +311,6 @@
   starting 4 threads for background file closing (?)
   (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m
   merging subrepository "t"
-    searching for copies back to rev 2
   resolving manifests
    branchmerge: True, force: False, partial: False
    ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198
--- a/tests/test-tags.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-tags.t	Thu May 09 18:37:37 2019 -0400
@@ -145,7 +145,7 @@
   $ hg blackbox -l 6
   1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> identify
   1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> writing 48 bytes to cache/hgtagsfnodes1
-  1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> 0/1 cache hits/lookups in * seconds (glob)
+  1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> 0/2 cache hits/lookups in * seconds (glob)
   1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> writing .hg/cache/tags2-visible with 1 tags
   1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> identify exited 0 after * seconds (glob)
   1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> blackbox -l 6
@@ -159,7 +159,7 @@
   $ hg blackbox -l 6
   1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> identify
   1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> not writing .hg/cache/hgtagsfnodes1 because lock cannot be acquired
-  1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> 0/1 cache hits/lookups in * seconds (glob)
+  1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> 0/2 cache hits/lookups in * seconds (glob)
   1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> writing .hg/cache/tags2-visible with 1 tags
   1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> identify exited 0 after * seconds (glob)
   1970/01/01 00:00:00 bob @b9154636be938d3d431e75a7c906504a079bfe07 (5000)> blackbox -l 6
@@ -363,7 +363,7 @@
   $ hg blackbox -l 6
   1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> tags
   1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> writing 24 bytes to cache/hgtagsfnodes1
-  1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> 2/3 cache hits/lookups in * seconds (glob)
+  1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> 3/4 cache hits/lookups in * seconds (glob)
   1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> writing .hg/cache/tags2-visible with 1 tags
   1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> tags exited 0 after * seconds (glob)
   1970/01/01 00:00:00 bob @8dbfe60eff306a54259cfe007db9e330e7ecf866 (5000)> blackbox -l 6
@@ -384,7 +384,7 @@
   $ hg blackbox -l 6
   1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> tags
   1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> couldn't write cache/hgtagsfnodes1: [Errno *] * (glob)
-  1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> 2/3 cache hits/lookups in * seconds (glob)
+  1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> 3/4 cache hits/lookups in * seconds (glob)
   1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> writing .hg/cache/tags2-visible with 1 tags
   1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> tags exited 0 after * seconds (glob)
   1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> blackbox -l 6
@@ -399,7 +399,7 @@
   $ hg blackbox -l 6
   1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> tags
   1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> writing 24 bytes to cache/hgtagsfnodes1
-  1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> 2/3 cache hits/lookups in * seconds (glob)
+  1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> 3/4 cache hits/lookups in * seconds (glob)
   1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> writing .hg/cache/tags2-visible with 1 tags
   1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> tags exited 0 after * seconds (glob)
   1970/01/01 00:00:00 bob @b968051b5cf3f624b771779c6d5f84f1d4c3fb5d (5000)> blackbox -l 6
@@ -427,7 +427,7 @@
 
   $ hg blackbox -l 5
   1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> writing 24 bytes to cache/hgtagsfnodes1
-  1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> 2/3 cache hits/lookups in * seconds (glob)
+  1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> 2/4 cache hits/lookups in * seconds (glob)
   1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> writing .hg/cache/tags2-visible with 1 tags
   1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> tags exited 0 after * seconds (glob)
   1970/01/01 00:00:00 bob @0c192d7d5e6b78a714de54a2e9627952a877e25a (5000)> blackbox -l 5
@@ -445,7 +445,7 @@
   $ hg blackbox -l 6
   1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> tags
   1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> writing 24 bytes to cache/hgtagsfnodes1
-  1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> 2/3 cache hits/lookups in * seconds (glob)
+  1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> 3/4 cache hits/lookups in * seconds (glob)
   1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> writing .hg/cache/tags2-visible with 1 tags
   1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> tags exited 0 after * seconds (glob)
   1970/01/01 00:00:00 bob @035f65efb448350f4772141702a81ab1df48c465 (5000)> blackbox -l 6
--- a/tests/test-up-local-change.t	Wed May 08 16:09:50 2019 -0400
+++ b/tests/test-up-local-change.t	Thu May 09 18:37:37 2019 -0400
@@ -40,7 +40,6 @@
   summary:     1
   
   $ hg --debug up
-    searching for copies back to rev 1
     unmatched files in other:
      b
   resolving manifests
@@ -68,9 +67,6 @@
   
   $ hg --debug up 0
   starting 4 threads for background file closing (?)
-    searching for copies back to rev 0
-    unmatched files in local (from topological common ancestor):
-     b
   resolving manifests
    branchmerge: False, force: False, partial: False
    ancestor: 1e71731e6fbb, local: 1e71731e6fbb+, remote: c19d34741b0a
@@ -95,7 +91,6 @@
   summary:     1
   
   $ hg --debug up
-    searching for copies back to rev 1
     unmatched files in other:
      b
   resolving manifests