schemes.py
175 lines
| 5.2 KiB
| text/x-python
|
PythonLexer
/ hgext / schemes.py
Alexander Solovyov
|
r9964 | # Copyright 2009, Alexander Solovyov <piranha@piranha.org.ua> | ||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
Matt Mackall
|
r10263 | # GNU General Public License version 2 or any later version. | ||
Alexander Solovyov
|
r9964 | |||
"""extend schemes with shortcuts to repository swarms | ||||
This extension allows you to specify shortcuts for parent URLs with a | ||||
lot of repositories to act like a scheme, for example:: | ||||
[schemes] | ||||
py = http://code.python.org/hg/ | ||||
After that you can use it like:: | ||||
hg clone py://trunk/ | ||||
Additionally there is support for some more complex schemas, for | ||||
example used by Google Code:: | ||||
[schemes] | ||||
gcode = http://{1}.googlecode.com/hg/ | ||||
The syntax is taken from Mercurial templates, and you have unlimited | ||||
number of variables, starting with ``{1}`` and continuing with | ||||
``{2}``, ``{3}`` and so on. This variables will receive parts of URL | ||||
supplied, split by ``/``. Anything not specified as ``{part}`` will be | ||||
just appended to an URL. | ||||
For convenience, the extension adds these schemes by default:: | ||||
[schemes] | ||||
py = http://hg.python.org/ | ||||
bb = https://bitbucket.org/ | ||||
bb+ssh = ssh://hg@bitbucket.org/ | ||||
gcode = https://{1}.googlecode.com/hg/ | ||||
Benjamin Pollack
|
r10777 | kiln = https://{1}.kilnhg.com/Repo/ | ||
Alexander Solovyov
|
r9964 | |||
Martin Geisler
|
r9965 | You can override a predefined scheme by defining a new scheme with the | ||
same name. | ||||
Alexander Solovyov
|
r9964 | """ | ||
timeless
|
r28379 | import os | ||
import re | ||||
Yuya Nishihara
|
r29205 | |||
from mercurial.i18n import _ | ||||
timeless
|
r28379 | from mercurial import ( | ||
error, | ||||
extensions, | ||||
hg, | ||||
Pulkit Goyal
|
r30640 | pycompat, | ||
Yuya Nishihara
|
r32337 | registrar, | ||
timeless
|
r28379 | templater, | ||
r47669 | ) | |||
from mercurial.utils import ( | ||||
urlutil, | ||||
timeless
|
r28379 | ) | ||
Alexander Solovyov
|
r9964 | |||
Jason R. Coombs
|
r27982 | cmdtable = {} | ||
Yuya Nishihara
|
r32337 | command = registrar.command(cmdtable) | ||
Augie Fackler
|
r29841 | # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for | ||
Augie Fackler
|
r25186 | # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should | ||
# be specifying the version(s) of Mercurial they are tested with, or | ||||
# leave the attribute unspecified. | ||||
Augie Fackler
|
r43347 | testedwith = b'ships-with-hg-core' | ||
Augie Fackler
|
r16743 | |||
Matt Harbison
|
r44474 | _partre = re.compile(br'{(\d+)\}') | ||
Alexander Solovyov
|
r9964 | |||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r49801 | class ShortRepository: | ||
Alexander Solovyov
|
r9964 | def __init__(self, url, scheme, templater): | ||
self.scheme = scheme | ||||
self.templater = templater | ||||
self.url = url | ||||
try: | ||||
Augie Fackler
|
r31181 | self.parts = max(map(int, _partre.findall(self.url))) | ||
Alexander Solovyov
|
r9964 | except ValueError: | ||
self.parts = 0 | ||||
def __repr__(self): | ||||
Augie Fackler
|
r43347 | return b'<ShortRepository: %s>' % self.scheme | ||
Alexander Solovyov
|
r9964 | |||
r50652 | def make_peer(self, ui, path, *args, **kwargs): | |||
new_url = self.resolve(path.rawloc) | ||||
path = path.copy(new_raw_location=new_url) | ||||
cls = hg.peer_schemes.get(path.url.scheme) | ||||
r50645 | if cls is not None: | |||
r50652 | return cls.make_peer(ui, path, *args, **kwargs) | |||
r50645 | return None | |||
Gregory Szorc
|
r39585 | def instance(self, ui, url, create, intents=None, createopts=None): | ||
Jason R. Coombs
|
r27981 | url = self.resolve(url) | ||
r50644 | u = urlutil.url(url) | |||
scheme = u.scheme or b'file' | ||||
if scheme in hg.peer_schemes: | ||||
cls = hg.peer_schemes[scheme] | ||||
elif scheme in hg.repo_schemes: | ||||
cls = hg.repo_schemes[scheme] | ||||
else: | ||||
cls = hg.LocalFactory | ||||
return cls.instance( | ||||
Augie Fackler
|
r43346 | ui, url, create, intents=intents, createopts=createopts | ||
) | ||||
Jason R. Coombs
|
r27981 | |||
def resolve(self, url): | ||||
r47669 | # Should this use the urlutil.url class, or is manual parsing better? | |||
Mads Kiilerich
|
r18910 | try: | ||
Augie Fackler
|
r43347 | url = url.split(b'://', 1)[1] | ||
Mads Kiilerich
|
r18910 | except IndexError: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b"no '://' in scheme url '%s'") % url) | ||
parts = url.split(b'/', self.parts) | ||||
Alexander Solovyov
|
r9964 | if len(parts) > self.parts: | ||
tail = parts[-1] | ||||
parts = parts[:-1] | ||||
else: | ||||
Augie Fackler
|
r43347 | tail = b'' | ||
Augie Fackler
|
r44937 | context = {b'%d' % (i + 1): v for i, v in enumerate(parts)} | ||
Augie Fackler
|
r43347 | return b''.join(self.templater.process(self.url, context)) + tail | ||
Alexander Solovyov
|
r9964 | |||
Augie Fackler
|
r43346 | |||
Matt Mackall
|
r13827 | def hasdriveletter(orig, path): | ||
Patrick Mezard
|
r15609 | if path: | ||
for scheme in schemes: | ||||
Augie Fackler
|
r43347 | if path.startswith(scheme + b':'): | ||
Patrick Mezard
|
r15609 | return False | ||
Brodie Rao
|
r13822 | return orig(path) | ||
Augie Fackler
|
r43346 | |||
Alexander Solovyov
|
r9964 | schemes = { | ||
Augie Fackler
|
r43347 | b'py': b'http://hg.python.org/', | ||
b'bb': b'https://bitbucket.org/', | ||||
b'bb+ssh': b'ssh://hg@bitbucket.org/', | ||||
b'gcode': b'https://{1}.googlecode.com/hg/', | ||||
b'kiln': b'https://{1}.kilnhg.com/Repo/', | ||||
Augie Fackler
|
r43346 | } | ||
Alexander Solovyov
|
r9964 | |||
Matt Harbison
|
r50745 | def _check_drive_letter(scheme: bytes) -> None: | ||
r50585 | """check if a scheme conflict with a Windows drive letter""" | |||
if ( | ||||
pycompat.iswindows | ||||
and len(scheme) == 1 | ||||
and scheme.isalpha() | ||||
and os.path.exists(b'%s:\\' % scheme) | ||||
): | ||||
msg = _(b'custom scheme %s:// conflicts with drive letter %s:\\\n') | ||||
msg %= (scheme, scheme.upper()) | ||||
raise error.Abort(msg) | ||||
Alexander Solovyov
|
r9964 | def extsetup(ui): | ||
Augie Fackler
|
r43347 | schemes.update(dict(ui.configitems(b'schemes'))) | ||
Yuya Nishihara
|
r38373 | t = templater.engine(templater.parse) | ||
Alexander Solovyov
|
r9964 | for scheme, url in schemes.items(): | ||
Matt Harbison
|
r50745 | _check_drive_letter(scheme) | ||
r50584 | url_scheme = urlutil.url(url).scheme | |||
if url_scheme in hg.peer_schemes: | ||||
hg.peer_schemes[scheme] = ShortRepository(url, scheme, t) | ||||
else: | ||||
hg.repo_schemes[scheme] = ShortRepository(url, scheme, t) | ||||
Brodie Rao
|
r13822 | |||
r51684 | extensions.wrapfunction(urlutil, 'hasdriveletter', hasdriveletter) | |||
Jason R. Coombs
|
r27982 | |||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r43347 | @command(b'debugexpandscheme', norepo=True) | ||
Jason R. Coombs
|
r27982 | def expandscheme(ui, url, **opts): | ||
Augie Fackler
|
r46554 | """given a repo path, provide the scheme-expanded path""" | ||
r50584 | scheme = urlutil.url(url).scheme | |||
if scheme in hg.peer_schemes: | ||||
cls = hg.peer_schemes[scheme] | ||||
else: | ||||
cls = hg.repo_schemes.get(scheme) | ||||
if cls is not None and isinstance(cls, ShortRepository): | ||||
url = cls.resolve(url) | ||||
Augie Fackler
|
r43347 | ui.write(url + b'\n') | ||