Patchwork [07,of,14,"] pull: add --remote-hidden option and pass it through peer creation

login
register
mail settings
Submitter Pierre-Yves David
Date April 13, 2019, 11:40 p.m.
Message ID <302c42682e7dbc2c32ef.1555198837@nodosa.octopoid.net>
Download mbox | patch
Permalink /patch/39605/
State Accepted
Headers show

Comments

Pierre-Yves David - April 13, 2019, 11:40 p.m.
# HG changeset patch
# User Manuel Jacob <me@manueljacob.de>
# Date 1554394050 -7200
#      Thu Apr 04 18:07:30 2019 +0200
# Node ID 302c42682e7dbc2c32ef60c07ed2e7d8e59c0ca6
# Parent  8abe74c8c131e862ec35cc9ab35c7f853033d739
# EXP-Topic hgweb-obsolete
# Available At https://bitbucket.org/octobus/mercurial-devel/
#              hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 302c42682e7d
pull: add --remote-hidden option and pass it through peer creation

This option will allow to pull changesets that are remotely hidden. This is
useful when looking into a changeset's evolution history, resolving evolution
instability and running mirroring.

The option is best effort and will only affect the pull when it can. The option
will be ignored when it cannot be honored.

Support for each types of peers is yet to be implemented. They currently all
warn about this lack of support. The warning code will get removed as peers
gain supports.

The option is still experimental. So we will have a lots of freedom to update
the UI or peers implementation before graduation out of experimental.

Based on changeset by Pierre-Yves David, which added the option.

Patch

diff --git a/hgext/narrow/narrowrepo.py b/hgext/narrow/narrowrepo.py
--- a/hgext/narrow/narrowrepo.py
+++ b/hgext/narrow/narrowrepo.py
@@ -24,8 +24,8 @@  def wraprepo(repo):
             dirstate = super(narrowrepository, self)._makedirstate()
             return narrowdirstate.wrapdirstate(self, dirstate)
 
-        def peer(self):
-            peer = super(narrowrepository, self).peer()
+        def peer(self, *args, **kwds):
+            peer = super(narrowrepository, self).peer(*args, **kwds)
             peer._caps.add(wireprototypes.NARROWCAP)
             peer._caps.add(wireprototypes.ELLIPSESCAP)
             return peer
diff --git a/hgext/schemes.py b/hgext/schemes.py
--- a/hgext/schemes.py
+++ b/hgext/schemes.py
@@ -78,10 +78,12 @@  class ShortRepository(object):
     def __repr__(self):
         return '<ShortRepository: %s>' % self.scheme
 
-    def instance(self, ui, url, create, intents=None, createopts=None):
+    def instance(self, ui, url, create, intents=None, createopts=None,
+                 remotehidden=False):
         url = self.resolve(url)
         return hg._peerlookup(url).instance(ui, url, create, intents=intents,
-                                            createopts=createopts)
+                                            createopts=createopts,
+                                            remotehidden=remotehidden)
 
     def resolve(self, url):
         # Should this use the util.url class, or is manual parsing better?
diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py
--- a/mercurial/bundlerepo.py
+++ b/mercurial/bundlerepo.py
@@ -430,7 +430,13 @@  class bundlerepository(object):
     def cancopy(self):
         return False
 
-    def peer(self):
+    def peer(self, remotehidden=False):
+        if remotehidden:
+
+            msg = _("ignoring `--remote-hidden` request\n"
+                    "(access to hidden changeset for bundle repo peers not "
+                    "supported yet)\n")
+            self.ui.warn(msg)
         return bundlepeer(self)
 
     def getcwd(self):
@@ -447,7 +453,8 @@  class bundlerepository(object):
             self.ui.warn(msg % nodemod.hex(p2))
         return super(bundlerepository, self).setparents(p1, p2)
 
-def instance(ui, path, create, intents=None, createopts=None):
+def instance(ui, path, create, intents=None, createopts=None,
+             remotehidden=False):
     if create:
         raise error.Abort(_('cannot create new bundle repository'))
     # internal config: bundle.mainreporoot
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -4390,6 +4390,8 @@  def postincoming(ui, repo, modheads, opt
     ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
     ('b', 'branch', [], _('a specific branch you would like to pull'),
      _('BRANCH')),
+    ('', 'remote-hidden', False,
+     _("include changeset hidden remotely (EXPERIMENTAL)")),
     ] + remoteopts,
     _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
     helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
@@ -4421,6 +4423,14 @@  def pull(ui, repo, source="default", **o
     Specifying bookmark as ``.`` is equivalent to specifying the active
     bookmark's name.
 
+    .. container:: verbose
+
+        One can use the `--remote-hidden` flag to pull changesets
+        remotely hidden. This flag is "best effort", and will only work
+        if the server supports the feature and is configured to allow
+        the user to access hidden changesets. This option is
+        experimental and backward compatibility is not garanteed.
+
     Returns 0 on success, 1 if an update had unresolved files.
     """
 
@@ -4432,10 +4442,11 @@  def pull(ui, repo, source="default", **o
 
     source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
     ui.status(_('pulling from %s\n') % util.hidepassword(source))
-    other = hg.peer(repo, opts, source)
+    other = hg.peer(repo, opts, source, remotehidden=opts['remote_hidden'])
     try:
         revs, checkout = hg.addbranchrevs(repo, other, branches,
-                                          opts.get('rev'))
+                                          opts.get('rev'),
+                                          remotehidden=opts['remote_hidden'])
 
         pullopargs = {}
 
diff --git a/mercurial/hg.py b/mercurial/hg.py
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -68,8 +68,9 @@  def _local(path):
 
     return isfile and bundlerepo or localrepo
 
-def addbranchrevs(lrepo, other, branches, revs):
-    peer = other.peer() # a courtesy to callers using a localrepo for other
+def addbranchrevs(lrepo, other, branches, revs, remotehidden=False):
+    # a courtesy to callers using a localrepo for other
+    peer = other.peer(remotehidden=remotehidden)
     hashbranch, branches = branches
     if not hashbranch and not branches:
         x = revs or None
@@ -165,10 +166,11 @@  def openpath(ui, path, sendaccept=True):
 wirepeersetupfuncs = []
 
 def _peerorrepo(ui, path, create=False, presetupfuncs=None,
-                intents=None, createopts=None):
+                intents=None, createopts=None, remotehidden=False):
     """return a repository object for the specified path"""
     obj = _peerlookup(path).instance(ui, path, create, intents=intents,
-                                     createopts=createopts)
+                                     createopts=createopts,
+                                     remotehidden=remotehidden)
     ui = getattr(obj, "ui", ui)
     for f in presetupfuncs or []:
         f(ui, obj)
@@ -199,11 +201,13 @@  def repository(ui, path='', create=False
                          (path or peer.url()))
     return repo.filtered('visible')
 
-def peer(uiorrepo, opts, path, create=False, intents=None, createopts=None):
+def peer(uiorrepo, opts, path, create=False, intents=None, createopts=None,
+         remotehidden=False):
     '''return a repository peer for the specified path'''
     rui = remoteui(uiorrepo, opts)
-    return _peerorrepo(rui, path, create, intents=intents,
-                       createopts=createopts).peer()
+    peerorrepo = _peerorrepo(rui, path, create, intents=intents,
+                             createopts=createopts, remotehidden=remotehidden)
+    return peerorrepo.peer(remotehidden=remotehidden)
 
 def defaultdest(source):
     '''return default destination of clone if none is given
diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py
--- a/mercurial/httppeer.py
+++ b/mercurial/httppeer.py
@@ -398,7 +398,12 @@  class httppeer(wireprotov1peer.wirepeer)
     def local(self):
         return None
 
-    def peer(self):
+    def peer(self, remotehidden=False):
+        if remotehidden:
+            msg = _("ignoring `--remote-hidden` request\n"
+                    "(access to hidden changeset for http peers not "
+                    "supported yet)\n")
+            self.ui.warn(msg)
         return self
 
     def canpush(self):
@@ -774,7 +779,12 @@  class httpv2peer(object):
     def local(self):
         return None
 
-    def peer(self):
+    def peer(self, remotehidden=False):
+        if remotehidden:
+            msg = _("ignoring `--remote-hidden` request\n"
+                    "(access to hidden changeset for http peers not "
+                    "supported yet)\n")
+            self.ui.warn(msg)
         return self
 
     def canpush(self):
@@ -938,7 +948,8 @@  def performhandshake(ui, url, opener, re
 
     return respurl, info
 
-def makepeer(ui, path, opener=None, requestbuilder=urlreq.request):
+def makepeer(ui, path, opener=None, requestbuilder=urlreq.request,
+             remotehidden=False):
     """Construct an appropriate HTTP peer instance.
 
     ``opener`` is an ``url.opener`` that should be used to establish
@@ -947,6 +958,11 @@  def makepeer(ui, path, opener=None, requ
     ``requestbuilder`` is the type used for constructing HTTP requests.
     It exists as an argument so extensions can override the default.
     """
+    if remotehidden:
+        msg = _("ignoring `--remote-hidden` request\n"
+                "(access to hidden changeset for http peers not "
+                "supported yet)\n")
+        ui.warn(msg)
     u = util.url(path)
     if u.query or u.fragment:
         raise error.Abort(_('unsupported URL component: "%s"') %
@@ -986,7 +1002,8 @@  def makepeer(ui, path, opener=None, requ
     return httppeer(ui, path, respurl, opener, requestbuilder,
                     info['v1capabilities'])
 
-def instance(ui, path, create, intents=None, createopts=None):
+def instance(ui, path, create, intents=None, createopts=None,
+             remotehidden=False):
     if create:
         raise error.Abort(_('cannot create new http repository'))
     try:
@@ -994,7 +1011,7 @@  def instance(ui, path, create, intents=N
             raise error.Abort(_('Python support for SSL and HTTPS '
                                 'is not installed'))
 
-        inst = makepeer(ui, path)
+        inst = makepeer(ui, path, remotehidden=remotehidden)
 
         return inst
     except error.RepoError as httpexception:
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -226,7 +226,12 @@  class localpeer(repository.peer):
     def local(self):
         return self._repo
 
-    def peer(self):
+    def peer(self, remotehidden=False):
+        if remotehidden:
+            msg = _("ignoring `--remote-hidden` request\n"
+                    "(access to hidden changeset for local peers not "
+                    "supported yet)\n")
+            self.ui.warn(msg)
         return self
 
     def canpush(self):
@@ -338,7 +343,7 @@  class locallegacypeer(localpeer):
     '''peer extension which implements legacy methods too; used for tests with
     restricted capabilities'''
 
-    def __init__(self, repo):
+    def __init__(self, repo, remotehidden=False):
         super(locallegacypeer, self).__init__(repo, caps=legacycaps)
 
     # Begin of baselegacywirecommands interface.
@@ -1169,7 +1174,12 @@  class localrepository(object):
                 parts.pop()
         return False
 
-    def peer(self):
+    def peer(self, remotehidden=False):
+        if remotehidden:
+            msg = _("ignoring `--remote-hidden` request\n"
+                    "(access to hidden changeset for local peers not "
+                    "supported yet)\n")
+            self.ui.warn(msg)
         return localpeer(self) # not cached to avoid reference cycle
 
     def unfiltered(self):
@@ -2877,7 +2887,8 @@  def undoname(fn):
     assert name.startswith('journal')
     return os.path.join(base, name.replace('journal', 'undo', 1))
 
-def instance(ui, path, create, intents=None, createopts=None):
+def instance(ui, path, create, intents=None, createopts=None,
+             remotehidden=False):
     localpath = util.urllocalpath(path)
     if create:
         createrepository(ui, localpath, createopts=createopts)
diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py
--- a/mercurial/sshpeer.py
+++ b/mercurial/sshpeer.py
@@ -407,7 +407,12 @@  class sshv1peer(wireprotov1peer.wirepeer
     def local(self):
         return None
 
-    def peer(self):
+    def peer(self, remotehidden=False):
+        if remotehidden:
+            msg = _("ignoring `--remote-hidden` request\n"
+                    "(access to hidden changeset for ssh peers not supported "
+                    "yet)\n")
+            self.ui.warn(msg)
         return self
 
     def canpush(self):
@@ -566,7 +571,8 @@  class sshv2peer(sshv1peer):
     # And handshake is performed before the peer is instantiated. So
     # we need no custom code.
 
-def makepeer(ui, path, proc, stdin, stdout, stderr, autoreadstderr=True):
+def makepeer(ui, path, proc, stdin, stdout, stderr, autoreadstderr=True,
+             remotehidden=False):
     """Make a peer instance from existing pipes.
 
     ``path`` and ``proc`` are stored on the eventual peer instance and may
@@ -580,6 +586,11 @@  def makepeer(ui, path, proc, stdin, stdo
     servers and clients via non-standard means, which can be useful for
     testing.
     """
+    if remotehidden:
+        msg = _("ignoring `--remote-hidden` request\n"
+                "(access to hidden changeset for ssh peers not supported "
+                "yet)\n")
+        ui.warn(msg)
     try:
         protoname, caps = _performhandshake(ui, stdin, stdout, stderr)
     except Exception:
@@ -597,7 +608,8 @@  def makepeer(ui, path, proc, stdin, stdo
         raise error.RepoError(_('unknown version of SSH protocol: %s') %
                               protoname)
 
-def instance(ui, path, create, intents=None, createopts=None):
+def instance(ui, path, create, intents=None, createopts=None,
+             remotehidden=False):
     """Create an SSH peer.
 
     The returned object conforms to the ``wireprotov1peer.wirepeer`` interface.
@@ -639,7 +651,8 @@  def instance(ui, path, create, intents=N
     proc, stdin, stdout, stderr = _makeconnection(ui, sshcmd, args, remotecmd,
                                                   remotepath, sshenv)
 
-    peer = makepeer(ui, path, proc, stdin, stdout, stderr)
+    peer = makepeer(ui, path, proc, stdin, stdout, stderr,
+                    remotehidden=remotehidden)
 
     # Finally, if supported by the server, notify it about our own
     # capabilities.
diff --git a/mercurial/statichttprepo.py b/mercurial/statichttprepo.py
--- a/mercurial/statichttprepo.py
+++ b/mercurial/statichttprepo.py
@@ -210,7 +210,12 @@  class statichttprepository(localrepo.loc
     def local(self):
         return False
 
-    def peer(self):
+    def peer(self, remotehidden=False):
+        if remotehidden:
+            msg = _("ignoring `--remote-hidden` request\n"
+                    "(access to hidden changeset for ssh peers not supported "
+                    "yet)\n")
+            self.ui.warn(msg)
         return statichttppeer(self)
 
     def wlock(self, wait=True):
@@ -223,7 +228,13 @@  class statichttprepository(localrepo.loc
     def _writecaches(self):
         pass # statichttprepository are read only
 
-def instance(ui, path, create, intents=None, createopts=None):
+def instance(ui, path, create, intents=None, createopts=None,
+             remotehidden=False):
+    if remotehidden:
+        msg = _("ignoring `--remote-hidden` request\n"
+                "(access to hidden changeset for ssh peers not supported "
+                "yet)\n")
+        ui.warn(msg)
     if create:
         raise error.Abort(_('cannot create new static-http repository'))
     return statichttprepository(ui, path[7:])
diff --git a/mercurial/unionrepo.py b/mercurial/unionrepo.py
--- a/mercurial/unionrepo.py
+++ b/mercurial/unionrepo.py
@@ -233,13 +233,19 @@  class unionrepository(object):
     def cancopy(self):
         return False
 
-    def peer(self):
+    def peer(self, remotehidden=False):
+        if remotehidden:
+            msg = _("ignoring `--remote-hidden` request\n"
+                    "(access to hidden changeset for union repo peers not "
+                    "supported yet)\n")
+            self.ui.warn(msg)
         return unionpeer(self)
 
     def getcwd(self):
         return encoding.getcwd() # always outside the repo
 
-def instance(ui, path, create, intents=None, createopts=None):
+def instance(ui, path, create, intents=None, createopts=None,
+             remotehidden=False):
     if create:
         raise error.Abort(_('cannot create new union repository'))
     parentpath = ui.config("bundle", "mainreporoot")
@@ -267,10 +273,16 @@  def instance(ui, path, create, intents=N
     else:
         repopath, repopath2 = parentpath, path
 
-    return makeunionrepository(ui, repopath, repopath2)
+    return makeunionrepository(ui, repopath, repopath2,
+                               remotehidden=remotehidden)
 
-def makeunionrepository(ui, repopath1, repopath2):
+def makeunionrepository(ui, repopath1, repopath2, remotehidden=False):
     """Make a union repository object from 2 local repo paths."""
+    if remotehidden:
+        msg = _("ignoring `--remote-hidden` request\n"
+                "(access to hidden changeset for union repo peers not "
+                "supported yet)\n")
+        ui.warn(msg)
     repo1 = localrepo.instance(ui, repopath1, create=False)
     repo2 = localrepo.instance(ui, repopath2, create=False)
 
diff --git a/tests/notcapable b/tests/notcapable
--- a/tests/notcapable
+++ b/tests/notcapable
@@ -14,10 +14,10 @@  def wrapcapable(orig, self, name, *args,
     if name in b'$CAP'.split(b' '):
         return False
     return orig(self, name, *args, **kwargs)
-def wrappeer(orig, self):
+def wrappeer(orig, self, *args, **kwargs):
     # Since we're disabling some newer features, we need to make sure local
     # repos add in the legacy features again.
-    return localrepo.locallegacypeer(self)
+    return localrepo.locallegacypeer(self, *args, **kwargs)
 EOF
 
 echo '[extensions]' >> $HGRCPATH
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -330,7 +330,7 @@  Show all commands + options
   parents: rev, style, template
   paths: template
   phase: public, draft, secret, force, rev
-  pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
+  pull: update, force, rev, bookmark, branch, remote-hidden, ssh, remotecmd, insecure
   push: force, rev, bookmark, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
   recover: 
   remove: after, force, subrepos, include, exclude, dry-run