--- a/hgext/convert/filemap.py Mon Nov 23 22:45:36 2009 +0100
+++ b/hgext/convert/filemap.py Mon Nov 23 23:04:44 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:
--- a/hgext/relink.py Mon Nov 23 22:45:36 2009 +0100
+++ b/hgext/relink.py Mon Nov 23 23:04:44 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),
--- a/mercurial/commands.py Mon Nov 23 22:45:36 2009 +0100
+++ b/mercurial/commands.py Mon Nov 23 23:04:44 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)
--- a/mercurial/dispatch.py Mon Nov 23 22:45:36 2009 +0100
+++ b/mercurial/dispatch.py Mon Nov 23 23:04:44 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"""
--- a/mercurial/hgweb/common.py Mon Nov 23 22:45:36 2009 +0100
+++ b/mercurial/hgweb/common.py Mon Nov 23 23:04:44 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)
--- a/mercurial/hgweb/hgweb_mod.py Mon Nov 23 22:45:36 2009 +0100
+++ b/mercurial/hgweb/hgweb_mod.py Mon Nov 23 23:04:44 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)
--- a/mercurial/httprepo.py Mon Nov 23 22:45:36 2009 +0100
+++ b/mercurial/httprepo.py Mon Nov 23 23:04:44 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:
--- a/mercurial/ui.py Mon Nov 23 22:45:36 2009 +0100
+++ b/mercurial/ui.py Mon Nov 23 23:04:44 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)
--- a/templates/gitweb/fileannotate.tmpl Mon Nov 23 22:45:36 2009 +0100
+++ b/templates/gitweb/fileannotate.tmpl Mon Nov 23 23:04:44 2009 +0100
@@ -21,6 +21,7 @@
<a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a> |
<a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
+<a href="{url}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a> |
<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
annotate |
<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
--- a/templates/gitweb/filediff.tmpl Mon Nov 23 22:45:36 2009 +0100
+++ b/templates/gitweb/filediff.tmpl Mon Nov 23 23:04:44 2009 +0100
@@ -21,6 +21,7 @@
<a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a> |
<a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
+<a href="{url}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a> |
<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
<a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
diff |
--- a/templates/gitweb/filerevision.tmpl Mon Nov 23 22:45:36 2009 +0100
+++ b/templates/gitweb/filerevision.tmpl Mon Nov 23 23:04:44 2009 +0100
@@ -21,6 +21,7 @@
<a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a> |
<a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
file |
+<a href="{url}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a> |
<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
<a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
--- a/tests/run-tests.py Mon Nov 23 22:45:36 2009 +0100
+++ b/tests/run-tests.py Mon Nov 23 23:04:44 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:
--- a/tests/test-convert-filemap Mon Nov 23 22:45:36 2009 +0100
+++ b/tests/test-convert-filemap Mon Nov 23 23:04:44 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
--- a/tests/test-convert-filemap.out Mon Nov 23 22:45:36 2009 +0100
+++ b/tests/test-convert-filemap.out Mon Nov 23 23:04:44 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:
--- a/tests/test-non-interactive-wsgi Mon Nov 23 22:45:36 2009 +0100
+++ b/tests/test-non-interactive-wsgi Mon Nov 23 23:04:44 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
--- a/tests/test-non-interactive-wsgi.out Mon Nov 23 22:45:36 2009 +0100
+++ b/tests/test-non-interactive-wsgi.out Mon Nov 23 23:04:44 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']