Mercurial > public > mercurial-scm > hg
comparison mercurial/url.py @ 13826:e574207e3bcd
url: refactor util.drop_scheme() and hg.localpath() into url.localpath()
This replaces util.drop_scheme() with url.localpath(), using url.url for
parsing instead of doing it on its own. The function is moved from
util to url to avoid an import cycle.
hg.localpath() is removed in favor of using url.localpath(). This
provides more consistent behavior between "hg clone" and other
commands.
To preserve backwards compatibility, URLs like bundle://../foo still
refer to ../foo, not /foo.
If a URL contains a scheme, percent-encoded entities are decoded. When
there's no scheme, all characters are left untouched.
Comparison of old and new behaviors:
URL drop_scheme() hg.localpath() url.localpath()
=== ============= ============== ===============
file://foo/foo /foo foo/foo /foo
file://localhost:80/foo /foo localhost:80/foo /foo
file://localhost:/foo /foo localhost:/foo /foo
file://localhost/foo /foo /foo /foo
file:///foo /foo /foo /foo
file://foo (empty string) foo /
file:/foo /foo /foo /foo
file:foo foo foo foo
file:foo%23bar foo%23bar foo%23bar foo#bar
foo%23bar foo%23bar foo%23bar foo%23bar
/foo /foo /foo /foo
Windows-related paths on Windows:
URL drop_scheme() hg.localpath() url.localpath()
=== ============= ============== ===============
file:///C:/foo C:/C:/foo /C:/foo C:/foo
file:///D:/foo C:/D:/foo /D:/foo D:/foo
file://C:/foo C:/foo C:/foo C:/foo
file://D:/foo C:/foo D:/foo D:/foo
file:////foo/bar //foo/bar //foo/bar //foo/bar
//foo/bar //foo/bar //foo/bar //foo/bar
\\foo\bar //foo/bar //foo/bar \\foo\bar
Windows-related paths on other platforms:
file:///C:/foo C:/C:/foo /C:/foo C:/foo
file:///D:/foo C:/D:/foo /D:/foo D:/foo
file://C:/foo C:/foo C:/foo C:/foo
file://D:/foo C:/foo D:/foo D:/foo
file:////foo/bar //foo/bar //foo/bar //foo/bar
//foo/bar //foo/bar //foo/bar //foo/bar
\\foo\bar //foo/bar //foo/bar \\foo\bar
For more information about file:// URL handling, see:
http://www-archive.mozilla.org/quality/networking/testing/filetests.html
Related issues:
- issue1153: File URIs aren't handled correctly in windows
This patch should preserve the fix implemented in
2770d03ae49f. However, it goes a step further and "promotes"
Windows-style drive letters from being interpreted as host names to
being part of the path.
- issue2154: Cannot escape '#' in Mercurial URLs (#1172 in THG)
The fragment is still interpreted as a revision or a branch, even in
paths to bundles. However, when file: is used, percent-encoded
entities are decoded, so file:test%23bundle.hg can refer to
test#bundle.hg ond isk.
author | Brodie Rao <brodie@bitheap.org> |
---|---|
date | Wed, 30 Mar 2011 20:03:05 -0700 |
parents | 65b89e80f892 |
children | f1823b9f073b |
comparison
equal
deleted
inserted
replaced
13825:cc383142e738 | 13826:e574207e3bcd |
---|---|
68 def __init__(self, path, parse_query=True, parse_fragment=True): | 68 def __init__(self, path, parse_query=True, parse_fragment=True): |
69 # We slowly chomp away at path until we have only the path left | 69 # We slowly chomp away at path until we have only the path left |
70 self.scheme = self.user = self.passwd = self.host = None | 70 self.scheme = self.user = self.passwd = self.host = None |
71 self.port = self.path = self.query = self.fragment = None | 71 self.port = self.path = self.query = self.fragment = None |
72 self._localpath = True | 72 self._localpath = True |
73 self._hostport = '' | |
74 self._origpath = path | |
73 | 75 |
74 # special case for Windows drive letters | 76 # special case for Windows drive letters |
75 if has_drive_letter(path): | 77 if has_drive_letter(path): |
76 self.path = path | 78 self.path = path |
77 return | 79 return |
135 self.host = None | 137 self.host = None |
136 | 138 |
137 # Don't split on colons in IPv6 addresses without ports | 139 # Don't split on colons in IPv6 addresses without ports |
138 if (self.host and ':' in self.host and | 140 if (self.host and ':' in self.host and |
139 not (self.host.startswith('[') and self.host.endswith(']'))): | 141 not (self.host.startswith('[') and self.host.endswith(']'))): |
142 self._hostport = self.host | |
140 self.host, self.port = self.host.rsplit(':', 1) | 143 self.host, self.port = self.host.rsplit(':', 1) |
141 if not self.host: | 144 if not self.host: |
142 self.host = None | 145 self.host = None |
143 | 146 |
144 if (self.host and self.scheme == 'file' and | 147 if (self.host and self.scheme == 'file' and |
229 if not self.user: | 232 if not self.user: |
230 return (s, None) | 233 return (s, None) |
231 return (s, (None, (str(self), self.host), | 234 return (s, (None, (str(self), self.host), |
232 self.user, self.passwd or '')) | 235 self.user, self.passwd or '')) |
233 | 236 |
237 def localpath(self): | |
238 if self.scheme == 'file' or self.scheme == 'bundle': | |
239 path = self.path or '/' | |
240 # For Windows, we need to promote hosts containing drive | |
241 # letters to paths with drive letters. | |
242 if has_drive_letter(self._hostport): | |
243 path = self._hostport + '/' + self.path | |
244 elif self.host is not None and self.path: | |
245 path = '/' + path | |
246 # We also need to handle the case of file:///C:/, which | |
247 # should return C:/, not /C:/. | |
248 elif has_drive_letter(path): | |
249 # Strip leading slash from paths with drive names | |
250 return path[1:] | |
251 return path | |
252 return self._origpath | |
253 | |
234 def has_scheme(path): | 254 def has_scheme(path): |
235 return bool(url(path).scheme) | 255 return bool(url(path).scheme) |
236 | 256 |
237 def has_drive_letter(path): | 257 def has_drive_letter(path): |
238 return path[1:2] == ':' and path[0:1].isalpha() | 258 return path[1:2] == ':' and path[0:1].isalpha() |
259 | |
260 def localpath(path): | |
261 return url(path, parse_query=False, parse_fragment=False).localpath() | |
239 | 262 |
240 def hidepassword(u): | 263 def hidepassword(u): |
241 '''hide user credential in a url string''' | 264 '''hide user credential in a url string''' |
242 u = url(u) | 265 u = url(u) |
243 if u.passwd: | 266 if u.passwd: |