[PATCH 11 of 11 RFC] url: refactor util.drop_scheme() and hg.localpath() into url.localpath()
Brodie Rao
brodie at bitheap.org
Sat Mar 26 01:29:46 CDT 2011
# HG changeset patch
# User Brodie Rao <brodie at bitheap.org>
# Date 1301119907 25200
# Node ID 0d6a6ff7d3c7703672f40ed994b721ecd51e0222
# Parent 79716c21ef9a7816d71fc8da1b6a3dd051d507bb
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.
This change has a side effect of making URLs like bundle://../foo not
correspond to relative paths with "hg bundle". This is because ".." is
considered to be the host for the URL. The URL should be bundle:../foo
to be relative.
Invalid URLs like the one above never worked with push, in, out, etc.,
so this makes URL handling more consistent.
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
/foo /foo /foo /foo
file:///C:/foo /C:/foo /C:/foo /C:/foo
file://C:/foo /foo C:/foo /foo
file://D:/foo /foo D:/foo /foo
On Windows:
URL drop_scheme() hg.localpath() url.localpath()
=== ============= ============== ===============
file://localhost:80/foo C:/foo localhost:80/foo /foo
file://localhost:/foo C:/foo localhost:/foo /foo
file://localhost/foo C:/foo C:/foo /foo
file:///foo C:/foo C:/foo /foo
file://foo (empty string) foo /
file:/foo /foo /foo /foo
file:foo foo foo foo
/foo /foo /foo /foo
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
The above behavior now also applies to the bundle scheme.
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. This patch does not implement percent encoding
handling.
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
@@ -1949,7 +1949,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
@@ -63,6 +63,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
if not path.startswith('/') and ':' in path:
parts = path.split(':', 1)
@@ -115,6 +117,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
@@ -201,9 +204,31 @@ 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 '/'
+ # On Windows, we need to promote hosts containing drive
+ # letters to paths with drive letters.
+ if (os.name == 'nt' and len(self._hostport) == 2 and
+ self._hostport[0].isalpha() and self._hostport[1] == ':'):
+ 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 (os.name == 'nt' and path.startswith('/') and
+ len(path) > 2 and path[1].isalpha() and path[2] == ':'):
+ # 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 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
@@ -1378,26 +1378,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
@@ -139,7 +139,7 @@ Log -R full.hg in fresh empty
$ rm -r empty
$ hg init empty
$ cd empty
- $ hg -R bundle://../full.hg log
+ $ hg -R bundle:../full.hg log
changeset: 8:aa35859c02ea
tag: tip
parent: 3:eebf5a27f8ca
@@ -196,6 +196,12 @@ Make sure bundlerepo doesn't leak tempfi
requires
store
+Pull invalid relative URL
+
+ $ hg pull bundle://../full.hg
+ abort: No such file or directory: /full.hg
+ [255]
+
Pull ../full.hg into empty (with hook)
$ echo '[hooks]' >> .hg/hgrc
@@ -203,10 +209,10 @@ Pull ../full.hg into empty (with hook)
doesn't work (yet ?)
-hg -R bundle://../full.hg verify
+hg -R bundle:../full.hg verify
- $ hg pull bundle://../full.hg
- pulling from bundle://../full.hg
+ $ hg pull bundle:../full.hg
+ pulling from bundle:../full.hg
requesting all changes
adding changesets
adding manifests
@@ -257,7 +263,7 @@ Create partial clones
Log -R full.hg in partial
- $ hg -R bundle://../full.hg log
+ $ hg -R bundle:../full.hg log
changeset: 8:aa35859c02ea
tag: tip
parent: 3:eebf5a27f8ca
@@ -309,8 +315,8 @@ Log -R full.hg in partial
Incoming full.hg in partial
- $ hg incoming bundle://../full.hg
- comparing with bundle://../full.hg
+ $ hg incoming bundle:../full.hg
+ comparing with bundle:../full.hg
searching for changes
changeset: 4:095197eb4973
parent: 0:f9ee2f85a263
@@ -343,7 +349,7 @@ Incoming full.hg in partial
Outgoing -R full.hg vs partial2 in partial
- $ hg -R bundle://../full.hg outgoing ../partial2
+ $ hg -R bundle:../full.hg outgoing ../partial2
comparing with ../partial2
searching for changes
changeset: 4:095197eb4973
@@ -377,7 +383,7 @@ Outgoing -R full.hg vs partial2 in parti
Outgoing -R does-not-exist.hg vs partial2 in partial
- $ hg -R bundle://../does-not-exist.hg outgoing ../partial2
+ $ hg -R bundle:../does-not-exist.hg outgoing ../partial2
abort: No such file or directory: ../does-not-exist.hg
[255]
$ cd ..
@@ -470,6 +476,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: repository /test-doesnt-exist not found!
[255]
+ $ hg pull -q file://../test
+ abort: repository /test not found!
+ [255]
+
$ hg pull -q file:../test
It's tricky to make file:// URLs working on every platform with
More information about the Mercurial-devel
mailing list