Show More
@@ -9,7 +9,7 b'' | |||||
9 |
|
9 | |||
10 | from mercurial.i18n import _ |
|
10 | from mercurial.i18n import _ | |
11 | from mercurial.node import nullid, short |
|
11 | from mercurial.node import nullid, short | |
12 |
from mercurial import commands, cmdutil, hg, util, |
|
12 | from mercurial import commands, cmdutil, hg, util, error | |
13 | from mercurial.lock import release |
|
13 | from mercurial.lock import release | |
14 |
|
14 | |||
15 | def fetch(ui, repo, source='default', **opts): |
|
15 | def fetch(ui, repo, source='default', **opts): | |
@@ -66,7 +66,7 b" def fetch(ui, repo, source='default', **" | |||||
66 | other = hg.repository(hg.remoteui(repo, opts), |
|
66 | other = hg.repository(hg.remoteui(repo, opts), | |
67 | ui.expandpath(source)) |
|
67 | ui.expandpath(source)) | |
68 | ui.status(_('pulling from %s\n') % |
|
68 | ui.status(_('pulling from %s\n') % | |
69 |
u |
|
69 | util.hidepassword(ui.expandpath(source))) | |
70 | revs = None |
|
70 | revs = None | |
71 | if opts['rev']: |
|
71 | if opts['rev']: | |
72 | try: |
|
72 | try: | |
@@ -125,7 +125,7 b" def fetch(ui, repo, source='default', **" | |||||
125 | # we don't translate commit messages |
|
125 | # we don't translate commit messages | |
126 | message = (cmdutil.logmessage(opts) or |
|
126 | message = (cmdutil.logmessage(opts) or | |
127 | ('Automated merge with %s' % |
|
127 | ('Automated merge with %s' % | |
128 |
u |
|
128 | util.removeauth(other.url()))) | |
129 | editor = cmdutil.commiteditor |
|
129 | editor = cmdutil.commiteditor | |
130 | if opts.get('force_editor') or opts.get('edit'): |
|
130 | if opts.get('force_editor') or opts.get('edit'): | |
131 | editor = cmdutil.commitforceeditor |
|
131 | editor = cmdutil.commitforceeditor |
@@ -48,7 +48,7 b' hgrc(5) for details.' | |||||
48 | import os, errno, socket, tempfile, cStringIO, time |
|
48 | import os, errno, socket, tempfile, cStringIO, time | |
49 | import email.MIMEMultipart, email.MIMEBase |
|
49 | import email.MIMEMultipart, email.MIMEBase | |
50 | import email.Utils, email.Encoders, email.Generator |
|
50 | import email.Utils, email.Encoders, email.Generator | |
51 |
from mercurial import cmdutil, commands, hg, mail, patch, util, discovery |
|
51 | from mercurial import cmdutil, commands, hg, mail, patch, util, discovery | |
52 | from mercurial.i18n import _ |
|
52 | from mercurial.i18n import _ | |
53 | from mercurial.node import bin |
|
53 | from mercurial.node import bin | |
54 |
|
54 | |||
@@ -239,7 +239,7 b' def patchbomb(ui, repo, *revs, **opts):' | |||||
239 | dest, branches = hg.parseurl(dest) |
|
239 | dest, branches = hg.parseurl(dest) | |
240 | revs, checkout = hg.addbranchrevs(repo, repo, branches, revs) |
|
240 | revs, checkout = hg.addbranchrevs(repo, repo, branches, revs) | |
241 | other = hg.repository(hg.remoteui(repo, opts), dest) |
|
241 | other = hg.repository(hg.remoteui(repo, opts), dest) | |
242 |
ui.status(_('comparing with %s\n') % u |
|
242 | ui.status(_('comparing with %s\n') % util.hidepassword(dest)) | |
243 | common, _anyinc, _heads = discovery.findcommonincoming(repo, other) |
|
243 | common, _anyinc, _heads = discovery.findcommonincoming(repo, other) | |
244 | nodes = revs and map(repo.lookup, revs) or revs |
|
244 | nodes = revs and map(repo.lookup, revs) or revs | |
245 | o = repo.changelog.findmissing(common, heads=nodes) |
|
245 | o = repo.changelog.findmissing(common, heads=nodes) |
@@ -41,7 +41,7 b' same name.' | |||||
41 | """ |
|
41 | """ | |
42 |
|
42 | |||
43 | import os, re |
|
43 | import os, re | |
44 |
from mercurial import extensions, hg, templater, |
|
44 | from mercurial import extensions, hg, templater, util | |
45 | from mercurial.i18n import _ |
|
45 | from mercurial.i18n import _ | |
46 |
|
46 | |||
47 |
|
47 | |||
@@ -95,4 +95,4 b' def extsetup(ui):' | |||||
95 | 'letter %s:\\\n') % (scheme, scheme.upper())) |
|
95 | 'letter %s:\\\n') % (scheme, scheme.upper())) | |
96 | hg.schemes[scheme] = ShortRepository(url, scheme, t) |
|
96 | hg.schemes[scheme] = ShortRepository(url, scheme, t) | |
97 |
|
97 | |||
98 |
extensions.wrapfunction(u |
|
98 | extensions.wrapfunction(util, 'hasdriveletter', hasdriveletter) |
@@ -15,7 +15,7 b' from node import nullid' | |||||
15 | from i18n import _ |
|
15 | from i18n import _ | |
16 | import os, struct, tempfile, shutil |
|
16 | import os, struct, tempfile, shutil | |
17 | import changegroup, util, mdiff, discovery |
|
17 | import changegroup, util, mdiff, discovery | |
18 |
import localrepo, changelog, manifest, filelog, revlog, error |
|
18 | import localrepo, changelog, manifest, filelog, revlog, error | |
19 |
|
19 | |||
20 | class bundlerevlog(revlog.revlog): |
|
20 | class bundlerevlog(revlog.revlog): | |
21 | def __init__(self, opener, indexfile, bundle, |
|
21 | def __init__(self, opener, indexfile, bundle, | |
@@ -274,7 +274,7 b' def instance(ui, path, create):' | |||||
274 | cwd = os.path.join(cwd,'') |
|
274 | cwd = os.path.join(cwd,'') | |
275 | if parentpath.startswith(cwd): |
|
275 | if parentpath.startswith(cwd): | |
276 | parentpath = parentpath[len(cwd):] |
|
276 | parentpath = parentpath[len(cwd):] | |
277 |
u = u |
|
277 | u = util.url(path) | |
278 | path = u.localpath() |
|
278 | path = u.localpath() | |
279 | if u.scheme == 'bundle': |
|
279 | if u.scheme == 'bundle': | |
280 | s = path.split("+", 1) |
|
280 | s = path.split("+", 1) |
@@ -2607,7 +2607,7 b' def incoming(ui, repo, source="default",' | |||||
2607 | if 'bookmarks' not in other.listkeys('namespaces'): |
|
2607 | if 'bookmarks' not in other.listkeys('namespaces'): | |
2608 | ui.warn(_("remote doesn't support bookmarks\n")) |
|
2608 | ui.warn(_("remote doesn't support bookmarks\n")) | |
2609 | return 0 |
|
2609 | return 0 | |
2610 |
ui.status(_('comparing with %s\n') % u |
|
2610 | ui.status(_('comparing with %s\n') % util.hidepassword(source)) | |
2611 | return bookmarks.diff(ui, repo, other) |
|
2611 | return bookmarks.diff(ui, repo, other) | |
2612 |
|
2612 | |||
2613 | ret = hg.incoming(ui, repo, source, opts) |
|
2613 | ret = hg.incoming(ui, repo, source, opts) | |
@@ -2894,7 +2894,7 b' def outgoing(ui, repo, dest=None, **opts' | |||||
2894 | if 'bookmarks' not in other.listkeys('namespaces'): |
|
2894 | if 'bookmarks' not in other.listkeys('namespaces'): | |
2895 | ui.warn(_("remote doesn't support bookmarks\n")) |
|
2895 | ui.warn(_("remote doesn't support bookmarks\n")) | |
2896 | return 0 |
|
2896 | return 0 | |
2897 |
ui.status(_('comparing with %s\n') % u |
|
2897 | ui.status(_('comparing with %s\n') % util.hidepassword(dest)) | |
2898 | return bookmarks.diff(ui, other, repo) |
|
2898 | return bookmarks.diff(ui, other, repo) | |
2899 |
|
2899 | |||
2900 | ret = hg.outgoing(ui, repo, dest, opts) |
|
2900 | ret = hg.outgoing(ui, repo, dest, opts) | |
@@ -2968,13 +2968,13 b' def paths(ui, repo, search=None):' | |||||
2968 | if search: |
|
2968 | if search: | |
2969 | for name, path in ui.configitems("paths"): |
|
2969 | for name, path in ui.configitems("paths"): | |
2970 | if name == search: |
|
2970 | if name == search: | |
2971 |
ui.write("%s\n" % u |
|
2971 | ui.write("%s\n" % util.hidepassword(path)) | |
2972 | return |
|
2972 | return | |
2973 | ui.warn(_("not found!\n")) |
|
2973 | ui.warn(_("not found!\n")) | |
2974 | return 1 |
|
2974 | return 1 | |
2975 | else: |
|
2975 | else: | |
2976 | for name, path in ui.configitems("paths"): |
|
2976 | for name, path in ui.configitems("paths"): | |
2977 |
ui.write("%s = %s\n" % (name, u |
|
2977 | ui.write("%s = %s\n" % (name, util.hidepassword(path))) | |
2978 |
|
2978 | |||
2979 | def postincoming(ui, repo, modheads, optupdate, checkout): |
|
2979 | def postincoming(ui, repo, modheads, optupdate, checkout): | |
2980 | if modheads == 0: |
|
2980 | if modheads == 0: | |
@@ -3017,7 +3017,7 b' def pull(ui, repo, source="default", **o' | |||||
3017 | """ |
|
3017 | """ | |
3018 | source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch')) |
|
3018 | source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch')) | |
3019 | other = hg.repository(hg.remoteui(repo, opts), source) |
|
3019 | other = hg.repository(hg.remoteui(repo, opts), source) | |
3020 |
ui.status(_('pulling from %s\n') % u |
|
3020 | ui.status(_('pulling from %s\n') % util.hidepassword(source)) | |
3021 | revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev')) |
|
3021 | revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev')) | |
3022 |
|
3022 | |||
3023 | if opts.get('bookmark'): |
|
3023 | if opts.get('bookmark'): | |
@@ -3100,7 +3100,7 b' def push(ui, repo, dest=None, **opts):' | |||||
3100 |
|
3100 | |||
3101 | dest = ui.expandpath(dest or 'default-push', dest or 'default') |
|
3101 | dest = ui.expandpath(dest or 'default-push', dest or 'default') | |
3102 | dest, branches = hg.parseurl(dest, opts.get('branch')) |
|
3102 | dest, branches = hg.parseurl(dest, opts.get('branch')) | |
3103 |
ui.status(_('pushing to %s\n') % u |
|
3103 | ui.status(_('pushing to %s\n') % util.hidepassword(dest)) | |
3104 | revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev')) |
|
3104 | revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev')) | |
3105 | other = hg.repository(hg.remoteui(repo, opts), dest) |
|
3105 | other = hg.repository(hg.remoteui(repo, opts), dest) | |
3106 | if revs: |
|
3106 | if revs: | |
@@ -3919,7 +3919,7 b' def summary(ui, repo, **opts):' | |||||
3919 | source, branches = hg.parseurl(ui.expandpath('default')) |
|
3919 | source, branches = hg.parseurl(ui.expandpath('default')) | |
3920 | other = hg.repository(hg.remoteui(repo, {}), source) |
|
3920 | other = hg.repository(hg.remoteui(repo, {}), source) | |
3921 | revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev')) |
|
3921 | revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev')) | |
3922 |
ui.debug('comparing with %s\n' % u |
|
3922 | ui.debug('comparing with %s\n' % util.hidepassword(source)) | |
3923 | repo.ui.pushbuffer() |
|
3923 | repo.ui.pushbuffer() | |
3924 | common, incoming, rheads = discovery.findcommonincoming(repo, other) |
|
3924 | common, incoming, rheads = discovery.findcommonincoming(repo, other) | |
3925 | repo.ui.popbuffer() |
|
3925 | repo.ui.popbuffer() | |
@@ -3929,7 +3929,7 b' def summary(ui, repo, **opts):' | |||||
3929 | dest, branches = hg.parseurl(ui.expandpath('default-push', 'default')) |
|
3929 | dest, branches = hg.parseurl(ui.expandpath('default-push', 'default')) | |
3930 | revs, checkout = hg.addbranchrevs(repo, repo, branches, None) |
|
3930 | revs, checkout = hg.addbranchrevs(repo, repo, branches, None) | |
3931 | other = hg.repository(hg.remoteui(repo, {}), dest) |
|
3931 | other = hg.repository(hg.remoteui(repo, {}), dest) | |
3932 |
ui.debug('comparing with %s\n' % u |
|
3932 | ui.debug('comparing with %s\n' % util.hidepassword(dest)) | |
3933 | repo.ui.pushbuffer() |
|
3933 | repo.ui.pushbuffer() | |
3934 | common, _anyinc, _heads = discovery.findcommonincoming(repo, other) |
|
3934 | common, _anyinc, _heads = discovery.findcommonincoming(repo, other) | |
3935 | repo.ui.popbuffer() |
|
3935 | repo.ui.popbuffer() |
@@ -11,13 +11,13 b' from lock import release' | |||||
11 | from node import hex, nullid |
|
11 | from node import hex, nullid | |
12 | import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks |
|
12 | import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks | |
13 | import lock, util, extensions, error, node |
|
13 | import lock, util, extensions, error, node | |
14 |
import cmdutil, discovery |
|
14 | import cmdutil, discovery | |
15 | import merge as mergemod |
|
15 | import merge as mergemod | |
16 | import verify as verifymod |
|
16 | import verify as verifymod | |
17 | import errno, os, shutil |
|
17 | import errno, os, shutil | |
18 |
|
18 | |||
19 | def _local(path): |
|
19 | def _local(path): | |
20 |
path = util.expandpath(u |
|
20 | path = util.expandpath(util.localpath(path)) | |
21 | return (os.path.isfile(path) and bundlerepo or localrepo) |
|
21 | return (os.path.isfile(path) and bundlerepo or localrepo) | |
22 |
|
22 | |||
23 | def addbranchrevs(lrepo, repo, branches, revs): |
|
23 | def addbranchrevs(lrepo, repo, branches, revs): | |
@@ -54,7 +54,7 b' def addbranchrevs(lrepo, repo, branches,' | |||||
54 | def parseurl(path, branches=None): |
|
54 | def parseurl(path, branches=None): | |
55 | '''parse url#branch, returning (url, (branch, branches))''' |
|
55 | '''parse url#branch, returning (url, (branch, branches))''' | |
56 |
|
56 | |||
57 |
u = u |
|
57 | u = util.url(path) | |
58 | branch = None |
|
58 | branch = None | |
59 | if u.fragment: |
|
59 | if u.fragment: | |
60 | branch = u.fragment |
|
60 | branch = u.fragment | |
@@ -71,7 +71,7 b' schemes = {' | |||||
71 | } |
|
71 | } | |
72 |
|
72 | |||
73 | def _lookup(path): |
|
73 | def _lookup(path): | |
74 |
u = u |
|
74 | u = util.url(path) | |
75 | scheme = u.scheme or 'file' |
|
75 | scheme = u.scheme or 'file' | |
76 | thing = schemes.get(scheme) or schemes['file'] |
|
76 | thing = schemes.get(scheme) or schemes['file'] | |
77 | try: |
|
77 | try: | |
@@ -221,8 +221,8 b' def clone(ui, source, dest=None, pull=Fa' | |||||
221 | else: |
|
221 | else: | |
222 | dest = ui.expandpath(dest) |
|
222 | dest = ui.expandpath(dest) | |
223 |
|
223 | |||
224 |
dest = u |
|
224 | dest = util.localpath(dest) | |
225 |
source = u |
|
225 | source = util.localpath(source) | |
226 |
|
226 | |||
227 | if os.path.exists(dest): |
|
227 | if os.path.exists(dest): | |
228 | if not os.path.isdir(dest): |
|
228 | if not os.path.isdir(dest): | |
@@ -248,7 +248,7 b' def clone(ui, source, dest=None, pull=Fa' | |||||
248 | abspath = origsource |
|
248 | abspath = origsource | |
249 | copy = False |
|
249 | copy = False | |
250 | if src_repo.cancopy() and islocal(dest): |
|
250 | if src_repo.cancopy() and islocal(dest): | |
251 |
abspath = os.path.abspath(u |
|
251 | abspath = os.path.abspath(util.localpath(origsource)) | |
252 | copy = not pull and not rev |
|
252 | copy = not pull and not rev | |
253 |
|
253 | |||
254 | if copy: |
|
254 | if copy: | |
@@ -421,7 +421,7 b' def _incoming(displaychlist, subreporecu' | |||||
421 | """ |
|
421 | """ | |
422 | source, branches = parseurl(ui.expandpath(source), opts.get('branch')) |
|
422 | source, branches = parseurl(ui.expandpath(source), opts.get('branch')) | |
423 | other = repository(remoteui(repo, opts), source) |
|
423 | other = repository(remoteui(repo, opts), source) | |
424 |
ui.status(_('comparing with %s\n') % u |
|
424 | ui.status(_('comparing with %s\n') % util.hidepassword(source)) | |
425 | revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev')) |
|
425 | revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev')) | |
426 |
|
426 | |||
427 | if revs: |
|
427 | if revs: | |
@@ -477,7 +477,7 b' def incoming(ui, repo, source, opts):' | |||||
477 | def _outgoing(ui, repo, dest, opts): |
|
477 | def _outgoing(ui, repo, dest, opts): | |
478 | dest = ui.expandpath(dest or 'default-push', dest or 'default') |
|
478 | dest = ui.expandpath(dest or 'default-push', dest or 'default') | |
479 | dest, branches = parseurl(dest, opts.get('branch')) |
|
479 | dest, branches = parseurl(dest, opts.get('branch')) | |
480 |
ui.status(_('comparing with %s\n') % u |
|
480 | ui.status(_('comparing with %s\n') % util.hidepassword(dest)) | |
481 | revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev')) |
|
481 | revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev')) | |
482 | if revs: |
|
482 | if revs: | |
483 | revs = [repo.lookup(rev) for rev in revs] |
|
483 | revs = [repo.lookup(rev) for rev in revs] |
@@ -9,7 +9,7 b'' | |||||
9 | import os, re, time |
|
9 | import os, re, time | |
10 | from mercurial.i18n import _ |
|
10 | from mercurial.i18n import _ | |
11 | from mercurial import ui, hg, scmutil, util, templater |
|
11 | from mercurial import ui, hg, scmutil, util, templater | |
12 |
from mercurial import error, encoding |
|
12 | from mercurial import error, encoding | |
13 | from common import ErrorResponse, get_mtime, staticfile, paritygen, \ |
|
13 | from common import ErrorResponse, get_mtime, staticfile, paritygen, \ | |
14 | get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR |
|
14 | get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR | |
15 | from hgweb_mod import hgweb |
|
15 | from hgweb_mod import hgweb | |
@@ -364,7 +364,7 b' class hgwebdir(object):' | |||||
364 |
|
364 | |||
365 | def updatereqenv(self, env): |
|
365 | def updatereqenv(self, env): | |
366 | if self._baseurl is not None: |
|
366 | if self._baseurl is not None: | |
367 |
u = u |
|
367 | u = util.url(self._baseurl) | |
368 | env['SERVER_NAME'] = u.host |
|
368 | env['SERVER_NAME'] = u.host | |
369 | if u.port: |
|
369 | if u.port: | |
370 | env['SERVER_PORT'] = u.port |
|
370 | env['SERVER_PORT'] = u.port |
@@ -28,7 +28,7 b' class httprepository(wireproto.wirerepos' | |||||
28 | self.path = path |
|
28 | self.path = path | |
29 | self.caps = None |
|
29 | self.caps = None | |
30 | self.handler = None |
|
30 | self.handler = None | |
31 |
u = u |
|
31 | u = util.url(path) | |
32 | if u.query or u.fragment: |
|
32 | if u.query or u.fragment: | |
33 | raise util.Abort(_('unsupported URL component: "%s"') % |
|
33 | raise util.Abort(_('unsupported URL component: "%s"') % | |
34 | (u.query or u.fragment)) |
|
34 | (u.query or u.fragment)) | |
@@ -111,12 +111,12 b' class httprepository(wireproto.wirerepos' | |||||
111 | except AttributeError: |
|
111 | except AttributeError: | |
112 | proto = resp.headers['content-type'] |
|
112 | proto = resp.headers['content-type'] | |
113 |
|
113 | |||
114 |
safeurl = u |
|
114 | safeurl = util.hidepassword(self._url) | |
115 | # accept old "text/plain" and "application/hg-changegroup" for now |
|
115 | # accept old "text/plain" and "application/hg-changegroup" for now | |
116 | if not (proto.startswith('application/mercurial-') or |
|
116 | if not (proto.startswith('application/mercurial-') or | |
117 | proto.startswith('text/plain') or |
|
117 | proto.startswith('text/plain') or | |
118 | proto.startswith('application/hg-changegroup')): |
|
118 | proto.startswith('application/hg-changegroup')): | |
119 |
self.ui.debug("requested URL: '%s'\n" % u |
|
119 | self.ui.debug("requested URL: '%s'\n" % util.hidepassword(cu)) | |
120 | raise error.RepoError( |
|
120 | raise error.RepoError( | |
121 | _("'%s' does not appear to be an hg repository:\n" |
|
121 | _("'%s' does not appear to be an hg repository:\n" | |
122 | "---%%<--- (%s)\n%s\n---%%<---\n") |
|
122 | "---%%<--- (%s)\n%s\n---%%<---\n") |
@@ -14,7 +14,6 b' import scmutil, util, extensions, hook, ' | |||||
14 | import match as matchmod |
|
14 | import match as matchmod | |
15 | import merge as mergemod |
|
15 | import merge as mergemod | |
16 | import tags as tagsmod |
|
16 | import tags as tagsmod | |
17 | import url as urlmod |
|
|||
18 | from lock import release |
|
17 | from lock import release | |
19 | import weakref, errno, os, time, inspect |
|
18 | import weakref, errno, os, time, inspect | |
20 | propertycache = util.propertycache |
|
19 | propertycache = util.propertycache | |
@@ -1695,7 +1694,7 b' class localrepository(repo.repository):' | |||||
1695 | cl.delayupdate() |
|
1694 | cl.delayupdate() | |
1696 | oldheads = cl.heads() |
|
1695 | oldheads = cl.heads() | |
1697 |
|
1696 | |||
1698 |
tr = self.transaction("\n".join([srctype, u |
|
1697 | tr = self.transaction("\n".join([srctype, util.hidepassword(url)])) | |
1699 | try: |
|
1698 | try: | |
1700 | trp = weakref.proxy(tr) |
|
1699 | trp = weakref.proxy(tr) | |
1701 | # pull off the changeset group |
|
1700 | # pull off the changeset group | |
@@ -1937,7 +1936,7 b' def aftertrans(files):' | |||||
1937 | return a |
|
1936 | return a | |
1938 |
|
1937 | |||
1939 | def instance(ui, path, create): |
|
1938 | def instance(ui, path, create): | |
1940 |
return localrepository(ui, u |
|
1939 | return localrepository(ui, util.localpath(path), create) | |
1941 |
|
1940 | |||
1942 | def islocal(path): |
|
1941 | def islocal(path): | |
1943 | return True |
|
1942 | return True |
@@ -6,7 +6,7 b'' | |||||
6 | # GNU General Public License version 2 or any later version. |
|
6 | # GNU General Public License version 2 or any later version. | |
7 |
|
7 | |||
8 | from i18n import _ |
|
8 | from i18n import _ | |
9 |
import util, error, wireproto |
|
9 | import util, error, wireproto | |
10 |
|
10 | |||
11 | class remotelock(object): |
|
11 | class remotelock(object): | |
12 | def __init__(self, repo): |
|
12 | def __init__(self, repo): | |
@@ -23,7 +23,7 b' class sshrepository(wireproto.wirereposi' | |||||
23 | self._url = path |
|
23 | self._url = path | |
24 | self.ui = ui |
|
24 | self.ui = ui | |
25 |
|
25 | |||
26 |
u = u |
|
26 | u = util.url(path, parsequery=False, parsefragment=False) | |
27 | if u.scheme != 'ssh' or not u.host or u.path is None: |
|
27 | if u.scheme != 'ssh' or not u.host or u.path is None: | |
28 | self._abort(error.RepoError(_("couldn't parse location %s") % path)) |
|
28 | self._abort(error.RepoError(_("couldn't parse location %s") % path)) | |
29 |
|
29 |
@@ -85,7 +85,7 b' class statichttprepository(localrepo.loc' | |||||
85 | self.ui = ui |
|
85 | self.ui = ui | |
86 |
|
86 | |||
87 | self.root = path |
|
87 | self.root = path | |
88 |
u = u |
|
88 | u = util.url(path.rstrip('/') + "/.hg") | |
89 | self.path, authinfo = u.authinfo() |
|
89 | self.path, authinfo = u.authinfo() | |
90 |
|
90 | |||
91 | opener = build_opener(ui, authinfo) |
|
91 | opener = build_opener(ui, authinfo) |
@@ -8,7 +8,7 b'' | |||||
8 | import errno, os, re, xml.dom.minidom, shutil, posixpath |
|
8 | import errno, os, re, xml.dom.minidom, shutil, posixpath | |
9 | import stat, subprocess, tarfile |
|
9 | import stat, subprocess, tarfile | |
10 | from i18n import _ |
|
10 | from i18n import _ | |
11 |
import config, scmutil, util, node, error, cmdutil, |
|
11 | import config, scmutil, util, node, error, cmdutil, bookmarks | |
12 | hg = None |
|
12 | hg = None | |
13 | propertycache = util.propertycache |
|
13 | propertycache = util.propertycache | |
14 |
|
14 | |||
@@ -194,13 +194,13 b' def _abssource(repo, push=False, abort=T' | |||||
194 | """return pull/push path of repo - either based on parent repo .hgsub info |
|
194 | """return pull/push path of repo - either based on parent repo .hgsub info | |
195 | or on the top repo config. Abort or return None if no source found.""" |
|
195 | or on the top repo config. Abort or return None if no source found.""" | |
196 | if hasattr(repo, '_subparent'): |
|
196 | if hasattr(repo, '_subparent'): | |
197 |
source = u |
|
197 | source = util.url(repo._subsource) | |
198 | source.path = posixpath.normpath(source.path) |
|
198 | source.path = posixpath.normpath(source.path) | |
199 | if posixpath.isabs(source.path) or source.scheme: |
|
199 | if posixpath.isabs(source.path) or source.scheme: | |
200 | return str(source) |
|
200 | return str(source) | |
201 | parent = _abssource(repo._subparent, push, abort=False) |
|
201 | parent = _abssource(repo._subparent, push, abort=False) | |
202 | if parent: |
|
202 | if parent: | |
203 |
parent = u |
|
203 | parent = util.url(parent) | |
204 | parent.path = posixpath.join(parent.path, source.path) |
|
204 | parent.path = posixpath.join(parent.path, source.path) | |
205 | parent.path = posixpath.normpath(parent.path) |
|
205 | parent.path = posixpath.normpath(parent.path) | |
206 | return str(parent) |
|
206 | return str(parent) |
@@ -7,7 +7,7 b'' | |||||
7 |
|
7 | |||
8 | from i18n import _ |
|
8 | from i18n import _ | |
9 | import errno, getpass, os, socket, sys, tempfile, traceback |
|
9 | import errno, getpass, os, socket, sys, tempfile, traceback | |
10 |
import config, scmutil, util, error |
|
10 | import config, scmutil, util, error | |
11 |
|
11 | |||
12 | class ui(object): |
|
12 | class ui(object): | |
13 | def __init__(self, src=None): |
|
13 | def __init__(self, src=None): | |
@@ -111,7 +111,7 b' class ui(object):' | |||||
111 | % (n, p, self.configsource('paths', n))) |
|
111 | % (n, p, self.configsource('paths', n))) | |
112 | p = p.replace('%%', '%') |
|
112 | p = p.replace('%%', '%') | |
113 | p = util.expandpath(p) |
|
113 | p = util.expandpath(p) | |
114 |
if not u |
|
114 | if not util.hasscheme(p) and not os.path.isabs(p): | |
115 | p = os.path.normpath(os.path.join(root, p)) |
|
115 | p = os.path.normpath(os.path.join(root, p)) | |
116 | c.set("paths", n, p) |
|
116 | c.set("paths", n, p) | |
117 |
|
117 | |||
@@ -332,7 +332,7 b' class ui(object):' | |||||
332 |
|
332 | |||
333 | def expandpath(self, loc, default=None): |
|
333 | def expandpath(self, loc, default=None): | |
334 | """Return repository location relative to cwd or from [paths]""" |
|
334 | """Return repository location relative to cwd or from [paths]""" | |
335 |
if u |
|
335 | if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')): | |
336 | return loc |
|
336 | return loc | |
337 |
|
337 | |||
338 | path = self.config('paths', loc) |
|
338 | path = self.config('paths', loc) |
@@ -7,273 +7,11 b'' | |||||
7 | # This software may be used and distributed according to the terms of the |
|
7 | # This software may be used and distributed according to the terms of the | |
8 | # GNU General Public License version 2 or any later version. |
|
8 | # GNU General Public License version 2 or any later version. | |
9 |
|
9 | |||
10 |
import urllib, urllib2, httplib, os, socket, cStringIO |
|
10 | import urllib, urllib2, httplib, os, socket, cStringIO | |
11 | import __builtin__ |
|
11 | import __builtin__ | |
12 | from i18n import _ |
|
12 | from i18n import _ | |
13 | import keepalive, util |
|
13 | import keepalive, util | |
14 |
|
14 | |||
15 | class url(object): |
|
|||
16 | """Reliable URL parser. |
|
|||
17 |
|
||||
18 | This parses URLs and provides attributes for the following |
|
|||
19 | components: |
|
|||
20 |
|
||||
21 | <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment> |
|
|||
22 |
|
||||
23 | Missing components are set to None. The only exception is |
|
|||
24 | fragment, which is set to '' if present but empty. |
|
|||
25 |
|
||||
26 | If parsefragment is False, fragment is included in query. If |
|
|||
27 | parsequery is False, query is included in path. If both are |
|
|||
28 | False, both fragment and query are included in path. |
|
|||
29 |
|
||||
30 | See http://www.ietf.org/rfc/rfc2396.txt for more information. |
|
|||
31 |
|
||||
32 | Note that for backward compatibility reasons, bundle URLs do not |
|
|||
33 | take host names. That means 'bundle://../' has a path of '../'. |
|
|||
34 |
|
||||
35 | Examples: |
|
|||
36 |
|
||||
37 | >>> url('http://www.ietf.org/rfc/rfc2396.txt') |
|
|||
38 | <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'> |
|
|||
39 | >>> url('ssh://[::1]:2200//home/joe/repo') |
|
|||
40 | <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'> |
|
|||
41 | >>> url('file:///home/joe/repo') |
|
|||
42 | <url scheme: 'file', path: '/home/joe/repo'> |
|
|||
43 | >>> url('bundle:foo') |
|
|||
44 | <url scheme: 'bundle', path: 'foo'> |
|
|||
45 | >>> url('bundle://../foo') |
|
|||
46 | <url scheme: 'bundle', path: '../foo'> |
|
|||
47 | >>> url('c:\\\\foo\\\\bar') |
|
|||
48 | <url path: 'c:\\\\foo\\\\bar'> |
|
|||
49 |
|
||||
50 | Authentication credentials: |
|
|||
51 |
|
||||
52 | >>> url('ssh://joe:xyz@x/repo') |
|
|||
53 | <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'> |
|
|||
54 | >>> url('ssh://joe@x/repo') |
|
|||
55 | <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'> |
|
|||
56 |
|
||||
57 | Query strings and fragments: |
|
|||
58 |
|
||||
59 | >>> url('http://host/a?b#c') |
|
|||
60 | <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'> |
|
|||
61 | >>> url('http://host/a?b#c', parsequery=False, parsefragment=False) |
|
|||
62 | <url scheme: 'http', host: 'host', path: 'a?b#c'> |
|
|||
63 | """ |
|
|||
64 |
|
||||
65 | _safechars = "!~*'()+" |
|
|||
66 | _safepchars = "/!~*'()+" |
|
|||
67 | _matchscheme = re.compile(r'^[a-zA-Z0-9+.\-]+:').match |
|
|||
68 |
|
||||
69 | def __init__(self, path, parsequery=True, parsefragment=True): |
|
|||
70 | # We slowly chomp away at path until we have only the path left |
|
|||
71 | self.scheme = self.user = self.passwd = self.host = None |
|
|||
72 | self.port = self.path = self.query = self.fragment = None |
|
|||
73 | self._localpath = True |
|
|||
74 | self._hostport = '' |
|
|||
75 | self._origpath = path |
|
|||
76 |
|
||||
77 | # special case for Windows drive letters |
|
|||
78 | if hasdriveletter(path): |
|
|||
79 | self.path = path |
|
|||
80 | return |
|
|||
81 |
|
||||
82 | # For compatibility reasons, we can't handle bundle paths as |
|
|||
83 | # normal URLS |
|
|||
84 | if path.startswith('bundle:'): |
|
|||
85 | self.scheme = 'bundle' |
|
|||
86 | path = path[7:] |
|
|||
87 | if path.startswith('//'): |
|
|||
88 | path = path[2:] |
|
|||
89 | self.path = path |
|
|||
90 | return |
|
|||
91 |
|
||||
92 | if self._matchscheme(path): |
|
|||
93 | parts = path.split(':', 1) |
|
|||
94 | if parts[0]: |
|
|||
95 | self.scheme, path = parts |
|
|||
96 | self._localpath = False |
|
|||
97 |
|
||||
98 | if not path: |
|
|||
99 | path = None |
|
|||
100 | if self._localpath: |
|
|||
101 | self.path = '' |
|
|||
102 | return |
|
|||
103 | else: |
|
|||
104 | if parsefragment and '#' in path: |
|
|||
105 | path, self.fragment = path.split('#', 1) |
|
|||
106 | if not path: |
|
|||
107 | path = None |
|
|||
108 | if self._localpath: |
|
|||
109 | self.path = path |
|
|||
110 | return |
|
|||
111 |
|
||||
112 | if parsequery and '?' in path: |
|
|||
113 | path, self.query = path.split('?', 1) |
|
|||
114 | if not path: |
|
|||
115 | path = None |
|
|||
116 | if not self.query: |
|
|||
117 | self.query = None |
|
|||
118 |
|
||||
119 | # // is required to specify a host/authority |
|
|||
120 | if path and path.startswith('//'): |
|
|||
121 | parts = path[2:].split('/', 1) |
|
|||
122 | if len(parts) > 1: |
|
|||
123 | self.host, path = parts |
|
|||
124 | path = path |
|
|||
125 | else: |
|
|||
126 | self.host = parts[0] |
|
|||
127 | path = None |
|
|||
128 | if not self.host: |
|
|||
129 | self.host = None |
|
|||
130 | if path: |
|
|||
131 | path = '/' + path |
|
|||
132 |
|
||||
133 | if self.host and '@' in self.host: |
|
|||
134 | self.user, self.host = self.host.rsplit('@', 1) |
|
|||
135 | if ':' in self.user: |
|
|||
136 | self.user, self.passwd = self.user.split(':', 1) |
|
|||
137 | if not self.host: |
|
|||
138 | self.host = None |
|
|||
139 |
|
||||
140 | # Don't split on colons in IPv6 addresses without ports |
|
|||
141 | if (self.host and ':' in self.host and |
|
|||
142 | not (self.host.startswith('[') and self.host.endswith(']'))): |
|
|||
143 | self._hostport = self.host |
|
|||
144 | self.host, self.port = self.host.rsplit(':', 1) |
|
|||
145 | if not self.host: |
|
|||
146 | self.host = None |
|
|||
147 |
|
||||
148 | if (self.host and self.scheme == 'file' and |
|
|||
149 | self.host not in ('localhost', '127.0.0.1', '[::1]')): |
|
|||
150 | raise util.Abort(_('file:// URLs can only refer to localhost')) |
|
|||
151 |
|
||||
152 | self.path = path |
|
|||
153 |
|
||||
154 | for a in ('user', 'passwd', 'host', 'port', |
|
|||
155 | 'path', 'query', 'fragment'): |
|
|||
156 | v = getattr(self, a) |
|
|||
157 | if v is not None: |
|
|||
158 | setattr(self, a, urllib.unquote(v)) |
|
|||
159 |
|
||||
160 | def __repr__(self): |
|
|||
161 | attrs = [] |
|
|||
162 | for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path', |
|
|||
163 | 'query', 'fragment'): |
|
|||
164 | v = getattr(self, a) |
|
|||
165 | if v is not None: |
|
|||
166 | attrs.append('%s: %r' % (a, v)) |
|
|||
167 | return '<url %s>' % ', '.join(attrs) |
|
|||
168 |
|
||||
169 | def __str__(self): |
|
|||
170 | """Join the URL's components back into a URL string. |
|
|||
171 |
|
||||
172 | Examples: |
|
|||
173 |
|
||||
174 | >>> str(url('http://user:pw@host:80/?foo#bar')) |
|
|||
175 | 'http://user:pw@host:80/?foo#bar' |
|
|||
176 | >>> str(url('ssh://user:pw@[::1]:2200//home/joe#')) |
|
|||
177 | 'ssh://user:pw@[::1]:2200//home/joe#' |
|
|||
178 | >>> str(url('http://localhost:80//')) |
|
|||
179 | 'http://localhost:80//' |
|
|||
180 | >>> str(url('http://localhost:80/')) |
|
|||
181 | 'http://localhost:80/' |
|
|||
182 | >>> str(url('http://localhost:80')) |
|
|||
183 | 'http://localhost:80/' |
|
|||
184 | >>> str(url('bundle:foo')) |
|
|||
185 | 'bundle:foo' |
|
|||
186 | >>> str(url('bundle://../foo')) |
|
|||
187 | 'bundle:../foo' |
|
|||
188 | >>> str(url('path')) |
|
|||
189 | 'path' |
|
|||
190 | """ |
|
|||
191 | if self._localpath: |
|
|||
192 | s = self.path |
|
|||
193 | if self.scheme == 'bundle': |
|
|||
194 | s = 'bundle:' + s |
|
|||
195 | if self.fragment: |
|
|||
196 | s += '#' + self.fragment |
|
|||
197 | return s |
|
|||
198 |
|
||||
199 | s = self.scheme + ':' |
|
|||
200 | if (self.user or self.passwd or self.host or |
|
|||
201 | self.scheme and not self.path): |
|
|||
202 | s += '//' |
|
|||
203 | if self.user: |
|
|||
204 | s += urllib.quote(self.user, safe=self._safechars) |
|
|||
205 | if self.passwd: |
|
|||
206 | s += ':' + urllib.quote(self.passwd, safe=self._safechars) |
|
|||
207 | if self.user or self.passwd: |
|
|||
208 | s += '@' |
|
|||
209 | if self.host: |
|
|||
210 | if not (self.host.startswith('[') and self.host.endswith(']')): |
|
|||
211 | s += urllib.quote(self.host) |
|
|||
212 | else: |
|
|||
213 | s += self.host |
|
|||
214 | if self.port: |
|
|||
215 | s += ':' + urllib.quote(self.port) |
|
|||
216 | if self.host: |
|
|||
217 | s += '/' |
|
|||
218 | if self.path: |
|
|||
219 | s += urllib.quote(self.path, safe=self._safepchars) |
|
|||
220 | if self.query: |
|
|||
221 | s += '?' + urllib.quote(self.query, safe=self._safepchars) |
|
|||
222 | if self.fragment is not None: |
|
|||
223 | s += '#' + urllib.quote(self.fragment, safe=self._safepchars) |
|
|||
224 | return s |
|
|||
225 |
|
||||
226 | def authinfo(self): |
|
|||
227 | user, passwd = self.user, self.passwd |
|
|||
228 | try: |
|
|||
229 | self.user, self.passwd = None, None |
|
|||
230 | s = str(self) |
|
|||
231 | finally: |
|
|||
232 | self.user, self.passwd = user, passwd |
|
|||
233 | if not self.user: |
|
|||
234 | return (s, None) |
|
|||
235 | return (s, (None, (str(self), self.host), |
|
|||
236 | self.user, self.passwd or '')) |
|
|||
237 |
|
||||
238 | def localpath(self): |
|
|||
239 | if self.scheme == 'file' or self.scheme == 'bundle': |
|
|||
240 | path = self.path or '/' |
|
|||
241 | # For Windows, we need to promote hosts containing drive |
|
|||
242 | # letters to paths with drive letters. |
|
|||
243 | if hasdriveletter(self._hostport): |
|
|||
244 | path = self._hostport + '/' + self.path |
|
|||
245 | elif self.host is not None and self.path: |
|
|||
246 | path = '/' + path |
|
|||
247 | # We also need to handle the case of file:///C:/, which |
|
|||
248 | # should return C:/, not /C:/. |
|
|||
249 | elif hasdriveletter(path): |
|
|||
250 | # Strip leading slash from paths with drive names |
|
|||
251 | return path[1:] |
|
|||
252 | return path |
|
|||
253 | return self._origpath |
|
|||
254 |
|
||||
255 | def hasscheme(path): |
|
|||
256 | return bool(url(path).scheme) |
|
|||
257 |
|
||||
258 | def hasdriveletter(path): |
|
|||
259 | return path[1:2] == ':' and path[0:1].isalpha() |
|
|||
260 |
|
||||
261 | def localpath(path): |
|
|||
262 | return url(path, parsequery=False, parsefragment=False).localpath() |
|
|||
263 |
|
||||
264 | def hidepassword(u): |
|
|||
265 | '''hide user credential in a url string''' |
|
|||
266 | u = url(u) |
|
|||
267 | if u.passwd: |
|
|||
268 | u.passwd = '***' |
|
|||
269 | return str(u) |
|
|||
270 |
|
||||
271 | def removeauth(u): |
|
|||
272 | '''remove all authentication information from a url string''' |
|
|||
273 | u = url(u) |
|
|||
274 | u.user = u.passwd = None |
|
|||
275 | return str(u) |
|
|||
276 |
|
||||
277 | def readauthforuri(ui, uri): |
|
15 | def readauthforuri(ui, uri): | |
278 | # Read configuration |
|
16 | # Read configuration | |
279 | config = dict() |
|
17 | config = dict() | |
@@ -357,7 +95,7 b' class proxyhandler(urllib2.ProxyHandler)' | |||||
357 | if not (proxyurl.startswith('http:') or |
|
95 | if not (proxyurl.startswith('http:') or | |
358 | proxyurl.startswith('https:')): |
|
96 | proxyurl.startswith('https:')): | |
359 | proxyurl = 'http://' + proxyurl + '/' |
|
97 | proxyurl = 'http://' + proxyurl + '/' | |
360 | proxy = url(proxyurl) |
|
98 | proxy = util.url(proxyurl) | |
361 | if not proxy.user: |
|
99 | if not proxy.user: | |
362 | proxy.user = ui.config("http_proxy", "user") |
|
100 | proxy.user = ui.config("http_proxy", "user") | |
363 | proxy.passwd = ui.config("http_proxy", "passwd") |
|
101 | proxy.passwd = ui.config("http_proxy", "passwd") | |
@@ -545,7 +283,7 b' def _generic_start_transaction(handler, ' | |||||
545 | new_tunnel = False |
|
283 | new_tunnel = False | |
546 |
|
284 | |||
547 | if new_tunnel or tunnel_host == req.get_full_url(): # has proxy |
|
285 | if new_tunnel or tunnel_host == req.get_full_url(): # has proxy | |
548 | u = url(tunnel_host) |
|
286 | u = util.url(tunnel_host) | |
549 | if new_tunnel or u.scheme == 'https': # only use CONNECT for HTTPS |
|
287 | if new_tunnel or u.scheme == 'https': # only use CONNECT for HTTPS | |
550 | h.realhostport = ':'.join([u.host, (u.port or '443')]) |
|
288 | h.realhostport = ':'.join([u.host, (u.port or '443')]) | |
551 | h.headers = req.headers.copy() |
|
289 | h.headers = req.headers.copy() | |
@@ -876,7 +614,7 b' def opener(ui, authinfo=None):' | |||||
876 | return opener |
|
614 | return opener | |
877 |
|
615 | |||
878 | def open(ui, url_, data=None): |
|
616 | def open(ui, url_, data=None): | |
879 | u = url(url_) |
|
617 | u = util.url(url_) | |
880 | if u.scheme: |
|
618 | if u.scheme: | |
881 | u.scheme = u.scheme.lower() |
|
619 | u.scheme = u.scheme.lower() | |
882 | url_, authinfo = u.authinfo() |
|
620 | url_, authinfo = u.authinfo() |
@@ -17,7 +17,7 b' from i18n import _' | |||||
17 | import error, osutil, encoding |
|
17 | import error, osutil, encoding | |
18 | import errno, re, shutil, sys, tempfile, traceback |
|
18 | import errno, re, shutil, sys, tempfile, traceback | |
19 | import os, time, calendar, textwrap, unicodedata, signal |
|
19 | import os, time, calendar, textwrap, unicodedata, signal | |
20 | import imp, socket |
|
20 | import imp, socket, urllib | |
21 |
|
21 | |||
22 | # Python compatibility |
|
22 | # Python compatibility | |
23 |
|
23 | |||
@@ -1283,3 +1283,265 b' def parsebool(s):' | |||||
1283 | If s is not a valid boolean, returns None. |
|
1283 | If s is not a valid boolean, returns None. | |
1284 | """ |
|
1284 | """ | |
1285 | return _booleans.get(s.lower(), None) |
|
1285 | return _booleans.get(s.lower(), None) | |
|
1286 | ||||
|
1287 | class url(object): | |||
|
1288 | """Reliable URL parser. | |||
|
1289 | ||||
|
1290 | This parses URLs and provides attributes for the following | |||
|
1291 | components: | |||
|
1292 | ||||
|
1293 | <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment> | |||
|
1294 | ||||
|
1295 | Missing components are set to None. The only exception is | |||
|
1296 | fragment, which is set to '' if present but empty. | |||
|
1297 | ||||
|
1298 | If parsefragment is False, fragment is included in query. If | |||
|
1299 | parsequery is False, query is included in path. If both are | |||
|
1300 | False, both fragment and query are included in path. | |||
|
1301 | ||||
|
1302 | See http://www.ietf.org/rfc/rfc2396.txt for more information. | |||
|
1303 | ||||
|
1304 | Note that for backward compatibility reasons, bundle URLs do not | |||
|
1305 | take host names. That means 'bundle://../' has a path of '../'. | |||
|
1306 | ||||
|
1307 | Examples: | |||
|
1308 | ||||
|
1309 | >>> url('http://www.ietf.org/rfc/rfc2396.txt') | |||
|
1310 | <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'> | |||
|
1311 | >>> url('ssh://[::1]:2200//home/joe/repo') | |||
|
1312 | <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'> | |||
|
1313 | >>> url('file:///home/joe/repo') | |||
|
1314 | <url scheme: 'file', path: '/home/joe/repo'> | |||
|
1315 | >>> url('bundle:foo') | |||
|
1316 | <url scheme: 'bundle', path: 'foo'> | |||
|
1317 | >>> url('bundle://../foo') | |||
|
1318 | <url scheme: 'bundle', path: '../foo'> | |||
|
1319 | >>> url('c:\\\\foo\\\\bar') | |||
|
1320 | <url path: 'c:\\\\foo\\\\bar'> | |||
|
1321 | ||||
|
1322 | Authentication credentials: | |||
|
1323 | ||||
|
1324 | >>> url('ssh://joe:xyz@x/repo') | |||
|
1325 | <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'> | |||
|
1326 | >>> url('ssh://joe@x/repo') | |||
|
1327 | <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'> | |||
|
1328 | ||||
|
1329 | Query strings and fragments: | |||
|
1330 | ||||
|
1331 | >>> url('http://host/a?b#c') | |||
|
1332 | <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'> | |||
|
1333 | >>> url('http://host/a?b#c', parsequery=False, parsefragment=False) | |||
|
1334 | <url scheme: 'http', host: 'host', path: 'a?b#c'> | |||
|
1335 | """ | |||
|
1336 | ||||
|
1337 | _safechars = "!~*'()+" | |||
|
1338 | _safepchars = "/!~*'()+" | |||
|
1339 | _matchscheme = re.compile(r'^[a-zA-Z0-9+.\-]+:').match | |||
|
1340 | ||||
|
1341 | def __init__(self, path, parsequery=True, parsefragment=True): | |||
|
1342 | # We slowly chomp away at path until we have only the path left | |||
|
1343 | self.scheme = self.user = self.passwd = self.host = None | |||
|
1344 | self.port = self.path = self.query = self.fragment = None | |||
|
1345 | self._localpath = True | |||
|
1346 | self._hostport = '' | |||
|
1347 | self._origpath = path | |||
|
1348 | ||||
|
1349 | # special case for Windows drive letters | |||
|
1350 | if hasdriveletter(path): | |||
|
1351 | self.path = path | |||
|
1352 | return | |||
|
1353 | ||||
|
1354 | # For compatibility reasons, we can't handle bundle paths as | |||
|
1355 | # normal URLS | |||
|
1356 | if path.startswith('bundle:'): | |||
|
1357 | self.scheme = 'bundle' | |||
|
1358 | path = path[7:] | |||
|
1359 | if path.startswith('//'): | |||
|
1360 | path = path[2:] | |||
|
1361 | self.path = path | |||
|
1362 | return | |||
|
1363 | ||||
|
1364 | if self._matchscheme(path): | |||
|
1365 | parts = path.split(':', 1) | |||
|
1366 | if parts[0]: | |||
|
1367 | self.scheme, path = parts | |||
|
1368 | self._localpath = False | |||
|
1369 | ||||
|
1370 | if not path: | |||
|
1371 | path = None | |||
|
1372 | if self._localpath: | |||
|
1373 | self.path = '' | |||
|
1374 | return | |||
|
1375 | else: | |||
|
1376 | if parsefragment and '#' in path: | |||
|
1377 | path, self.fragment = path.split('#', 1) | |||
|
1378 | if not path: | |||
|
1379 | path = None | |||
|
1380 | if self._localpath: | |||
|
1381 | self.path = path | |||
|
1382 | return | |||
|
1383 | ||||
|
1384 | if parsequery and '?' in path: | |||
|
1385 | path, self.query = path.split('?', 1) | |||
|
1386 | if not path: | |||
|
1387 | path = None | |||
|
1388 | if not self.query: | |||
|
1389 | self.query = None | |||
|
1390 | ||||
|
1391 | # // is required to specify a host/authority | |||
|
1392 | if path and path.startswith('//'): | |||
|
1393 | parts = path[2:].split('/', 1) | |||
|
1394 | if len(parts) > 1: | |||
|
1395 | self.host, path = parts | |||
|
1396 | path = path | |||
|
1397 | else: | |||
|
1398 | self.host = parts[0] | |||
|
1399 | path = None | |||
|
1400 | if not self.host: | |||
|
1401 | self.host = None | |||
|
1402 | if path: | |||
|
1403 | path = '/' + path | |||
|
1404 | ||||
|
1405 | if self.host and '@' in self.host: | |||
|
1406 | self.user, self.host = self.host.rsplit('@', 1) | |||
|
1407 | if ':' in self.user: | |||
|
1408 | self.user, self.passwd = self.user.split(':', 1) | |||
|
1409 | if not self.host: | |||
|
1410 | self.host = None | |||
|
1411 | ||||
|
1412 | # Don't split on colons in IPv6 addresses without ports | |||
|
1413 | if (self.host and ':' in self.host and | |||
|
1414 | not (self.host.startswith('[') and self.host.endswith(']'))): | |||
|
1415 | self._hostport = self.host | |||
|
1416 | self.host, self.port = self.host.rsplit(':', 1) | |||
|
1417 | if not self.host: | |||
|
1418 | self.host = None | |||
|
1419 | ||||
|
1420 | if (self.host and self.scheme == 'file' and | |||
|
1421 | self.host not in ('localhost', '127.0.0.1', '[::1]')): | |||
|
1422 | raise Abort(_('file:// URLs can only refer to localhost')) | |||
|
1423 | ||||
|
1424 | self.path = path | |||
|
1425 | ||||
|
1426 | for a in ('user', 'passwd', 'host', 'port', | |||
|
1427 | 'path', 'query', 'fragment'): | |||
|
1428 | v = getattr(self, a) | |||
|
1429 | if v is not None: | |||
|
1430 | setattr(self, a, urllib.unquote(v)) | |||
|
1431 | ||||
|
1432 | def __repr__(self): | |||
|
1433 | attrs = [] | |||
|
1434 | for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path', | |||
|
1435 | 'query', 'fragment'): | |||
|
1436 | v = getattr(self, a) | |||
|
1437 | if v is not None: | |||
|
1438 | attrs.append('%s: %r' % (a, v)) | |||
|
1439 | return '<url %s>' % ', '.join(attrs) | |||
|
1440 | ||||
|
1441 | def __str__(self): | |||
|
1442 | """Join the URL's components back into a URL string. | |||
|
1443 | ||||
|
1444 | Examples: | |||
|
1445 | ||||
|
1446 | >>> str(url('http://user:pw@host:80/?foo#bar')) | |||
|
1447 | 'http://user:pw@host:80/?foo#bar' | |||
|
1448 | >>> str(url('ssh://user:pw@[::1]:2200//home/joe#')) | |||
|
1449 | 'ssh://user:pw@[::1]:2200//home/joe#' | |||
|
1450 | >>> str(url('http://localhost:80//')) | |||
|
1451 | 'http://localhost:80//' | |||
|
1452 | >>> str(url('http://localhost:80/')) | |||
|
1453 | 'http://localhost:80/' | |||
|
1454 | >>> str(url('http://localhost:80')) | |||
|
1455 | 'http://localhost:80/' | |||
|
1456 | >>> str(url('bundle:foo')) | |||
|
1457 | 'bundle:foo' | |||
|
1458 | >>> str(url('bundle://../foo')) | |||
|
1459 | 'bundle:../foo' | |||
|
1460 | >>> str(url('path')) | |||
|
1461 | 'path' | |||
|
1462 | """ | |||
|
1463 | if self._localpath: | |||
|
1464 | s = self.path | |||
|
1465 | if self.scheme == 'bundle': | |||
|
1466 | s = 'bundle:' + s | |||
|
1467 | if self.fragment: | |||
|
1468 | s += '#' + self.fragment | |||
|
1469 | return s | |||
|
1470 | ||||
|
1471 | s = self.scheme + ':' | |||
|
1472 | if (self.user or self.passwd or self.host or | |||
|
1473 | self.scheme and not self.path): | |||
|
1474 | s += '//' | |||
|
1475 | if self.user: | |||
|
1476 | s += urllib.quote(self.user, safe=self._safechars) | |||
|
1477 | if self.passwd: | |||
|
1478 | s += ':' + urllib.quote(self.passwd, safe=self._safechars) | |||
|
1479 | if self.user or self.passwd: | |||
|
1480 | s += '@' | |||
|
1481 | if self.host: | |||
|
1482 | if not (self.host.startswith('[') and self.host.endswith(']')): | |||
|
1483 | s += urllib.quote(self.host) | |||
|
1484 | else: | |||
|
1485 | s += self.host | |||
|
1486 | if self.port: | |||
|
1487 | s += ':' + urllib.quote(self.port) | |||
|
1488 | if self.host: | |||
|
1489 | s += '/' | |||
|
1490 | if self.path: | |||
|
1491 | s += urllib.quote(self.path, safe=self._safepchars) | |||
|
1492 | if self.query: | |||
|
1493 | s += '?' + urllib.quote(self.query, safe=self._safepchars) | |||
|
1494 | if self.fragment is not None: | |||
|
1495 | s += '#' + urllib.quote(self.fragment, safe=self._safepchars) | |||
|
1496 | return s | |||
|
1497 | ||||
|
1498 | def authinfo(self): | |||
|
1499 | user, passwd = self.user, self.passwd | |||
|
1500 | try: | |||
|
1501 | self.user, self.passwd = None, None | |||
|
1502 | s = str(self) | |||
|
1503 | finally: | |||
|
1504 | self.user, self.passwd = user, passwd | |||
|
1505 | if not self.user: | |||
|
1506 | return (s, None) | |||
|
1507 | return (s, (None, (str(self), self.host), | |||
|
1508 | self.user, self.passwd or '')) | |||
|
1509 | ||||
|
1510 | def localpath(self): | |||
|
1511 | if self.scheme == 'file' or self.scheme == 'bundle': | |||
|
1512 | path = self.path or '/' | |||
|
1513 | # For Windows, we need to promote hosts containing drive | |||
|
1514 | # letters to paths with drive letters. | |||
|
1515 | if hasdriveletter(self._hostport): | |||
|
1516 | path = self._hostport + '/' + self.path | |||
|
1517 | elif self.host is not None and self.path: | |||
|
1518 | path = '/' + path | |||
|
1519 | # We also need to handle the case of file:///C:/, which | |||
|
1520 | # should return C:/, not /C:/. | |||
|
1521 | elif hasdriveletter(path): | |||
|
1522 | # Strip leading slash from paths with drive names | |||
|
1523 | return path[1:] | |||
|
1524 | return path | |||
|
1525 | return self._origpath | |||
|
1526 | ||||
|
1527 | def hasscheme(path): | |||
|
1528 | return bool(url(path).scheme) | |||
|
1529 | ||||
|
1530 | def hasdriveletter(path): | |||
|
1531 | return path[1:2] == ':' and path[0:1].isalpha() | |||
|
1532 | ||||
|
1533 | def localpath(path): | |||
|
1534 | return url(path, parsequery=False, parsefragment=False).localpath() | |||
|
1535 | ||||
|
1536 | def hidepassword(u): | |||
|
1537 | '''hide user credential in a url string''' | |||
|
1538 | u = url(u) | |||
|
1539 | if u.passwd: | |||
|
1540 | u.passwd = '***' | |||
|
1541 | return str(u) | |||
|
1542 | ||||
|
1543 | def removeauth(u): | |||
|
1544 | '''remove all authentication information from a url string''' | |||
|
1545 | u = url(u) | |||
|
1546 | u.user = u.passwd = None | |||
|
1547 | return str(u) |
@@ -53,7 +53,7 b' import doctest' | |||||
53 |
|
53 | |||
54 | def test_url(): |
|
54 | def test_url(): | |
55 | """ |
|
55 | """ | |
56 |
>>> from mercurial.u |
|
56 | >>> from mercurial.util import url | |
57 |
|
57 | |||
58 | This tests for edge cases in url.URL's parsing algorithm. Most of |
|
58 | This tests for edge cases in url.URL's parsing algorithm. Most of | |
59 | these aren't useful for documentation purposes, so they aren't |
|
59 | these aren't useful for documentation purposes, so they aren't |
General Comments 0
You need to be logged in to leave comments.
Login now