# HG changeset patch # User Patrick Mezard # Date 1259012808 -3600 # Node ID 311eeb2f6951f640cc0d5aa1a2a3ccba1d71a128 # Parent 6f92997dbdca1cec58eea2b43d86f238cae00471# Parent 58bdcab55bdcbd8be842979a47d47ac9f7ab7e22 Merge with crew-stable diff -r 58bdcab55bdc -r 311eeb2f6951 hgext/convert/filemap.py --- a/hgext/convert/filemap.py Mon Nov 23 22:08:59 2009 +0100 +++ b/hgext/convert/filemap.py Mon Nov 23 22:46:48 2009 +0100 @@ -10,11 +10,11 @@ from common import SKIPREV, converter_source def rpairs(name): - yield '.', name e = len(name) while e != -1: yield name[:e], name[e+1:] e = name.rfind('/', 0, e) + yield '.', name class filemapper(object): '''Map and filter filenames when importing. @@ -82,7 +82,7 @@ exc = self.lookup(name, self.exclude)[0] else: exc = '' - if not inc or exc: + if (not self.include and exc) or (len(inc) <= len(exc)): return None newpre, pre, suf = self.lookup(name, self.rename) if newpre: diff -r 58bdcab55bdc -r 311eeb2f6951 hgext/relink.py --- a/hgext/relink.py Mon Nov 23 22:08:59 2009 +0100 +++ b/hgext/relink.py Mon Nov 23 22:46:48 2009 +0100 @@ -14,25 +14,27 @@ def relink(ui, repo, origin=None, **opts): """recreate hardlinks between two repositories - When repositories are cloned locally, their data files will be hardlinked - so that they only use the space of a single repository. + When repositories are cloned locally, their data files will be + hardlinked so that they only use the space of a single repository. - Unfortunately, subsequent pulls into either repository will break hardlinks - for any files touched by the new changesets, even if both repositories end - up pulling the same changes. + Unfortunately, subsequent pulls into either repository will break + hardlinks for any files touched by the new changesets, even if + both repositories end up pulling the same changes. - Similarly, passing --rev to "hg clone" will fail to use - any hardlinks, falling back to a complete copy of the source repository. + Similarly, passing --rev to "hg clone" will fail to use any + hardlinks, falling back to a complete copy of the source + repository. - This command lets you recreate those hardlinks and reclaim that wasted - space. + This command lets you recreate those hardlinks and reclaim that + wasted space. - This repository will be relinked to share space with ORIGIN, which must be - on the same local disk. If ORIGIN is omitted, looks for "default-relink", - then "default", in [paths]. + This repository will be relinked to share space with ORIGIN, which + must be on the same local disk. If ORIGIN is omitted, looks for + "default-relink", then "default", in [paths]. - Do not attempt any read operations on this repository while the command is - running. (Both repositories will be locked against writes.) + Do not attempt any read operations on this repository while the + command is running. (Both repositories will be locked against + writes.) """ src = hg.repository( cmdutil.remoteui(repo, opts), diff -r 58bdcab55bdc -r 311eeb2f6951 mercurial/commands.py --- a/mercurial/commands.py Mon Nov 23 22:08:59 2009 +0100 +++ b/mercurial/commands.py Mon Nov 23 22:46:48 2009 +0100 @@ -1476,7 +1476,7 @@ ui.write('\n') try: - aliases, i = cmdutil.findcmd(name, table, False) + aliases, entry = cmdutil.findcmd(name, table, False) except error.AmbiguousCommand, inst: # py3k fix: except vars can't be used outside the scope of the # except block, nor can be used inside a lambda. python issue4617 @@ -1486,11 +1486,11 @@ return # synopsis - if len(i) > 2: - if i[2].startswith('hg'): - ui.write("%s\n" % i[2]) + if len(entry) > 2: + if entry[2].startswith('hg'): + ui.write("%s\n" % entry[2]) else: - ui.write('hg %s %s\n' % (aliases[0], i[2])) + ui.write('hg %s %s\n' % (aliases[0], entry[2])) else: ui.write('hg %s\n' % aliases[0]) @@ -1499,7 +1499,7 @@ ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:])) # description - doc = gettext(i[0].__doc__) + doc = gettext(entry[0].__doc__) if not doc: doc = _("(no help text available)") if ui.quiet: @@ -1508,8 +1508,8 @@ if not ui.quiet: # options - if i[1]: - option_lists.append((_("options:\n"), i[1])) + if entry[1]: + option_lists.append((_("options:\n"), entry[1])) addglobalopts(False) diff -r 58bdcab55bdc -r 311eeb2f6951 mercurial/dispatch.py --- a/mercurial/dispatch.py Mon Nov 23 22:08:59 2009 +0100 +++ b/mercurial/dispatch.py Mon Nov 23 22:46:48 2009 +0100 @@ -200,6 +200,12 @@ self.args = aliasargs(self.fn) + args if cmd not in commands.norepo.split(' '): self.norepo = False + if self.help.startswith("hg " + cmd): + # drop prefix in old-style help lines so hg shows the alias + self.help = self.help[4 + len(cmd):] + self.__doc__ = _("alias for: hg %s\n\n%s") \ + % (definition, self.fn.__doc__) + except error.UnknownCommand: def fn(ui, *args): ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \ @@ -240,14 +246,14 @@ if args: cmd, args = args[0], args[1:] - aliases, i = cmdutil.findcmd(cmd, commands.table, + aliases, entry = cmdutil.findcmd(cmd, commands.table, ui.config("ui", "strict")) cmd = aliases[0] - args = aliasargs(i[0]) + args + args = aliasargs(entry[0]) + args defaults = ui.config("defaults", cmd) if defaults: args = map(util.expandpath, shlex.split(defaults)) + args - c = list(i[1]) + c = list(entry[1]) else: cmd = None c = [] @@ -267,7 +273,7 @@ options[n] = cmdoptions[n] del cmdoptions[n] - return (cmd, cmd and i[0] or None, args, options, cmdoptions) + return (cmd, cmd and entry[0] or None, args, options, cmdoptions) def _parseconfig(ui, config): """parse the --config options from the command line""" diff -r 58bdcab55bdc -r 311eeb2f6951 mercurial/hgweb/common.py --- a/mercurial/hgweb/common.py Mon Nov 23 22:08:59 2009 +0100 +++ b/mercurial/hgweb/common.py Mon Nov 23 22:46:48 2009 +0100 @@ -16,6 +16,58 @@ HTTP_METHOD_NOT_ALLOWED = 405 HTTP_SERVER_ERROR = 500 +# Hooks for hgweb permission checks; extensions can add hooks here. Each hook +# is invoked like this: hook(hgweb, request, operation), where operation is +# either read, pull or push. Hooks should either raise an ErrorResponse +# exception, or just return. +# It is possible to do both authentication and authorization through this. +permhooks = [] + +def checkauthz(hgweb, req, op): + '''Check permission for operation based on request data (including + authentication info). Return if op allowed, else raise an ErrorResponse + exception.''' + + user = req.env.get('REMOTE_USER') + + deny_read = hgweb.configlist('web', 'deny_read') + if deny_read and (not user or deny_read == ['*'] or user in deny_read): + raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') + + allow_read = hgweb.configlist('web', 'allow_read') + result = (not allow_read) or (allow_read == ['*']) + if not (result or user in allow_read): + raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') + + if op == 'pull' and not hgweb.allowpull: + raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized') + elif op == 'pull' or op is None: # op is None for interface requests + return + + # enforce that you can only push using POST requests + if req.env['REQUEST_METHOD'] != 'POST': + msg = 'push requires POST request' + raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg) + + # require ssl by default for pushing, auth info cannot be sniffed + # and replayed + scheme = req.env.get('wsgi.url_scheme') + if hgweb.configbool('web', 'push_ssl', True) and scheme != 'https': + raise ErrorResponse(HTTP_OK, 'ssl required') + + deny = hgweb.configlist('web', 'deny_push') + if deny and (not user or deny == ['*'] or user in deny): + raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') + + allow = hgweb.configlist('web', 'allow_push') + result = allow and (allow == ['*'] or user in allow) + if not result: + raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') + +# Add the default permhook, which provides simple authorization. +permhooks.append(checkauthz) + + class ErrorResponse(Exception): def __init__(self, code, message=None, headers=[]): Exception.__init__(self) diff -r 58bdcab55bdc -r 311eeb2f6951 mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py Mon Nov 23 22:08:59 2009 +0100 +++ b/mercurial/hgweb/hgweb_mod.py Mon Nov 23 22:46:48 2009 +0100 @@ -8,7 +8,7 @@ import os from mercurial import ui, hg, hook, error, encoding, templater -from common import get_mtime, ErrorResponse +from common import get_mtime, ErrorResponse, permhooks from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR from common import HTTP_UNAUTHORIZED, HTTP_METHOD_NOT_ALLOWED from request import wsgirequest @@ -54,7 +54,9 @@ return self.repo.ui.configlist(section, name, default, untrusted=untrusted) - def refresh(self): + def refresh(self, request=None): + if request: + self.repo.ui.environ = request.env mtime = get_mtime(self.repo.root) if mtime != self.mtime: self.mtime = mtime @@ -80,7 +82,7 @@ def run_wsgi(self, req): - self.refresh() + self.refresh(req) # work with CGI variables to create coherent structure # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME @@ -281,42 +283,5 @@ } def check_perm(self, req, op): - '''Check permission for operation based on request data (including - authentication info). Return if op allowed, else raise an ErrorResponse - exception.''' - - user = req.env.get('REMOTE_USER') - - deny_read = self.configlist('web', 'deny_read') - if deny_read and (not user or deny_read == ['*'] or user in deny_read): - raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') - - allow_read = self.configlist('web', 'allow_read') - result = (not allow_read) or (allow_read == ['*']) - if not (result or user in allow_read): - raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') - - if op == 'pull' and not self.allowpull: - raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized') - elif op == 'pull' or op is None: # op is None for interface requests - return - - # enforce that you can only push using POST requests - if req.env['REQUEST_METHOD'] != 'POST': - msg = 'push requires POST request' - raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg) - - # require ssl by default for pushing, auth info cannot be sniffed - # and replayed - scheme = req.env.get('wsgi.url_scheme') - if self.configbool('web', 'push_ssl', True) and scheme != 'https': - raise ErrorResponse(HTTP_OK, 'ssl required') - - deny = self.configlist('web', 'deny_push') - if deny and (not user or deny == ['*'] or user in deny): - raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') - - allow = self.configlist('web', 'allow_push') - result = allow and (allow == ['*'] or user in allow) - if not result: - raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') + for hook in permhooks: + hook(self, req, op) diff -r 58bdcab55bdc -r 311eeb2f6951 mercurial/httprepo.py --- a/mercurial/httprepo.py Mon Nov 23 22:08:59 2009 +0100 +++ b/mercurial/httprepo.py Mon Nov 23 22:46:48 2009 +0100 @@ -93,7 +93,7 @@ resp_url = resp.geturl() if resp_url.endswith(qs): resp_url = resp_url[:-len(qs)] - if self._url != resp_url: + if self._url.rstrip('/') != resp_url.rstrip('/'): self.ui.status(_('real URL is %s\n') % resp_url) self._url = resp_url try: diff -r 58bdcab55bdc -r 311eeb2f6951 mercurial/ui.py --- a/mercurial/ui.py Mon Nov 23 22:08:59 2009 +0100 +++ b/mercurial/ui.py Mon Nov 23 22:46:48 2009 +0100 @@ -29,8 +29,11 @@ self._ocfg = src._ocfg.copy() self._trustusers = src._trustusers.copy() self._trustgroups = src._trustgroups.copy() + self.environ = src.environ self.fixconfig() else: + # shared read-only environment + self.environ = os.environ # we always trust global config files for f in util.rcpath(): self.readconfig(f, trust=True) diff -r 58bdcab55bdc -r 311eeb2f6951 templates/gitweb/fileannotate.tmpl --- a/templates/gitweb/fileannotate.tmpl Mon Nov 23 22:08:59 2009 +0100 +++ b/templates/gitweb/fileannotate.tmpl Mon Nov 23 22:46:48 2009 +0100 @@ -21,6 +21,7 @@ files | changeset | file | +latest | revisions | annotate | diff | diff -r 58bdcab55bdc -r 311eeb2f6951 templates/gitweb/filediff.tmpl --- a/templates/gitweb/filediff.tmpl Mon Nov 23 22:08:59 2009 +0100 +++ b/templates/gitweb/filediff.tmpl Mon Nov 23 22:46:48 2009 +0100 @@ -21,6 +21,7 @@ files | changeset | file | +latest | revisions | annotate | diff | diff -r 58bdcab55bdc -r 311eeb2f6951 templates/gitweb/filerevision.tmpl --- a/templates/gitweb/filerevision.tmpl Mon Nov 23 22:08:59 2009 +0100 +++ b/templates/gitweb/filerevision.tmpl Mon Nov 23 22:46:48 2009 +0100 @@ -21,6 +21,7 @@ files | changeset | file | +latest | revisions | annotate | diff | diff -r 58bdcab55bdc -r 311eeb2f6951 tests/run-tests.py --- a/tests/run-tests.py Mon Nov 23 22:08:59 2009 +0100 +++ b/tests/run-tests.py Mon Nov 23 22:46:48 2009 +0100 @@ -293,10 +293,18 @@ script = os.path.realpath(sys.argv[0]) hgroot = os.path.dirname(os.path.dirname(script)) os.chdir(hgroot) + nohome = '--home=""' + if os.name == 'nt': + # The --home="" trick works only on OS where os.sep == '/' + # because of a distutils convert_path() fast-path. Avoid it at + # least on Windows for now, deal with .pydistutils.cfg bugs + # when they happen. + nohome = '' cmd = ('%s setup.py %s clean --all' ' install --force --prefix="%s" --install-lib="%s"' - ' --install-scripts="%s" >%s 2>&1' - % (sys.executable, pure, INST, PYTHONDIR, BINDIR, installerrs)) + ' --install-scripts="%s" %s >%s 2>&1' + % (sys.executable, pure, INST, PYTHONDIR, BINDIR, nohome, + installerrs)) vlog("# Running", cmd) if os.system(cmd) == 0: if not options.verbose: diff -r 58bdcab55bdc -r 311eeb2f6951 tests/test-convert-filemap --- a/tests/test-convert-filemap Mon Nov 23 22:08:59 2009 +0100 +++ b/tests/test-convert-filemap Mon Nov 23 22:46:48 2009 +0100 @@ -16,9 +16,11 @@ echo foo > foo echo baz > baz -mkdir dir +mkdir -p dir/subdir echo dir/file >> dir/file echo dir/file2 >> dir/file2 +echo dir/subdir/file3 >> dir/subdir/file3 +echo dir/subdir/file4 >> dir/subdir/file4 hg ci -d '0 0' -qAm '0: add foo baz dir/' echo bar > bar @@ -114,6 +116,8 @@ include copied rename foo foo2 rename copied copied2 +exclude dir/subdir +include dir/subdir/file3 EOF hg -q convert --filemap renames.fmap --datesort source renames.repo hg up -q -R renames.repo diff -r 58bdcab55bdc -r 311eeb2f6951 tests/test-convert-filemap.out --- a/tests/test-convert-filemap.out Mon Nov 23 22:08:59 2009 +0100 +++ b/tests/test-convert-filemap.out Mon Nov 23 22:46:48 2009 +0100 @@ -16,7 +16,7 @@ |/ o 1 "1: add bar quux; copy foo to copied" files: bar copied quux | -o 0 "0: add foo baz dir/" files: baz dir/file dir/file2 foo +o 0 "0: add foo baz dir/" files: baz dir/file dir/file2 dir/subdir/file3 dir/subdir/file4 foo % final file versions in this repo: 9463f52fe115e377cf2878d4fc548117211063f2 644 bar @@ -24,6 +24,8 @@ 6ca237634e1f6bee1b6db94292fb44f092a25842 644 copied 3e20847584beff41d7cd16136b7331ab3d754be0 644 dir/file 75e6d3f8328f5f6ace6bf10b98df793416a09dca 644 dir/file2 +5fe139720576e18e34bcc9f79174db8897c8afe9 644 dir/subdir/file3 +57a1c1511590f3de52874adfa04effe8a77d64af 644 dir/subdir/file4 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644 quux copied renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd @@ -144,10 +146,11 @@ | o 1 "1: add bar quux; copy foo to copied" files: copied2 | -o 0 "0: add foo baz dir/" files: dir2/file foo2 +o 0 "0: add foo baz dir/" files: dir2/file dir2/subdir/file3 foo2 e5e3d520be9be45937d0b06b004fadcd6c221fa2 644 copied2 3e20847584beff41d7cd16136b7331ab3d754be0 644 dir2/file +5fe139720576e18e34bcc9f79174db8897c8afe9 644 dir2/subdir/file3 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo2 copied2 renamed from foo2:2ed2a3912a0b24502043eae84ee4b279c18b90dd copied: diff -r 58bdcab55bdc -r 311eeb2f6951 tests/test-non-interactive-wsgi --- a/tests/test-non-interactive-wsgi Mon Nov 23 22:08:59 2009 +0100 +++ b/tests/test-non-interactive-wsgi Mon Nov 23 22:46:48 2009 +0100 @@ -60,9 +60,14 @@ 'SERVER_PROTOCOL': 'HTTP/1.0' } -hgweb('.')(env, startrsp) +i = hgweb('.') +i(env, startrsp) print '---- ERRORS' print errors.getvalue() +print '---- OS.ENVIRON wsgi variables' +print sorted([x for x in os.environ if x.startswith('wsgi')]) +print '---- request.ENVIRON wsgi variables' +print sorted([x for x in i.repo.ui.environ if x.startswith('wsgi')]) EOF python request.py diff -r 58bdcab55bdc -r 311eeb2f6951 tests/test-non-interactive-wsgi.out --- a/tests/test-non-interactive-wsgi.out Mon Nov 23 22:08:59 2009 +0100 +++ b/tests/test-non-interactive-wsgi.out Mon Nov 23 22:46:48 2009 +0100 @@ -10,3 +10,7 @@ [('Content-Type', 'text/html; charset=ascii')] ---- ERRORS +---- OS.ENVIRON wsgi variables +[] +---- request.ENVIRON wsgi variables +['wsgi.errors', 'wsgi.input', 'wsgi.multiprocess', 'wsgi.multithread', 'wsgi.run_once', 'wsgi.url_scheme', 'wsgi.version']