diff --git a/hgext/schemes.py b/hgext/schemes.py --- a/hgext/schemes.py +++ b/hgext/schemes.py @@ -78,9 +78,10 @@ def __repr__(self): return '' % self.scheme - def instance(self, ui, url, create, intents=None): + def instance(self, ui, url, create, intents=None, createopts=None): url = self.resolve(url) - return hg._peerlookup(url).instance(ui, url, create, intents=intents) + return hg._peerlookup(url).instance(ui, url, create, intents=intents, + createopts=createopts) 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 @@ -449,7 +449,7 @@ self.ui.warn(msg % nodemod.hex(p2)) return super(bundlerepository, self).setparents(p1, p2) -def instance(ui, path, create, intents=None): +def instance(ui, path, create, intents=None, createopts=None): if create: raise error.Abort(_('cannot create new bundle repository')) # internal config: bundle.mainreporoot diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -158,9 +158,10 @@ wirepeersetupfuncs = [] def _peerorrepo(ui, path, create=False, presetupfuncs=None, - intents=None): + intents=None, createopts=None): """return a repository object for the specified path""" - obj = _peerlookup(path).instance(ui, path, create, intents=intents) + obj = _peerlookup(path).instance(ui, path, create, intents=intents, + createopts=createopts) ui = getattr(obj, "ui", ui) if ui.configbool('devel', 'debug.extensions'): log = lambda msg, *values: ui.debug('debug.extensions: ', @@ -184,20 +185,22 @@ f(ui, obj) return obj -def repository(ui, path='', create=False, presetupfuncs=None, intents=None): +def repository(ui, path='', create=False, presetupfuncs=None, intents=None, + createopts=None): """return a repository object for the specified path""" peer = _peerorrepo(ui, path, create, presetupfuncs=presetupfuncs, - intents=intents) + intents=intents, createopts=createopts) repo = peer.local() if not repo: raise error.Abort(_("repository '%s' is not local") % (path or peer.url())) return repo.filtered('visible') -def peer(uiorrepo, opts, path, create=False, intents=None): +def peer(uiorrepo, opts, path, create=False, intents=None, createopts=None): '''return a repository peer for the specified path''' rui = remoteui(uiorrepo, opts) - return _peerorrepo(rui, path, create, intents=intents).peer() + return _peerorrepo(rui, path, create, intents=intents, + createopts=createopts).peer() 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 @@ -982,7 +982,7 @@ return httppeer(ui, path, respurl, opener, requestbuilder, info['v1capabilities']) -def instance(ui, path, create, intents=None): +def instance(ui, path, create, intents=None, createopts=None): if create: raise error.Abort(_('cannot create new http repository')) try: diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -2380,26 +2380,28 @@ assert name.startswith('journal') return os.path.join(base, name.replace('journal', 'undo', 1)) -def instance(ui, path, create, intents=None): +def instance(ui, path, create, intents=None, createopts=None): if create: vfs = vfsmod.vfs(path, expandpath=True, realpath=True) if vfs.exists('.hg'): raise error.RepoError(_('repository %s already exists') % path) - createrepository(ui, vfs) + createrepository(ui, vfs, createopts=createopts) return localrepository(ui, util.urllocalpath(path), intents=intents) def islocal(path): return True -def newreporequirements(ui): +def newreporequirements(ui, createopts=None): """Determine the set of requirements for a new local repository. Extensions can wrap this function to specify custom requirements for new repositories. """ + createopts = createopts or {} + requirements = {'revlogv1'} if ui.configbool('format', 'usestore'): requirements.add('store') @@ -2440,13 +2442,43 @@ return requirements -def createrepository(ui, wdirvfs): +def filterknowncreateopts(ui, createopts): + """Filters a dict of repo creation options against options that are known. + + Receives a dict of repo creation options and returns a dict of those + options that we don't know how to handle. + + This function is called as part of repository creation. If the + returned dict contains any items, repository creation will not + be allowed, as it means there was a request to create a repository + with options not recognized by loaded code. + + Extensions can wrap this function to filter out creation options + they know how to handle. + """ + return dict(createopts) + +def createrepository(ui, wdirvfs, createopts=None): """Create a new repository in a vfs. ``wdirvfs`` is a vfs instance pointing at the working directory. ``requirements`` is a set of requirements for the new repository. """ - requirements = newreporequirements(ui) + createopts = createopts or {} + + unknownopts = filterknowncreateopts(ui, createopts) + + if not isinstance(unknownopts, dict): + raise error.ProgrammingError('filterknowncreateopts() did not return ' + 'a dict') + + if unknownopts: + raise error.Abort(_('unable to create repository because of unknown ' + 'creation option: %s') % + ', '.sorted(unknownopts.keys()), + hint=_('is a required extension not loaded?')) + + requirements = newreporequirements(ui, createopts=createopts) if not wdirvfs.exists(): wdirvfs.makedirs() diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -597,7 +597,7 @@ raise error.RepoError(_('unknown version of SSH protocol: %s') % protoname) -def instance(ui, path, create, intents=None): +def instance(ui, path, create, intents=None, createopts=None): """Create an SSH peer. The returned object conforms to the ``wireprotov1peer.wirepeer`` interface. @@ -620,6 +620,14 @@ args = procutil.sshargs(sshcmd, u.host, u.user, u.port) if create: + # We /could/ do this, but only if the remote init command knows how to + # handle them. We don't yet make any assumptions about that. And without + # querying the remote, there's no way of knowing if the remote even + # supports said requested feature. + if createopts: + raise error.RepoError(_('cannot create remote SSH repositories ' + 'with extra options')) + cmd = '%s %s %s' % (sshcmd, args, procutil.shellquote('%s init %s' % (_serverquote(remotecmd), _serverquote(remotepath)))) diff --git a/mercurial/statichttprepo.py b/mercurial/statichttprepo.py --- a/mercurial/statichttprepo.py +++ b/mercurial/statichttprepo.py @@ -215,7 +215,7 @@ def _writecaches(self): pass # statichttprepository are read only -def instance(ui, path, create, intents=None): +def instance(ui, path, create, intents=None, createopts=None): 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,7 +233,7 @@ def getcwd(self): return pycompat.getcwd() # always outside the repo -def instance(ui, path, create, intents=None): +def instance(ui, path, create, intents=None, createopts=None): if create: raise error.Abort(_('cannot create new union repository')) parentpath = ui.config("bundle", "mainreporoot")