diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py +++ b/mercurial/bundlerepo.py @@ -15,7 +15,7 @@ from node import nullid from i18n import _ import os, struct, tempfile, shutil import changegroup, util, mdiff, discovery -import localrepo, changelog, manifest, filelog, revlog, error +import localrepo, changelog, manifest, filelog, revlog, error, url class bundlerevlog(revlog.revlog): def __init__(self, opener, indexfile, bundle, @@ -274,9 +274,9 @@ def instance(ui, path, create): cwd = os.path.join(cwd,'') if parentpath.startswith(cwd): parentpath = parentpath[len(cwd):] - path = util.drop_scheme('file', path) - if path.startswith('bundle:'): - path = util.drop_scheme('bundle', path) + u = url.url(path) + path = u.localpath() + if u.scheme == 'bundle': s = path.split("+", 1) if len(s) == 1: repopath, bundlename = parentpath, s[0] diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -17,7 +17,7 @@ import verify as verifymod import errno, os, shutil def _local(path): - path = util.expandpath(util.drop_scheme('file', path)) + path = util.expandpath(url.localpath(path)) return (os.path.isfile(path) and bundlerepo or localrepo) def addbranchrevs(lrepo, repo, branches, revs): @@ -102,15 +102,6 @@ def defaultdest(source): '''return default destination of clone if none is given''' return os.path.basename(os.path.normpath(source)) -def localpath(path): - if path.startswith('file://localhost/'): - return path[16:] - if path.startswith('file://'): - return path[7:] - if path.startswith('file:'): - return path[5:] - return path - def share(ui, source, dest=None, update=True): '''create a shared repository''' @@ -230,8 +221,8 @@ def clone(ui, source, dest=None, pull=Fa else: dest = ui.expandpath(dest) - dest = localpath(dest) - source = localpath(source) + dest = url.localpath(dest) + source = url.localpath(source) if os.path.exists(dest): if not os.path.isdir(dest): @@ -257,7 +248,7 @@ def clone(ui, source, dest=None, pull=Fa abspath = origsource copy = False if src_repo.cancopy() and islocal(dest): - abspath = os.path.abspath(util.drop_scheme('file', origsource)) + abspath = os.path.abspath(url.localpath(origsource)) copy = not pull and not rev if copy: diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -1928,7 +1928,7 @@ def aftertrans(files): return a def instance(ui, path, create): - return localrepository(ui, util.drop_scheme('file', path), create) + return localrepository(ui, urlmod.localpath(path), create) def islocal(path): return True diff --git a/mercurial/url.py b/mercurial/url.py --- a/mercurial/url.py +++ b/mercurial/url.py @@ -70,6 +70,8 @@ class url(object): self.scheme = self.user = self.passwd = self.host = None self.port = self.path = self.query = self.fragment = None self._localpath = True + self._hostport = '' + self._origpath = path # special case for Windows drive letters if has_drive_letter(path): @@ -137,6 +139,7 @@ class url(object): # Don't split on colons in IPv6 addresses without ports if (self.host and ':' in self.host and not (self.host.startswith('[') and self.host.endswith(']'))): + self._hostport = self.host self.host, self.port = self.host.rsplit(':', 1) if not self.host: self.host = None @@ -231,12 +234,32 @@ class url(object): return (s, (None, (str(self), self.host), self.user, self.passwd or '')) + def localpath(self): + if self.scheme == 'file' or self.scheme == 'bundle': + path = self.path or '/' + # For Windows, we need to promote hosts containing drive + # letters to paths with drive letters. + if has_drive_letter(self._hostport): + path = self._hostport + '/' + self.path + elif self.host is not None and self.path: + path = '/' + path + # We also need to handle the case of file:///C:/, which + # should return C:/, not /C:/. + elif has_drive_letter(path): + # Strip leading slash from paths with drive names + return path[1:] + return path + return self._origpath + def has_scheme(path): return bool(url(path).scheme) def has_drive_letter(path): return path[1:2] == ':' and path[0:1].isalpha() +def localpath(path): + return url(path, parse_query=False, parse_fragment=False).localpath() + def hidepassword(u): '''hide user credential in a url string''' u = url(u) diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -1384,26 +1384,6 @@ def bytecount(nbytes): return format % (nbytes / float(divisor)) return units[-1][2] % nbytes -def drop_scheme(scheme, path): - sc = scheme + ':' - if path.startswith(sc): - path = path[len(sc):] - if path.startswith('//'): - if scheme == 'file': - i = path.find('/', 2) - if i == -1: - return '' - # On Windows, absolute paths are rooted at the current drive - # root. On POSIX they are rooted at the file system root. - if os.name == 'nt': - droot = os.path.splitdrive(os.getcwd())[0] + '/' - path = os.path.join(droot, path[i + 1:]) - else: - path = path[i:] - else: - path = path[2:] - return path - def uirepr(s): # Avoid double backslash in Windows path repr() return repr(s).replace('\\\\', '\\') diff --git a/tests/test-bundle.t b/tests/test-bundle.t --- a/tests/test-bundle.t +++ b/tests/test-bundle.t @@ -470,6 +470,22 @@ test for 540d1059c802 $ cd .. +test bundle with # in the filename (issue2154): + + $ cp bundle.hg 'test#bundle.hg' + $ cd orig + $ hg incoming '../test#bundle.hg' + comparing with ../test + abort: unknown revision 'bundle.hg'! + [255] + +note that percent encoding is not handled: + + $ hg incoming ../test%23bundle.hg + abort: repository ../test%23bundle.hg not found! + [255] + $ cd .. + test for http://mercurial.selenic.com/bts/issue1144 test that verify bundle does not traceback diff --git a/tests/test-pull.t b/tests/test-pull.t --- a/tests/test-pull.t +++ b/tests/test-pull.t @@ -71,6 +71,10 @@ Test 'file:' uri handling: abort: file:// URLs can only refer to localhost [255] + $ hg pull -q file://../test + abort: file:// URLs can only refer to localhost + [255] + $ hg pull -q file:../test It's tricky to make file:// URLs working on every platform with