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: