Mercurial > hg
changeset 6812:fdf5980bd010
Merge with main
test-remove is still failing for status() does not return removed files in a
sorted list. We can live with this for now, a fix is coming soon.
author | Patrick Mezard <pmezard@gmail.com> |
---|---|
date | Sun, 20 Jul 2008 20:00:02 +0200 |
parents | 2134d6c09432 (diff) 70ecce68df7c (current diff) |
children | d8159dd15db3 |
files | hgext/hgk.py |
diffstat | 38 files changed, 512 insertions(+), 189 deletions(-) [+] |
line wrap: on
line diff
--- a/contrib/win32/mercurial.iss Sun Jul 20 19:25:08 2008 +0200 +++ b/contrib/win32/mercurial.iss Sun Jul 20 20:00:02 2008 +0200 @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! [Setup] -AppCopyright=Copyright 2005-2007 Matt Mackall and others +AppCopyright=Copyright 2005-2008 Matt Mackall and others AppName=Mercurial AppVerName=Mercurial snapshot InfoAfterFile=contrib/win32/postinstall.txt @@ -18,7 +18,7 @@ DefaultDirName={pf}\Mercurial SourceDir=..\.. VersionInfoDescription=Mercurial distributed SCM -VersionInfoCopyright=Copyright 2005-2007 Matt Mackall and others +VersionInfoCopyright=Copyright 2005-2008 Matt Mackall and others VersionInfoCompany=Matt Mackall and others InternalCompressLevel=max SolidCompression=true
--- a/contrib/zsh_completion Sun Jul 20 19:25:08 2008 +0200 +++ b/contrib/zsh_completion Sun Jul 20 20:00:02 2008 +0200 @@ -205,8 +205,7 @@ _hg_config() { typeset -a items - local line - items=(${${(%f)"$(_hg_cmd showconfig)"}%%\=*}) + items=(${${(%f)"$(_call_program hg hg showconfig)"}%%\=*}) (( $#items )) && _describe -t config 'config item' items } @@ -291,10 +290,14 @@ '--cwd[change working directory]:new working directory:_files -/' '(--noninteractive -y)'{-y,--noninteractive}'[do not prompt, assume yes for any required answers]' '(--verbose -v)'{-v,--verbose}'[enable additional output]' + '*--config[set/override config option]:defined config items:_hg_config' '(--quiet -q)'{-q,--quiet}'[suppress output]' '(--help -h)'{-h,--help}'[display help and exit]' '--debug[debug mode]' '--debugger[start debugger]' + '--encoding[set the charset encoding (default: UTF8)]' + '--encodingmode[set the charset encoding mode (default: strict)]' + '--lsprof[print improved command execution profile]' '--traceback[print traceback on exception]' '--time[time how long the command takes]' '--profile[profile]'
--- a/hgext/convert/__init__.py Sun Jul 20 19:25:08 2008 +0200 +++ b/hgext/convert/__init__.py Sun Jul 20 20:00:02 2008 +0200 @@ -86,6 +86,50 @@ --config convert.hg.saverev=True (boolean) allow target to preserve source revision ID + CVS Source + ---------- + + CVS source will use a sandbox (i.e. a checked-out copy) from CVS + to indicate the starting point of what will be converted. Direct + access to the repository files is not needed, unless of course + the repository is :local:. The conversion uses the top level + directory in the sandbox to find the CVS repository, and then uses + CVS rlog commands to find files to convert. This means that unless + a filemap is given, all files under the starting directory will be + converted, and that any directory reorganisation in the CVS + sandbox is ignored. + + Because CVS does not have changesets, it is necessary to collect + individual commits to CVS and merge them into changesets. CVS source + can use the external 'cvsps' program (this is a legacy option and may + be removed in future) or use its internal changeset merging code. + External cvsps is default, and options may be passed to it by setting + --config convert.cvsps='cvsps -A -u --cvs-direct -q' + The options shown are the defaults. + + Internal cvsps is selected by setting + --config convert.cvsps=builtin + and has a few more configurable options: + --config convert.cvsps.fuzz=60 (integer) + Specify the maximum time (in seconds) that is allowed between + commits with identical user and log message in a single + changeset. When very large files were checked in as part + of a changeset then the default may not be long enough. + --config convert.cvsps.mergeto='{{mergetobranch ([-\w]+)}}' + Specify a regular expression to which commit log messages are + matched. If a match occurs, then the conversion process will + insert a dummy revision merging the branch on which this log + message occurs to the branch indicated in the regex. + --config convert.cvsps.mergefrom='{{mergefrombranch ([-\w]+)}}' + Specify a regular expression to which commit log messages are + matched. If a match occurs, then the conversion process will + add the most recent revision on the branch indicated in the + regex as the second parent of the changeset. + + The hgext/convert/cvsps wrapper script allows the builtin changeset + merging code to be run without doing a conversion. Its parameters and + output are similar to that of cvsps 2.1. + Subversion Source -----------------
--- a/hgext/mq.py Sun Jul 20 19:25:08 2008 +0200 +++ b/hgext/mq.py Sun Jul 20 20:00:02 2008 +0200 @@ -1129,7 +1129,7 @@ f = repo.file(dst) src = f.renamed(man[dst]) if src: - copies[src[0]] = copies.get(dst, []) + copies.setdefault(src[0], []).extend(copies.get(dst, [])) if dst in a: copies[src[0]].append(dst) # we can't copy a file created by the patch itself
--- a/mercurial/commands.py Sun Jul 20 19:25:08 2008 +0200 +++ b/mercurial/commands.py Sun Jul 20 20:00:02 2008 +0200 @@ -2026,9 +2026,9 @@ Valid URLs are of the form: local/filesystem/path (or file://local/filesystem/path) - http://[user@]host[:port]/[path] - https://[user@]host[:port]/[path] - ssh://[user@]host[:port]/[path] + http://[user[:pass]@]host[:port]/[path] + https://[user[:pass]@]host[:port]/[path] + ssh://[user[:pass]@]host[:port]/[path] static-http://host[:port]/[path] Paths in the local filesystem can either point to Mercurial @@ -2088,9 +2088,9 @@ Valid URLs are of the form: local/filesystem/path (or file://local/filesystem/path) - ssh://[user@]host[:port]/[path] - http://[user@]host[:port]/[path] - https://[user@]host[:port]/[path] + ssh://[user[:pass]@]host[:port]/[path] + http://[user[:pass]@]host[:port]/[path] + https://[user[:pass]@]host[:port]/[path] An optional identifier after # indicates a particular branch, tag, or changeset to push. If -r is used, the named changeset and all its
--- a/mercurial/hgweb/hgweb_mod.py Sun Jul 20 19:25:08 2008 +0200 +++ b/mercurial/hgweb/hgweb_mod.py Sun Jul 20 20:00:02 2008 +0200 @@ -10,25 +10,17 @@ from mercurial.node import hex, nullid from mercurial.repo import RepoError from mercurial import mdiff, ui, hg, util, patch, hook -from mercurial import revlog, templater, templatefilters, changegroup +from mercurial import revlog, templater, templatefilters from common import get_mtime, style_map, paritygen, countgen, ErrorResponse from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR from request import wsgirequest import webcommands, protocol, webutil -shortcuts = { - 'cl': [('cmd', ['changelog']), ('rev', None)], - 'sl': [('cmd', ['shortlog']), ('rev', None)], - 'cs': [('cmd', ['changeset']), ('node', None)], - 'f': [('cmd', ['file']), ('filenode', None)], - 'fl': [('cmd', ['filelog']), ('filenode', None)], - 'fd': [('cmd', ['filediff']), ('node', None)], - 'fa': [('cmd', ['annotate']), ('filenode', None)], - 'mf': [('cmd', ['manifest']), ('manifest', None)], - 'ca': [('cmd', ['archive']), ('node', None)], - 'tags': [('cmd', ['tags'])], - 'tip': [('cmd', ['changeset']), ('node', ['tip'])], - 'static': [('cmd', ['static']), ('file', None)] +perms = { + 'changegroup': 'pull', + 'changegroupsubset': 'pull', + 'unbundle': 'push', + 'stream_out': 'pull', } class hgweb(object): @@ -44,7 +36,6 @@ self.reponame = name self.archives = 'zip', 'gz', 'bz2' self.stripecount = 1 - self._capabilities = None # a repo owner may set web.templates in .hg/hgrc to get any file # readable by the user running the CGI script self.templatepath = self.config("web", "templates", @@ -76,18 +67,6 @@ self.maxfiles = int(self.config("web", "maxfiles", 10)) self.allowpull = self.configbool("web", "allowpull", True) self.encoding = self.config("web", "encoding", util._encoding) - self._capabilities = None - - def capabilities(self): - if self._capabilities is not None: - return self._capabilities - caps = ['lookup', 'changegroupsubset'] - if self.configbool('server', 'uncompressed'): - caps.append('stream=%d' % self.repo.changelog.version) - if changegroup.bundlepriority: - caps.append('unbundle=%s' % ','.join(changegroup.bundlepriority)) - self._capabilities = caps - return caps def run(self): if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."): @@ -97,22 +76,22 @@ def __call__(self, env, respond): req = wsgirequest(env, respond) - self.run_wsgi(req) - return req + return self.run_wsgi(req) def run_wsgi(self, req): self.refresh() - # expand form shortcuts + # process this if it's a protocol request + # protocol bits don't need to create any URLs + # and the clients always use the old URL structure - for k in shortcuts.iterkeys(): - if k in req.form: - for name, value in shortcuts[k]: - if value is None: - value = req.form[k] - req.form[name] = value - del req.form[k] + cmd = req.form.get('cmd', [''])[0] + if cmd and cmd in protocol.__all__: + if cmd in perms and not self.check_perm(req, perms[cmd]): + return [] + method = getattr(protocol, cmd) + return method(self.repo, req) # work with CGI variables to create coherent structure # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME @@ -145,8 +124,10 @@ cmd = cmd[style+1:] # avoid accepting e.g. style parameter as command - if hasattr(webcommands, cmd) or hasattr(protocol, cmd): + if hasattr(webcommands, cmd): req.form['cmd'] = [cmd] + else: + cmd = '' if args and args[0]: node = args.pop(0) @@ -164,14 +145,6 @@ req.form['node'] = [fn[:-len(ext)]] req.form['type'] = [type_] - # process this if it's a protocol request - - cmd = req.form.get('cmd', [''])[0] - if cmd in protocol.__all__: - method = getattr(protocol, cmd) - method(self, req) - return - # process the web interface request try: @@ -196,6 +169,7 @@ req.write(content) del tmpl + return [] except revlog.LookupError, err: req.respond(HTTP_NOT_FOUND, ctype) @@ -203,12 +177,15 @@ if 'manifest' not in msg: msg = 'revision not found: %s' % err.name req.write(tmpl('error', error=msg)) + return [] except (RepoError, revlog.RevlogError), inst: req.respond(HTTP_SERVER_ERROR, ctype) req.write(tmpl('error', error=str(inst))) + return [] except ErrorResponse, inst: req.respond(inst.code, ctype) req.write(tmpl('error', error=inst.message)) + return [] def templater(self, req): @@ -364,16 +341,39 @@ 'zip': ('application/zip', 'zip', '.zip', None), } - def check_perm(self, req, op, default): - '''check permission for operation based on user auth. - return true if op allowed, else false. - default is policy to use if no config given.''' + def check_perm(self, req, op): + '''Check permission for operation based on request data (including + authentication info. Return true if op allowed, else false.''' + + def error(status, message): + req.respond(status, protocol.HGTYPE) + req.write('0\n%s\n' % message) + + if op == 'pull': + return self.allowpull + + # enforce that you can only push using POST requests + if req.env['REQUEST_METHOD'] != 'POST': + error('405 Method Not Allowed', 'push requires POST request') + return False + + # 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': + error(HTTP_OK, 'ssl required') + return False user = req.env.get('REMOTE_USER') - deny = self.configlist('web', 'deny_' + op) + deny = self.configlist('web', 'deny_push') if deny and (not user or deny == ['*'] or user in deny): + error('401 Unauthorized', 'push not authorized') return False - allow = self.configlist('web', 'allow_' + op) - return (allow and (allow == ['*'] or user in allow)) or default + allow = self.configlist('web', 'allow_push') + result = allow and (allow == ['*'] or user in allow) + if not result: + error('401 Unauthorized', 'push not authorized') + + return result
--- a/mercurial/hgweb/hgwebdir_mod.py Sun Jul 20 19:25:08 2008 +0200 +++ b/mercurial/hgweb/hgwebdir_mod.py Sun Jul 20 20:00:02 2008 +0200 @@ -70,8 +70,7 @@ def __call__(self, env, respond): req = wsgirequest(env, respond) - self.run_wsgi(req) - return req + return self.run_wsgi(req) def run_wsgi(self, req): @@ -91,13 +90,13 @@ else: fname = req.form['static'][0] req.write(staticfile(static, fname, req)) - return + return [] # top-level index elif not virtual: req.respond(HTTP_OK, ctype) req.write(self.makeindex(req, tmpl)) - return + return [] # nested indexes and hgwebs @@ -108,8 +107,7 @@ req.env['REPO_NAME'] = virtual try: repo = hg.repository(self.parentui, real) - hgweb(repo).run_wsgi(req) - return + return hgweb(repo).run_wsgi(req) except IOError, inst: msg = inst.strerror raise ErrorResponse(HTTP_SERVER_ERROR, msg) @@ -121,7 +119,7 @@ if [r for r in repos if r.startswith(subdir)]: req.respond(HTTP_OK, ctype) req.write(self.makeindex(req, tmpl, subdir)) - return + return [] up = virtual.rfind('/') if up < 0: @@ -131,10 +129,12 @@ # prefixes not found req.respond(HTTP_NOT_FOUND, ctype) req.write(tmpl("notfound", repo=virtual)) + return [] except ErrorResponse, err: req.respond(err.code, ctype) req.write(tmpl('error', error=err.message or '')) + return [] finally: tmpl = None
--- a/mercurial/hgweb/protocol.py Sun Jul 20 19:25:08 2008 +0200 +++ b/mercurial/hgweb/protocol.py Sun Jul 20 20:00:02 2008 +0200 @@ -21,69 +21,65 @@ HGTYPE = 'application/mercurial-0.1' -def lookup(web, req): +def lookup(repo, req): try: - r = hex(web.repo.lookup(req.form['key'][0])) + r = hex(repo.lookup(req.form['key'][0])) success = 1 except Exception,inst: r = str(inst) success = 0 resp = "%s %s\n" % (success, r) req.respond(HTTP_OK, HGTYPE, length=len(resp)) - req.write(resp) + yield resp -def heads(web, req): - resp = " ".join(map(hex, web.repo.heads())) + "\n" +def heads(repo, req): + resp = " ".join(map(hex, repo.heads())) + "\n" req.respond(HTTP_OK, HGTYPE, length=len(resp)) - req.write(resp) + yield resp -def branches(web, req): +def branches(repo, req): nodes = [] if 'nodes' in req.form: nodes = map(bin, req.form['nodes'][0].split(" ")) resp = cStringIO.StringIO() - for b in web.repo.branches(nodes): + for b in repo.branches(nodes): resp.write(" ".join(map(hex, b)) + "\n") resp = resp.getvalue() req.respond(HTTP_OK, HGTYPE, length=len(resp)) - req.write(resp) + yield resp -def between(web, req): +def between(repo, req): if 'pairs' in req.form: pairs = [map(bin, p.split("-")) for p in req.form['pairs'][0].split(" ")] resp = cStringIO.StringIO() - for b in web.repo.between(pairs): + for b in repo.between(pairs): resp.write(" ".join(map(hex, b)) + "\n") resp = resp.getvalue() req.respond(HTTP_OK, HGTYPE, length=len(resp)) - req.write(resp) + yield resp -def changegroup(web, req): +def changegroup(repo, req): req.respond(HTTP_OK, HGTYPE) nodes = [] - if not web.allowpull: - return if 'roots' in req.form: nodes = map(bin, req.form['roots'][0].split(" ")) z = zlib.compressobj() - f = web.repo.changegroup(nodes, 'serve') + f = repo.changegroup(nodes, 'serve') while 1: chunk = f.read(4096) if not chunk: break - req.write(z.compress(chunk)) + yield z.compress(chunk) - req.write(z.flush()) + yield z.flush() -def changegroupsubset(web, req): +def changegroupsubset(repo, req): req.respond(HTTP_OK, HGTYPE) bases = [] heads = [] - if not web.allowpull: - return if 'bases' in req.form: bases = [bin(x) for x in req.form['bases'][0].split(' ')] @@ -91,67 +87,44 @@ heads = [bin(x) for x in req.form['heads'][0].split(' ')] z = zlib.compressobj() - f = web.repo.changegroupsubset(bases, heads, 'serve') + f = repo.changegroupsubset(bases, heads, 'serve') while 1: chunk = f.read(4096) if not chunk: break - req.write(z.compress(chunk)) + yield z.compress(chunk) - req.write(z.flush()) + yield z.flush() -def capabilities(web, req): - resp = ' '.join(web.capabilities()) - req.respond(HTTP_OK, HGTYPE, length=len(resp)) - req.write(resp) +def capabilities(repo, req): + caps = ['lookup', 'changegroupsubset'] + if repo.ui.configbool('server', 'uncompressed', untrusted=True): + caps.append('stream=%d' % repo.changelog.version) + if changegroupmod.bundlepriority: + caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority)) + rsp = ' '.join(caps) + req.respond(HTTP_OK, HGTYPE, length=len(rsp)) + yield rsp -def unbundle(web, req): +def unbundle(repo, req): + + errorfmt = '0\n%s\n' + proto = req.env.get('wsgi.url_scheme') or 'http' + their_heads = req.form['heads'][0].split(' ') - def bail(response, headers={}): + def check_heads(): + heads = map(hex, repo.heads()) + return their_heads == [hex('force')] or their_heads == heads + + # fail early if possible + if not check_heads(): length = int(req.env.get('CONTENT_LENGTH', 0)) for s in util.filechunkiter(req, limit=length): # drain incoming bundle, else client will not see # response when run outside cgi script pass - - status = headers.pop('status', HTTP_OK) - req.header(headers.items()) - req.respond(status, HGTYPE) - req.write('0\n') - req.write(response) - - # enforce that you can only unbundle with POST requests - if req.env['REQUEST_METHOD'] != 'POST': - headers = {'status': '405 Method Not Allowed'} - bail('unbundle requires POST request\n', headers) - return - - # require ssl by default, auth info cannot be sniffed and - # replayed - ssl_req = web.configbool('web', 'push_ssl', True) - if ssl_req: - if req.env.get('wsgi.url_scheme') != 'https': - bail('ssl required\n') - return - proto = 'https' - else: - proto = 'http' - - # do not allow push unless explicitly allowed - if not web.check_perm(req, 'push', False): - bail('push not authorized\n', headers={'status': '401 Unauthorized'}) - return - - their_heads = req.form['heads'][0].split(' ') - - def check_heads(): - heads = map(hex, web.repo.heads()) - return their_heads == [hex('force')] or their_heads == heads - - # fail early if possible - if not check_heads(): - bail('unsynced changes\n') - return + req.respond(HTTP_OK, HGTYPE) + return errorfmt % 'unsynced changes', req.respond(HTTP_OK, HGTYPE) @@ -166,12 +139,10 @@ fp.write(s) try: - lock = web.repo.lock() + lock = repo.lock() try: if not check_heads(): - req.write('0\n') - req.write('unsynced changes\n') - return + return errorfmt % 'unsynced changes', fp.seek(0) header = fp.read(6) @@ -190,26 +161,23 @@ url = 'remote:%s:%s' % (proto, req.env.get('REMOTE_HOST', '')) try: - ret = web.repo.addchangegroup(gen, 'serve', url) + ret = repo.addchangegroup(gen, 'serve', url) except util.Abort, inst: sys.stdout.write("abort: %s\n" % inst) ret = 0 finally: val = sys.stdout.getvalue() sys.stdout, sys.stderr = oldio - req.write('%d\n' % ret) - req.write(val) + return '%d\n%s' % (ret, val), finally: del lock except ValueError, inst: - req.write('0\n') - req.write(str(inst) + '\n') + return errorfmt % inst, except (OSError, IOError), inst: - req.write('0\n') filename = getattr(inst, 'filename', '') # Don't send our filesystem layout to the client - if filename.startswith(web.repo.root): - filename = filename[len(web.repo.root)+1:] + if filename.startswith(repo.root): + filename = filename[len(repo.root)+1:] else: filename = '' error = getattr(inst, 'strerror', 'Unknown error') @@ -218,13 +186,12 @@ else: code = HTTP_SERVER_ERROR req.respond(code) - req.write('%s: %s\n' % (error, filename)) + return '0\n%s: %s\n' % (error, filename), finally: fp.close() os.unlink(tempname) -def stream_out(web, req): - if not web.allowpull: - return +def stream_out(repo, req): req.respond(HTTP_OK, HGTYPE) - streamclone.stream_out(web.repo, req, untrusted=True) + streamclone.stream_out(repo, req, untrusted=True) + return []
--- a/mercurial/hgweb/request.py Sun Jul 20 19:25:08 2008 +0200 +++ b/mercurial/hgweb/request.py Sun Jul 20 20:00:02 2008 +0200 @@ -9,6 +9,31 @@ import socket, cgi, errno from common import ErrorResponse, statusmessage +shortcuts = { + 'cl': [('cmd', ['changelog']), ('rev', None)], + 'sl': [('cmd', ['shortlog']), ('rev', None)], + 'cs': [('cmd', ['changeset']), ('node', None)], + 'f': [('cmd', ['file']), ('filenode', None)], + 'fl': [('cmd', ['filelog']), ('filenode', None)], + 'fd': [('cmd', ['filediff']), ('node', None)], + 'fa': [('cmd', ['annotate']), ('filenode', None)], + 'mf': [('cmd', ['manifest']), ('manifest', None)], + 'ca': [('cmd', ['archive']), ('node', None)], + 'tags': [('cmd', ['tags'])], + 'tip': [('cmd', ['changeset']), ('node', ['tip'])], + 'static': [('cmd', ['static']), ('file', None)] +} + +def expand(form): + for k in shortcuts.iterkeys(): + if k in form: + for name, value in shortcuts[k]: + if value is None: + value = form[k] + form[name] = value + del form[k] + return form + class wsgirequest(object): def __init__(self, wsgienv, start_response): version = wsgienv['wsgi.version'] @@ -21,7 +46,7 @@ self.multiprocess = wsgienv['wsgi.multiprocess'] self.run_once = wsgienv['wsgi.run_once'] self.env = wsgienv - self.form = cgi.parse(self.inp, self.env, keep_blank_values=1) + self.form = expand(cgi.parse(self.inp, self.env, keep_blank_values=1)) self._start_response = start_response self.server_write = None self.headers = []
--- a/mercurial/hgweb/server.py Sun Jul 20 19:25:08 2008 +0200 +++ b/mercurial/hgweb/server.py Sun Jul 20 20:00:02 2008 +0200 @@ -122,7 +122,8 @@ self.saved_headers = [] self.sent_headers = False self.length = None - self.server.application(env, self._start_response) + for chunk in self.server.application(env, self._start_response): + self._write(chunk) def send_headers(self): if not self.saved_status:
--- a/mercurial/httprepo.py Sun Jul 20 19:25:08 2008 +0200 +++ b/mercurial/httprepo.py Sun Jul 20 20:00:02 2008 +0200 @@ -268,6 +268,7 @@ # 1.0 here is the _protocol_ version opener.addheaders = [('User-agent', 'mercurial/proto-1.0')] + opener.addheaders.append(('Accept', 'application/mercurial-0.1')) urllib2.install_opener(opener) def url(self):
--- a/mercurial/merge.py Sun Jul 20 19:25:08 2008 +0200 +++ b/mercurial/merge.py Sun Jul 20 20:00:02 2008 +0200 @@ -258,6 +258,17 @@ return action +def actioncmp(a1, a2): + m1 = a1[1] + m2 = a2[1] + if m1 == m2: + return cmp(a1, a2) + if m1 == 'r': + return -1 + if m2 == 'r': + return 1 + return cmp(a1, a2) + def applyupdates(repo, action, wctx, mctx): "apply the merge action list to the working directory" @@ -265,7 +276,7 @@ ms = mergestate(repo) ms.reset(wctx.parents()[0].node()) moves = [] - action.sort() + action.sort(actioncmp) # prescan for merges for a in action:
--- a/mercurial/templater.py Sun Jul 20 19:25:08 2008 +0200 +++ b/mercurial/templater.py Sun Jul 20 20:00:02 2008 +0200 @@ -81,18 +81,18 @@ def __contains__(self, key): return key in self.cache or key in self.map - def __call__(self, t, **map): - '''perform expansion. - t is name of map element to expand. - map is added elements to use during expansion.''' + def _template(self, t): + '''Get the template for the given template name. Use a local cache.''' if not t in self.cache: try: self.cache[t] = file(self.map[t]).read() except IOError, inst: raise IOError(inst.args[0], _('template file %s: %s') % (self.map[t], inst.args[1])) - tmpl = self.cache[t] + return self.cache[t] + def _process(self, tmpl, map): + '''Render a template. Returns a generator.''' while tmpl: m = self.template_re.search(tmpl) if not m: @@ -119,13 +119,34 @@ lm = map.copy() for i in v: lm.update(i) - yield self(format, **lm) + t = self._template(format) + yield self._process(t, lm) else: if fl: for f in fl.split("|")[1:]: v = self.filters[f](v) yield v + def __call__(self, t, **map): + '''Perform expansion. t is name of map element to expand. map contains + added elements for use during expansion. Is a generator.''' + tmpl = self._template(t) + iters = [self._process(tmpl, map)] + while iters: + try: + item = iters[0].next() + except StopIteration: + iters.pop(0) + continue + if isinstance(item, str): + yield item + elif item is None: + yield '' + elif hasattr(item, '__iter__'): + iters.insert(0, iter(item)) + else: + yield str(item) + def templatepath(name=None): '''return location of template file or directory (if no name). returns None if not found.'''
--- a/setup.py Sun Jul 20 19:25:08 2008 +0200 +++ b/setup.py Sun Jul 20 20:00:02 2008 +0200 @@ -129,7 +129,7 @@ [os.path.join(root, file_) for file_ in files]) for root, dirs, files in os.walk('templates')], cmdclass=cmdclass, - options=dict(py2exe=dict(packages=['hgext']), + options=dict(py2exe=dict(packages=['hgext', 'email']), bdist_mpkg=dict(zipdist=True, license='COPYING', readme='contrib/macosx/Readme.html',
--- a/templates/coal/changeset.tmpl Sun Jul 20 19:25:08 2008 +0200 +++ b/templates/coal/changeset.tmpl Sun Jul 20 20:00:02 2008 +0200 @@ -9,8 +9,8 @@ <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> </div> <ul> - <li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li> - <li><a href="{url}graph{sessionvars%urlparameter}">graph</a></li> + <li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li> + <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li> <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> </ul> <ul>
--- a/templates/coal/fileannotate.tmpl Sun Jul 20 19:25:08 2008 +0200 +++ b/templates/coal/fileannotate.tmpl Sun Jul 20 20:00:02 2008 +0200 @@ -10,8 +10,8 @@ <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> </div> <ul> -<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li> -<li><a href="{url}graph/{sessionvars%urlparameter}">graph</a></li> +<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li> +<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li> <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> </ul>
--- a/templates/coal/filediff.tmpl Sun Jul 20 19:25:08 2008 +0200 +++ b/templates/coal/filediff.tmpl Sun Jul 20 20:00:02 2008 +0200 @@ -10,8 +10,8 @@ <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> </div> <ul> -<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li> -<li><a href="{url}graph/{sessionvars%urlparameter}">graph</a></li> +<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li> +<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li> <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> </ul> <ul>
--- a/templates/coal/filelog.tmpl Sun Jul 20 19:25:08 2008 +0200 +++ b/templates/coal/filelog.tmpl Sun Jul 20 20:00:02 2008 +0200 @@ -15,8 +15,8 @@ <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> </div> <ul> -<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li> -<li><a href="{url}graph/{sessionvars%urlparameter}">graph</a></li> +<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li> +<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li> <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> </ul> <ul>
--- a/templates/coal/filerevision.tmpl Sun Jul 20 19:25:08 2008 +0200 +++ b/templates/coal/filerevision.tmpl Sun Jul 20 20:00:02 2008 +0200 @@ -10,8 +10,8 @@ <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> </div> <ul> -<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li> -<li><a href="{url}graph/{sessionvars%urlparameter}">graph</a></li> +<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li> +<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li> <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> </ul> <ul>
--- a/templates/coal/graph.tmpl Sun Jul 20 19:25:08 2008 +0200 +++ b/templates/coal/graph.tmpl Sun Jul 20 20:00:02 2008 +0200 @@ -15,7 +15,7 @@ <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> </div> <ul> -<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li> +<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li> <li class="active">graph</li> <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> </ul>
--- a/templates/coal/manifest.tmpl Sun Jul 20 19:25:08 2008 +0200 +++ b/templates/coal/manifest.tmpl Sun Jul 20 20:00:02 2008 +0200 @@ -10,8 +10,8 @@ <img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a> </div> <ul> -<li><a href="{url}shortlog/{sessionvars%urlparameter}">log</a></li> -<li><a href="{url}graph/{sessionvars%urlparameter}">graph</a></li> +<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li> +<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li> <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> </ul> <ul>
--- a/templates/coal/shortlog.tmpl Sun Jul 20 19:25:08 2008 +0200 +++ b/templates/coal/shortlog.tmpl Sun Jul 20 20:00:02 2008 +0200 @@ -15,7 +15,7 @@ </div> <ul> <li class="active">log</li> -<li><a href="{url}graph{sessionvars%urlparameter}">graph</a></li> +<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li> <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li> </ul> <ul>
--- a/tests/hghave Sun Jul 20 19:25:08 2008 +0200 +++ b/tests/hghave Sun Jul 20 20:00:02 2008 +0200 @@ -57,6 +57,24 @@ finally: os.remove(path) +def has_icasefs(): + # Stolen from mercurial.util + fd, path = tempfile.mkstemp(prefix=tempprefix) + os.close(fd) + try: + s1 = os.stat(path) + d, b = os.path.split(path) + p2 = os.path.join(d, b.upper()) + if path == p2: + p2 = os.path.join(d, b.lower()) + try: + s2 = os.stat(p2) + return s2 == s1 + except: + return False + finally: + os.remove(path) + def has_fifo(): return hasattr(os, "mkfifo") @@ -129,6 +147,7 @@ "fifo": (has_fifo, "named pipes"), "git": (has_git, "git command line client"), "hotshot": (has_hotshot, "python hotshot module"), + "icasefs": (has_icasefs, "case insensitive file system"), "lsprof": (has_lsprof, "python lsprof module"), "mtn": (has_mtn, "monotone client (> 0.31)"), "svn": (has_svn, "subversion client and admin tools"),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-casefolding Sun Jul 20 20:00:02 2008 +0200 @@ -0,0 +1,41 @@ +#!/bin/sh + +"$TESTDIR/hghave" icasefs || exit 80 + +echo '% test file addition with bad case' +hg init repo1 +cd repo1 +echo a > a +hg add A +hg st +hg ci -m adda +hg manifest +cd .. + +echo '% test case collision on rename (issue 750)' +hg init repo2 +cd repo2 +echo a > a +hg --debug ci -Am adda +hg mv a A +# 'a' used to be removed under windows +test -f a || echo 'a is missing' +hg st +cd .. + +echo '% test case collision between revisions (issue 912)' +hg init repo3 +cd repo3 +echo a > a +hg ci -Am adda +hg rm a +hg ci -Am removea +echo A > A +hg ci -Am addA +# Used to fail under case insensitive fs +hg up -C 0 +hg up -C +cd .. + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-casefolding.out Sun Jul 20 20:00:02 2008 +0200 @@ -0,0 +1,13 @@ +% test file addition with bad case +adding a +A a +a +% test case collision on rename (issue 750) +adding a +a +A: not overwriting - file exists +% test case collision between revisions (issue 912) +adding a +adding A +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +1 files updated, 0 files merged, 1 files removed, 0 files unresolved
--- a/tests/test-convert-baz Sun Jul 20 19:25:08 2008 +0200 +++ b/tests/test-convert-baz Sun Jul 20 20:00:02 2008 +0200 @@ -69,4 +69,5 @@ echo % show graph log glog -R baz-repo-hg +hg up -q -R baz-repo-hg hg -R baz-repo-hg manifest --debug
--- a/tests/test-convert-darcs Sun Jul 20 19:25:08 2008 +0200 +++ b/tests/test-convert-darcs Sun Jul 20 20:00:02 2008 +0200 @@ -58,4 +58,5 @@ # "c" file in p1.1 patch are reverted too. # Just to say that manifest not listing "c" here is a bug. glog -R darcs-repo-hg +hg up -q -R darcs-repo-hg hg -R darcs-repo-hg manifest --debug
--- a/tests/test-convert-tla Sun Jul 20 19:25:08 2008 +0200 +++ b/tests/test-convert-tla Sun Jul 20 20:00:02 2008 +0200 @@ -69,4 +69,5 @@ echo % show graph log glog -R tla-repo-hg +hg up -q -R tla-repo-hg hg -R tla-repo-hg manifest --debug
--- a/tests/test-convert.out Sun Jul 20 19:25:08 2008 +0200 +++ b/tests/test-convert.out Sun Jul 20 20:00:02 2008 +0200 @@ -74,6 +74,50 @@ --config convert.hg.saverev=True (boolean) allow target to preserve source revision ID + CVS Source + ---------- + + CVS source will use a sandbox (i.e. a checked-out copy) from CVS + to indicate the starting point of what will be converted. Direct + access to the repository files is not needed, unless of course + the repository is :local:. The conversion uses the top level + directory in the sandbox to find the CVS repository, and then uses + CVS rlog commands to find files to convert. This means that unless + a filemap is given, all files under the starting directory will be + converted, and that any directory reorganisation in the CVS + sandbox is ignored. + + Because CVS does not have changesets, it is necessary to collect + individual commits to CVS and merge them into changesets. CVS source + can use the external 'cvsps' program (this is a legacy option and may + be removed in future) or use its internal changeset merging code. + External cvsps is default, and options may be passed to it by setting + --config convert.cvsps='cvsps -A -u --cvs-direct -q' + The options shown are the defaults. + + Internal cvsps is selected by setting + --config convert.cvsps=builtin + and has a few more configurable options: + --config convert.cvsps.fuzz=60 (integer) + Specify the maximum time (in seconds) that is allowed between + commits with identical user and log message in a single + changeset. When very large files were checked in as part + of a changeset then the default may not be long enough. + --config convert.cvsps.mergeto='{{mergetobranch ([-\w]+)}}' + Specify a regular expression to which commit log messages are + matched. If a match occurs, then the conversion process will + insert a dummy revision merging the branch on which this log + message occurs to the branch indicated in the regex. + --config convert.cvsps.mergefrom='{{mergefrombranch ([-\w]+)}}' + Specify a regular expression to which commit log messages are + matched. If a match occurs, then the conversion process will + add the most recent revision on the branch indicated in the + regex as the second parent of the changeset. + + The hgext/convert/cvsps wrapper script allows the builtin changeset + merging code to be run without doing a conversion. Its parameters and + output are similar to that of cvsps 2.1. + Subversion Source -----------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-hgk Sun Jul 20 20:00:02 2008 +0200 @@ -0,0 +1,11 @@ +#!/bin/sh + +echo "[extensions]" >> $HGRCPATH +echo "hgk=" >> $HGRCPATH + +hg init repo +cd repo +echo a > a +hg ci -Am adda +hg debug-cat-file commit 0 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-hgk.out Sun Jul 20 20:00:02 2008 +0200 @@ -0,0 +1,9 @@ +adding a +tree a0c8bcbbb45c +parent 000000000000 +author test 0 0 +committer test 0 0 +revision 0 +branch default + +adda
--- a/tests/test-hgweb-commands Sun Jul 20 19:25:08 2008 +0200 +++ b/tests/test-hgweb-commands Sun Jul 20 20:00:02 2008 +0200 @@ -34,21 +34,22 @@ echo % Overviews "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/tags/?style=atom' | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//" "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/summary/?style=gitweb' | sed "s/[0-9]* years ago/long ago/g" +"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/graph/?style=gitweb' | sed "s/[0-9]* years/long/g" echo % capabilities -"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/capabilities' +"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=capabilities' echo % heads -"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/heads' +"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=heads' echo % lookup -"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/lookup/1' +"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=lookup&node=1' echo % branches -"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/branches' +"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=branches' echo % changegroup -"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/changegroup' +"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=changegroup' echo % stream_out -"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/stream_out' +"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=stream_out' echo % failing unbundle, requires POST request -"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/unbundle' +"$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=unbundle' echo % Static files "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/static/style.css'
--- a/tests/test-mq-qrefresh Sun Jul 20 19:25:08 2008 +0200 +++ b/tests/test-mq-qrefresh Sun Jul 20 20:00:02 2008 +0200 @@ -82,3 +82,28 @@ cat .hg/patches/mqbase | \ sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \ -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" +cd .. + + + +echo "[diff]" >> $HGRCPATH +echo "git=True" >> $HGRCPATH + +# Test qrefresh --git losing copy metadata +echo % create test repo +hg init repo +cd repo +echo a > a +hg ci -Am adda +hg copy a ab +echo b >> ab +hg copy a ac +echo c >> ac +echo % capture changes +hg qnew -f p1 +hg qdiff +echo % refresh and check changes again +hg qref +hg qdiff +cd .. +
--- a/tests/test-mq-qrefresh.out Sun Jul 20 19:25:08 2008 +0200 +++ b/tests/test-mq-qrefresh.out Sun Jul 20 20:00:02 2008 +0200 @@ -159,3 +159,39 @@ @@ -1,1 +1,1 @@ -base +patched +% create test repo +adding a +% capture changes +diff --git a/a b/ab +copy from a +copy to ab +--- a/a ++++ b/ab +@@ -1,1 +1,2 @@ + a ++b +diff --git a/a b/ac +copy from a +copy to ac +--- a/a ++++ b/ac +@@ -1,1 +1,2 @@ + a ++c +% refresh and check changes again +diff --git a/a b/ab +copy from a +copy to ab +--- a/a ++++ b/ab +@@ -1,1 +1,2 @@ + a ++b +diff --git a/a b/ac +copy from a +copy to ac +--- a/a ++++ b/ac +@@ -1,1 +1,2 @@ + a ++c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-pull-http Sun Jul 20 20:00:02 2008 +0200 @@ -0,0 +1,36 @@ +#!/bin/sh + +cp "$TESTDIR"/printenv.py . + +hg init test +cd test +echo a > a +hg ci -Ama -d '0 0' + +cd .. +hg clone test test2 +cd test2 +echo a >> a +hg ci -mb -d '0 0' + +echo % expect error, cloning not allowed +echo '[web]' > .hg/hgrc +echo 'allowpull = false' >> .hg/hgrc +hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log +cat hg.pid >> $DAEMON_PIDS +hg clone http://localhost:$HGPORT/ test3 | sed -e 's,:[0-9][0-9]*/,/,' +kill `cat hg.pid` +echo % serve errors +cat errors.log + +req() { + hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log + cat hg.pid >> $DAEMON_PIDS + hg --cwd ../test pull http://localhost:$HGPORT/ | sed -e 's,:[0-9][0-9]*/,/,' + kill `cat hg.pid` + echo % serve errors + cat errors.log +} + +echo % expect error, pulling not allowed +req
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-pull-http.out Sun Jul 20 20:00:02 2008 +0200 @@ -0,0 +1,12 @@ +adding a +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +% expect error, cloning not allowed +abort: error: +requesting all changes +% serve errors +% expect error, pulling not allowed +abort: error: +pulling from http://localhost/ +searching for changes +% serve errors
--- a/tests/test-update-reverse.out Sun Jul 20 19:25:08 2008 +0200 +++ b/tests/test-update-reverse.out Sun Jul 20 20:00:02 2008 +0200 @@ -46,9 +46,9 @@ side2: remote deleted -> r side1: remote deleted -> r main: remote created -> g -getting main removing side1 removing side2 +getting main 1 files updated, 0 files merged, 2 files removed, 0 files unresolved Should only show a main a