# HG changeset patch # User Manuel Jacob # Date 2019-04-04 16:07:30 # Node ID 3a2df812e1c7a7fed0ff50c436f04433830d0f61 # Parent 4077d6222cf1fb1407e41fd407b4c7b48d902929 pull: add --remote-hidden option and pass it through peer creation This option will allow to pull changesets that are hidden on the remote. This is useful when looking into a changeset’s evolution history, resolving evolution instability or mirroring a repository. 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 type of peer is yet to be implemented. They currently all warn about lack of support. The warning code will get removed as peers gain support for this option. The option is still experimental, so we will have freedom to update the UI or implementation before it graduates out of experimental. Based on a changeset by Pierre-Yves David, which added the option. diff --git a/hgext/narrow/narrowrepo.py b/hgext/narrow/narrowrepo.py --- a/hgext/narrow/narrowrepo.py +++ b/hgext/narrow/narrowrepo.py @@ -19,8 +19,8 @@ def wraprepo(repo): dirstate = super(narrowrepository, self)._makedirstate() return narrowdirstate.wrapdirstate(self, dirstate) - def peer(self, path=None): - peer = super(narrowrepository, self).peer(path=path) + 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/mercurial/bundlerepo.py b/mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py +++ b/mercurial/bundlerepo.py @@ -484,8 +484,8 @@ class bundlerepository: def cancopy(self): return False - def peer(self, path=None): - return bundlepeer(self, path=path) + def peer(self, path=None, remotehidden=False): + return bundlepeer(self, path=path, remotehidden=remotehidden) def getcwd(self): return encoding.getcwd() # always outside the repo diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -5405,6 +5405,12 @@ def postincoming(ui, repo, modheads, opt _(b'a specific branch you would like to pull'), _(b'BRANCH'), ), + ( + b'', + b'remote-hidden', + False, + _(b"include changesets hidden on the remote (EXPERIMENTAL)"), + ), ] + remoteopts, _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'), @@ -5442,6 +5448,14 @@ def pull(ui, repo, *sources, **opts): Specifying bookmark as ``.`` is equivalent to specifying the active bookmark's name. + .. container:: verbose + + One can use the `--remote-hidden` flag to pull changesets + hidden on the remote. 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 backwards compatibility is not garanteed. + Returns 0 on success, 1 if an update had unresolved files. """ @@ -5456,12 +5470,16 @@ def pull(ui, repo, *sources, **opts): for path in urlutil.get_pull_paths(repo, ui, sources): ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(path.loc)) ui.flush() - other = hg.peer(repo, opts, path) + other = hg.peer(repo, opts, path, remotehidden=opts[b'remote_hidden']) update_conflict = None try: branches = (path.branch, opts.get(b'branch', [])) revs, checkout = hg.addbranchrevs( - repo, other, branches, opts.get(b'rev') + repo, + other, + branches, + opts.get(b'rev'), + remotehidden=opts[b'remote_hidden'], ) pullopargs = {} diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -65,10 +65,10 @@ release = lock.release sharedbookmarks = b'bookmarks' -def addbranchrevs(lrepo, other, branches, revs): +def addbranchrevs(lrepo, other, branches, revs, remotehidden=False): if util.safehasattr(other, 'peer'): # a courtesy to callers using a localrepo for other - peer = other.peer() + peer = other.peer(remotehidden=remotehidden) else: peer = other hashbranch, branches = branches @@ -242,7 +242,15 @@ def repository( return repo.filtered(b'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''' ui = getattr(uiorrepo, 'ui', uiorrepo) rui = remoteui(uiorrepo, opts) @@ -260,6 +268,7 @@ def peer(uiorrepo, opts, path, create=Fa create, intents=intents, createopts=createopts, + remotehidden=remotehidden, ) _setup_repo_or_peer(rui, peer) else: @@ -274,7 +283,7 @@ def peer(uiorrepo, opts, path, create=Fa intents=intents, createopts=createopts, ) - peer = repo.peer(path=peer_path) + peer = repo.peer(path=peer_path, remotehidden=remotehidden) return peer diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py --- a/mercurial/httppeer.py +++ b/mercurial/httppeer.py @@ -381,8 +381,17 @@ def parsev1commandresponse(ui, baseurl, class httppeer(wireprotov1peer.wirepeer): - def __init__(self, ui, path, url, opener, requestbuilder, caps): - super().__init__(ui, path=path) + def __init__( + self, ui, path, url, opener, requestbuilder, caps, remotehidden=False + ): + super().__init__(ui, path=path, remotehidden=remotehidden) + if remotehidden: + msg = _( + b"ignoring `--remote-hidden` request\n" + b"(access to hidden changeset for http peers not " + b"supported yet)\n" + ) + ui.warn(msg) self._url = url self._caps = caps self.limitedarguments = caps is not None and b'httppostargs' not in caps @@ -592,7 +601,9 @@ def performhandshake(ui, url, opener, re return respurl, info -def _make_peer(ui, path, opener=None, requestbuilder=urlreq.request): +def _make_peer( + 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 @@ -615,11 +626,19 @@ def _make_peer(ui, path, opener=None, re respurl, info = performhandshake(ui, url, opener, requestbuilder) return httppeer( - ui, path, respurl, opener, requestbuilder, info[b'v1capabilities'] + ui, + path, + respurl, + opener, + requestbuilder, + info[b'v1capabilities'], + remotehidden=remotehidden, ) -def make_peer(ui, path, create, intents=None, createopts=None): +def make_peer( + ui, path, create, intents=None, createopts=None, remotehidden=False +): if create: raise error.Abort(_(b'cannot create new http repository')) try: @@ -628,7 +647,7 @@ def make_peer(ui, path, create, intents= _(b'Python support for SSL and HTTPS is not installed') ) - inst = _make_peer(ui, path) + inst = _make_peer(ui, path, remotehidden=remotehidden) return inst except error.RepoError as httpexception: diff --git a/mercurial/interfaces/repository.py b/mercurial/interfaces/repository.py --- a/mercurial/interfaces/repository.py +++ b/mercurial/interfaces/repository.py @@ -388,7 +388,7 @@ class peer: limitedarguments = False - def __init__(self, ui, path=None): + def __init__(self, ui, path=None, remotehidden=False): self.ui = ui self.path = path diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -307,8 +307,18 @@ class localcommandexecutor: class localpeer(repository.peer): '''peer for a local repo; reflects only the most recent API''' - def __init__(self, repo, caps=None, path=None): - super(localpeer, self).__init__(repo.ui, path=path) + def __init__(self, repo, caps=None, path=None, remotehidden=False): + super(localpeer, self).__init__( + repo.ui, path=path, remotehidden=remotehidden + ) + + if remotehidden: + msg = _( + b"ignoring `--remote-hidden` request\n" + b"(access to hidden changeset for %r not " + b"supported yet)\n" + ) % type(self) + self.ui.warn(msg) if caps is None: caps = moderncaps.copy() @@ -455,8 +465,10 @@ class locallegacypeer(localpeer): """peer extension which implements legacy methods too; used for tests with restricted capabilities""" - def __init__(self, repo, path=None): - super(locallegacypeer, self).__init__(repo, caps=legacycaps, path=path) + def __init__(self, repo, path=None, remotehidden=False): + super(locallegacypeer, self).__init__( + repo, caps=legacycaps, path=path, remotehidden=remotehidden + ) # Begin of baselegacywirecommands interface. @@ -1657,8 +1669,10 @@ class localrepository: parts.pop() return False - def peer(self, path=None): - return localpeer(self, path=path) # not cached to avoid reference cycle + def peer(self, path=None, remotehidden=False): + return localpeer( + self, path=path, remotehidden=remotehidden + ) # not cached to avoid reference cycle def unfiltered(self): """Return unfiltered version of the repository diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -372,7 +372,16 @@ def _performhandshake(ui, stdin, stdout, class sshv1peer(wireprotov1peer.wirepeer): def __init__( - self, ui, path, proc, stdin, stdout, stderr, caps, autoreadstderr=True + self, + ui, + path, + proc, + stdin, + stdout, + stderr, + caps, + autoreadstderr=True, + remotehidden=False, ): """Create a peer from an existing SSH connection. @@ -383,7 +392,14 @@ class sshv1peer(wireprotov1peer.wirepeer ``autoreadstderr`` denotes whether to automatically read from stderr and to forward its output. """ - super().__init__(ui, path=path) + super().__init__(ui, path=path, remotehidden=remotehidden) + if remotehidden: + msg = _( + b"ignoring `--remote-hidden` request\n" + b"(access to hidden changeset for ssh peers not supported " + b"yet)\n" + ) + ui.warn(msg) # self._subprocess is unused. Keeping a handle on the process # holds a reference and prevents it from being garbage collected. self._subprocess = proc @@ -568,7 +584,16 @@ class sshv1peer(wireprotov1peer.wirepeer self._readerr() -def _make_peer(ui, path, proc, stdin, stdout, stderr, autoreadstderr=True): +def _make_peer( + 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 @@ -598,6 +623,7 @@ def _make_peer(ui, path, proc, stdin, st stderr, caps, autoreadstderr=autoreadstderr, + remotehidden=remotehidden, ) else: _cleanuppipes(ui, stdout, stdin, stderr, warn=None) @@ -606,7 +632,9 @@ def _make_peer(ui, path, proc, stdin, st ) -def make_peer(ui, path, create, intents=None, createopts=None): +def make_peer( + ui, path, create, intents=None, createopts=None, remotehidden=False +): """Create an SSH peer. The returned object conforms to the ``wireprotov1peer.wirepeer`` interface. @@ -658,7 +686,9 @@ def make_peer(ui, path, create, intents= ui, sshcmd, args, remotecmd, remotepath, sshenv ) - peer = _make_peer(ui, path, proc, stdin, stdout, stderr) + peer = _make_peer( + 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 @@ -237,8 +237,8 @@ class statichttprepository( def local(self): return False - def peer(self, path=None): - return statichttppeer(self, path=path) + def peer(self, path=None, remotehidden=False): + return statichttppeer(self, path=path, remotehidden=remotehidden) def wlock(self, wait=True): raise error.LockUnavailable( @@ -260,8 +260,12 @@ class statichttprepository( pass # statichttprepository are read only -def make_peer(ui, path, create, intents=None, createopts=None): +def make_peer( + ui, path, create, intents=None, createopts=None, remotehidden=False +): if create: raise error.Abort(_(b'cannot create new static-http repository')) url = path.loc[7:] - return statichttprepository(ui, url).peer(path=path) + return statichttprepository(ui, url).peer( + path=path, remotehidden=remotehidden + ) diff --git a/mercurial/unionrepo.py b/mercurial/unionrepo.py --- a/mercurial/unionrepo.py +++ b/mercurial/unionrepo.py @@ -270,8 +270,8 @@ class unionrepository: def cancopy(self): return False - def peer(self, path=None): - return unionpeer(self, path=None) + def peer(self, path=None, remotehidden=False): + return unionpeer(self, path=None, remotehidden=remotehidden) def getcwd(self): return encoding.getcwd() # always outside the repo diff --git a/tests/notcapable b/tests/notcapable --- a/tests/notcapable +++ b/tests/notcapable @@ -15,10 +15,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, path=None): +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, path=path) + 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 @@ -366,7 +366,7 @@ Show all commands + options parents: rev, style, template paths: template phase: public, draft, secret, force, rev - pull: update, force, confirm, rev, bookmark, branch, ssh, remotecmd, insecure + pull: update, force, confirm, rev, bookmark, branch, remote-hidden, ssh, remotecmd, insecure purge: abort-on-err, all, ignored, dirs, files, print, print0, confirm, include, exclude push: force, rev, bookmark, all-bookmarks, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure recover: verify