# HG changeset patch # User Matt Mackall # Date 1300226026 18000 # Node ID 30a0e3519f69142288d3d5b6d64a966cf171163d # Parent 0652b2da832daada866a68f7a4359227570c2447# Parent 9777df929035ed35cda8c98aac846b7d138b7645 merge with stable diff -r 9777df929035 -r 30a0e3519f69 .hgignore --- a/.hgignore Mon Mar 14 21:35:31 2011 +0100 +++ b/.hgignore Tue Mar 15 16:53:46 2011 -0500 @@ -7,6 +7,7 @@ *.mergebackup *.o *.so +*.dll *.pyd *.pyc *.pyo diff -r 9777df929035 -r 30a0e3519f69 contrib/check-code.py --- a/contrib/check-code.py Mon Mar 14 21:35:31 2011 +0100 +++ b/contrib/check-code.py Tue Mar 15 16:53:46 2011 -0500 @@ -66,6 +66,7 @@ (r'^source\b', "don't use 'source', use '.'"), (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"), (r'ls\s+[^|-]+\s+-', "options to 'ls' must come before filenames"), + (r'[^>]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"), ] testfilters = [ diff -r 9777df929035 -r 30a0e3519f69 contrib/zsh_completion --- a/contrib/zsh_completion Mon Mar 14 21:35:31 2011 +0100 +++ b/contrib/zsh_completion Tue Mar 15 16:53:46 2011 -0500 @@ -360,8 +360,8 @@ '(--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)]' + '--encoding[set the charset encoding]' + '--encodingmode[set the charset encoding mode]' '--lsprof[print improved command execution profile]' '--traceback[print traceback on exception]' '--time[time how long the command takes]' diff -r 9777df929035 -r 30a0e3519f69 hgext/color.py --- a/hgext/color.py Mon Mar 14 21:35:31 2011 +0100 +++ b/hgext/color.py Tue Mar 15 16:53:46 2011 -0500 @@ -18,11 +18,11 @@ '''colorize output from some commands -This extension modifies the status and resolve commands to add color to their -output to reflect file status, the qseries command to add color to reflect -patch status (applied, unapplied, missing), and to diff-related -commands to highlight additions, removals, diff headers, and trailing -whitespace. +This extension modifies the status and resolve commands to add color +to their output to reflect file status, the qseries command to add +color to reflect patch status (applied, unapplied, missing), and to +diff-related commands to highlight additions, removals, diff headers, +and trailing whitespace. Other effects in addition to color, like bold and underlined text, are also available. Effects are rendered with the ECMA-48 SGR control diff -r 9777df929035 -r 30a0e3519f69 hgext/convert/subversion.py --- a/hgext/convert/subversion.py Mon Mar 14 21:35:31 2011 +0100 +++ b/hgext/convert/subversion.py Tue Mar 15 16:53:46 2011 -0500 @@ -259,6 +259,7 @@ except ValueError: raise util.Abort(_('svn: revision %s is not an integer') % rev) + self.trunkname = self.ui.config('convert', 'svn.trunk', 'trunk').strip('/') self.startrev = self.ui.config('convert', 'svn.startrev', default=0) try: self.startrev = int(self.startrev) @@ -761,9 +762,8 @@ author = author and self.recode(author) or '' try: branch = self.module.split("/")[-1] - trunkname = self.ui.config('convert', 'svn.trunk', 'trunk') - if branch == trunkname.strip('/'): - branch = '' + if branch == self.trunkname: + branch = None except IndexError: branch = None @@ -944,6 +944,7 @@ class svn_sink(converter_sink, commandline): commit_re = re.compile(r'Committed revision (\d+).', re.M) + uuid_re = re.compile(r'Repository UUID:\s*(\S+)', re.M) def prerun(self): if self.wc: @@ -964,8 +965,6 @@ def __init__(self, ui, path): - if svn is None: - raise MissingTool(_('Could not load Subversion python bindings')) converter_sink.__init__(self, ui, path) commandline.__init__(self, ui, 'svn') self.delete = [] @@ -1012,8 +1011,8 @@ fp.close() util.set_flags(hook, False, True) - xport = transport.SvnRaTransport(url=geturl(path)) - self.uuid = svn.ra.get_uuid(xport.ra) + output = self.run0('info') + self.uuid = self.uuid_re.search(output).group(1).strip() def wjoin(self, *names): return os.path.join(self.wc, *names) diff -r 9777df929035 -r 30a0e3519f69 hgext/eol.py --- a/hgext/eol.py Mon Mar 14 21:35:31 2011 +0100 +++ b/hgext/eol.py Tue Mar 15 16:53:46 2011 -0500 @@ -73,11 +73,13 @@ only need to these filters until you have prepared a ``.hgeol`` file. The ``win32text.forbid*`` hooks provided by the win32text extension -have been unified into a single hook named ``eol.hook``. The hook will -lookup the expected line endings from the ``.hgeol`` file, which means -you must migrate to a ``.hgeol`` file first before using the hook. -Remember to enable the eol extension in the repository where you -install the hook. +have been unified into a single hook named ``eol.checkheadshook``. The +hook will lookup the expected line endings from the ``.hgeol`` file, +which means you must migrate to a ``.hgeol`` file first before using +the hook. ``eol.checkheadshook`` only checks heads, intermediate +invalid revisions will be pushed. To forbid them completely, use the +``eol.checkallhook`` hook. These hooks are best used as +``pretxnchangegroup`` hooks. See :hg:`help patterns` for more information about the glob patterns used. @@ -127,36 +129,119 @@ 'cleverdecode:': tocrlf } +class eolfile(object): + def __init__(self, ui, root, data): + self._decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'} + self._encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'} -def hook(ui, repo, node, hooktype, **kwargs): - """verify that files have expected EOLs""" + self.cfg = config.config() + # Our files should not be touched. The pattern must be + # inserted first override a '** = native' pattern. + self.cfg.set('patterns', '.hg*', 'BIN') + # We can then parse the user's patterns. + self.cfg.parse('.hgeol', data) + + isrepolf = self.cfg.get('repository', 'native') != 'CRLF' + self._encode['NATIVE'] = isrepolf and 'to-lf' or 'to-crlf' + iswdlf = ui.config('eol', 'native', os.linesep) in ('LF', '\n') + self._decode['NATIVE'] = iswdlf and 'to-lf' or 'to-crlf' + + include = [] + exclude = [] + for pattern, style in self.cfg.items('patterns'): + key = style.upper() + if key == 'BIN': + exclude.append(pattern) + else: + include.append(pattern) + # This will match the files for which we need to care + # about inconsistent newlines. + self.match = match.match(root, '', [], include, exclude) + + def setfilters(self, ui): + for pattern, style in self.cfg.items('patterns'): + key = style.upper() + try: + ui.setconfig('decode', pattern, self._decode[key]) + ui.setconfig('encode', pattern, self._encode[key]) + except KeyError: + ui.warn(_("ignoring unknown EOL style '%s' from %s\n") + % (style, self.cfg.source('patterns', pattern))) + + def checkrev(self, repo, ctx, files): + failed = [] + for f in (files or ctx.files()): + if f not in ctx: + continue + for pattern, style in self.cfg.items('patterns'): + if not match.match(repo.root, '', [pattern])(f): + continue + target = self._encode[style.upper()] + data = ctx[f].data() + if (target == "to-lf" and "\r\n" in data + or target == "to-crlf" and singlelf.search(data)): + failed.append((str(ctx), target, f)) + break + return failed + +def parseeol(ui, repo, nodes): + try: + for node in nodes: + try: + if node is None: + # Cannot use workingctx.data() since it would load + # and cache the filters before we configure them. + data = repo.wfile('.hgeol').read() + else: + data = repo[node]['.hgeol'].data() + return eolfile(ui, repo.root, data) + except (IOError, LookupError): + pass + except error.ParseError, inst: + ui.warn(_("warning: ignoring .hgeol file due to parse error " + "at %s: %s\n") % (inst.args[1], inst.args[0])) + return None + +def _checkhook(ui, repo, node, headsonly): + # Get revisions to check and touched files at the same time files = set() + revs = set() for rev in xrange(repo[node].rev(), len(repo)): - files.update(repo[rev].files()) - tip = repo['tip'] - for f in files: - if f not in tip: - continue - for pattern, target in ui.configitems('encode'): - if match.match(repo.root, '', [pattern])(f): - data = tip[f].data() - if target == "to-lf" and "\r\n" in data: - raise util.Abort(_("%s should not have CRLF line endings") - % f) - elif target == "to-crlf" and singlelf.search(data): - raise util.Abort(_("%s should not have LF line endings") - % f) - # Ignore other rules for this file - break + revs.add(rev) + if headsonly: + ctx = repo[rev] + files.update(ctx.files()) + for pctx in ctx.parents(): + revs.discard(pctx.rev()) + failed = [] + for rev in revs: + ctx = repo[rev] + eol = parseeol(ui, repo, [ctx.node()]) + if eol: + failed.extend(eol.checkrev(repo, ctx, files)) + if failed: + eols = {'to-lf': 'CRLF', 'to-crlf': 'LF'} + msgs = [] + for node, target, f in failed: + msgs.append(_(" %s in %s should not have %s line endings") % + (f, node, eols[target])) + raise util.Abort(_("end-of-line check failed:\n") + "\n".join(msgs)) + +def checkallhook(ui, repo, node, hooktype, **kwargs): + """verify that files have expected EOLs""" + _checkhook(ui, repo, node, False) + +def checkheadshook(ui, repo, node, hooktype, **kwargs): + """verify that files have expected EOLs""" + _checkhook(ui, repo, node, True) + +# "checkheadshook" used to be called "hook" +hook = checkheadshook def preupdate(ui, repo, hooktype, parent1, parent2): #print "preupdate for %s: %s -> %s" % (repo.root, parent1, parent2) - try: - repo.readhgeol(parent1) - except error.ParseError, inst: - ui.warn(_("warning: ignoring .hgeol file due to parse error " - "at %s: %s\n") % (inst.args[1], inst.args[0])) + repo.loadeol([parent1]) return False def uisetup(ui): @@ -184,66 +269,15 @@ class eolrepo(repo.__class__): - _decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'} - _encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'} - - def readhgeol(self, node=None, data=None): - if data is None: - try: - if node is None: - data = self.wfile('.hgeol').read() - else: - data = self[node]['.hgeol'].data() - except (IOError, LookupError): - return None - - if self.ui.config('eol', 'native', os.linesep) in ('LF', '\n'): - self._decode['NATIVE'] = 'to-lf' - else: - self._decode['NATIVE'] = 'to-crlf' - - eol = config.config() - # Our files should not be touched. The pattern must be - # inserted first override a '** = native' pattern. - eol.set('patterns', '.hg*', 'BIN') - # We can then parse the user's patterns. - eol.parse('.hgeol', data) - - if eol.get('repository', 'native') == 'CRLF': - self._encode['NATIVE'] = 'to-crlf' - else: - self._encode['NATIVE'] = 'to-lf' - - for pattern, style in eol.items('patterns'): - key = style.upper() - try: - self.ui.setconfig('decode', pattern, self._decode[key]) - self.ui.setconfig('encode', pattern, self._encode[key]) - except KeyError: - self.ui.warn(_("ignoring unknown EOL style '%s' from %s\n") - % (style, eol.source('patterns', pattern))) - - include = [] - exclude = [] - for pattern, style in eol.items('patterns'): - key = style.upper() - if key == 'BIN': - exclude.append(pattern) - else: - include.append(pattern) - - # This will match the files for which we need to care - # about inconsistent newlines. - return match.match(self.root, '', [], include, exclude) + def loadeol(self, nodes): + eol = parseeol(self.ui, self, nodes) + if eol is None: + return None + eol.setfilters(self.ui) + return eol.match def _hgcleardirstate(self): - try: - self._eolfile = self.readhgeol() or self.readhgeol('tip') - except error.ParseError, inst: - ui.warn(_("warning: ignoring .hgeol file due to parse error " - "at %s: %s\n") % (inst.args[1], inst.args[0])) - self._eolfile = None - + self._eolfile = self.loadeol([None, 'tip']) if not self._eolfile: self._eolfile = util.never return diff -r 9777df929035 -r 30a0e3519f69 hgext/keyword.py --- a/hgext/keyword.py Mon Mar 14 21:35:31 2011 +0100 +++ b/hgext/keyword.py Tue Mar 15 16:53:46 2011 -0500 @@ -109,11 +109,26 @@ } # date like in cvs' $Date -utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S') +def utcdate(text): + ''':utcdate: Date. Returns a UTC-date in this format: "2009/08/18 11:00:13". + ''' + return util.datestr((text[0], 0), '%Y/%m/%d %H:%M:%S') # date like in svn's $Date -svnisodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)') +def svnisodate(text): + ''':svnisodate: Date. Returns a date in this format: "2009-08-18 13:00:13 + +0200 (Tue, 18 Aug 2009)". + ''' + return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)') # date like in svn's $Id -svnutcdate = lambda x: util.datestr((x[0], 0), '%Y-%m-%d %H:%M:%SZ') +def svnutcdate(text): + ''':svnutcdate: Date. Returns a UTC-date in this format: "2009-08-18 + 11:00:13Z". + ''' + return util.datestr((text[0], 0), '%Y-%m-%d %H:%M:%SZ') + +templatefilters.filters.update({'utcdate': utcdate, + 'svnisodate': svnisodate, + 'svnutcdate': svnutcdate}) # make keyword tools accessible kwtools = {'templater': None, 'hgcmd': ''} @@ -176,9 +191,6 @@ for k, v in kwmaps) else: self.templates = _defaultkwmaps(self.ui) - templatefilters.filters.update({'utcdate': utcdate, - 'svnisodate': svnisodate, - 'svnutcdate': svnutcdate}) @util.propertycache def escape(self): diff -r 9777df929035 -r 30a0e3519f69 hgext/mq.py --- a/hgext/mq.py Mon Mar 14 21:35:31 2011 +0100 +++ b/hgext/mq.py Tue Mar 15 16:53:46 2011 -0500 @@ -1899,7 +1899,7 @@ With -g/--git, patches imported with --rev will use the git diff format. See the diffs help topic for information on why this is important for preserving rename/copy information and permission - changes. + changes. Use :hg:`qfinish` to remove changesets from mq control. To import a patch from standard input, pass - as the patch file. When importing from standard input, a patch name must be specified diff -r 9777df929035 -r 30a0e3519f69 hgext/rebase.py --- a/hgext/rebase.py Mon Mar 14 21:35:31 2011 +0100 +++ b/hgext/rebase.py Tue Mar 15 16:53:46 2011 -0500 @@ -90,7 +90,7 @@ contf = opts.get('continue') abortf = opts.get('abort') collapsef = opts.get('collapse', False) - extrafn = opts.get('extrafn') + extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion keepf = opts.get('keep', False) keepbranchesf = opts.get('keepbranches', False) detachf = opts.get('detach', False) @@ -138,8 +138,7 @@ external = checkexternal(repo, state, targetancestors) if keepbranchesf: - if extrafn: - raise util.Abort(_('cannot use both keepbranches and extrafn')) + assert not extrafn, 'cannot use both keepbranches and extrafn' def extrafn(ctx, extra): extra['branch'] = ctx.branch() diff -r 9777df929035 -r 30a0e3519f69 hgext/transplant.py --- a/hgext/transplant.py Mon Mar 14 21:35:31 2011 +0100 +++ b/hgext/transplant.py Tue Mar 15 16:53:46 2011 -0500 @@ -177,12 +177,11 @@ lock.release() wlock.release() - def filter(self, filter, changelog, patchfile): + def filter(self, filter, node, changelog, patchfile): '''arbitrarily rewrite changeset before applying it''' self.ui.status(_('filtering %s\n') % patchfile) user, date, msg = (changelog[1], changelog[2], changelog[4]) - fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-') fp = os.fdopen(fd, 'w') fp.write("# HG changeset patch\n") @@ -194,7 +193,9 @@ try: util.system('%s %s %s' % (filter, util.shellquote(headerfile), util.shellquote(patchfile)), - environ={'HGUSER': changelog[1]}, + environ={'HGUSER': changelog[1], + 'HGREVISION': revlog.hex(node), + }, onerr=util.Abort, errprefix=_('filter failed')) user, date, msg = self.parselog(file(headerfile))[1:4] finally: @@ -209,7 +210,7 @@ date = "%d %d" % (time, timezone) extra = {'transplant_source': node} if filter: - (user, date, message) = self.filter(filter, cl, patchfile) + (user, date, message) = self.filter(filter, node, cl, patchfile) if log: # we don't translate messages inserted into commits diff -r 9777df929035 -r 30a0e3519f69 mercurial/ancestor.py --- a/mercurial/ancestor.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/ancestor.py Tue Mar 15 16:53:46 2011 -0500 @@ -9,9 +9,10 @@ def ancestor(a, b, pfunc): """ - return a minimal-distance ancestor of nodes a and b, or None if there is no - such ancestor. Note that there can be several ancestors with the same - (minimal) distance, and the one returned is arbitrary. + Returns the common ancestor of a and b that is furthest from a + root (as measured by longest path) or None if no ancestor is + found. If there are multiple common ancestors at the same + distance, the first one found is returned. pfunc must return a list of parent vertices for a given vertex """ @@ -22,6 +23,7 @@ a, b = sorted([a, b]) # find depth from root of all ancestors + # depth is stored as a negative for heapq parentcache = {} visit = [a, b] depth = {} @@ -39,6 +41,7 @@ if p not in depth: visit.append(p) if visit[-1] == vertex: + # -(maximum distance of parents + 1) depth[vertex] = min([depth[p] for p in pl]) - 1 visit.pop() diff -r 9777df929035 -r 30a0e3519f69 mercurial/bookmarks.py --- a/mercurial/bookmarks.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/bookmarks.py Tue Mar 15 16:53:46 2011 -0500 @@ -101,13 +101,7 @@ if current == mark: return - refs = repo._bookmarks - - # do not update if we do update to a rev equal to the current bookmark - if (mark and mark not in refs and - current and refs[current] == repo.changectx('.').node()): - return - if mark not in refs: + if mark not in repo._bookmarks: mark = '' if not valid(mark): raise util.Abort(_("bookmark '%s' contains illegal " @@ -163,6 +157,28 @@ finally: w.release() +def updatefromremote(ui, repo, remote): + ui.debug("checking for updated bookmarks\n") + rb = remote.listkeys('bookmarks') + changed = False + for k in rb.keys(): + if k in repo._bookmarks: + nr, nl = rb[k], repo._bookmarks[k] + if nr in repo: + cr = repo[nr] + cl = repo[nl] + if cl.rev() >= cr.rev(): + continue + if cr in cl.descendants(): + repo._bookmarks[k] = cr.node() + changed = True + ui.status(_("updating bookmark %s\n") % k) + else: + ui.warn(_("not updating divergent" + " bookmark %s\n") % k) + if changed: + write(repo) + def diff(ui, repo, remote): ui.status(_("searching for changed bookmarks\n")) diff -r 9777df929035 -r 30a0e3519f69 mercurial/commands.py --- a/mercurial/commands.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/commands.py Tue Mar 15 16:53:46 2011 -0500 @@ -13,7 +13,7 @@ import patch, help, mdiff, url, encoding, templatekw, discovery import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server import merge as mergemod -import minirst, revset +import minirst, revset, templatefilters import dagparser # Commands start here, listed alphabetically @@ -303,7 +303,8 @@ return 0 def bisect(ui, repo, rev=None, extra=None, command=None, - reset=None, good=None, bad=None, skip=None, noupdate=None): + reset=None, good=None, bad=None, skip=None, extend=None, + noupdate=None): """subdivision search of changesets This command helps to find changesets which introduce problems. To @@ -326,6 +327,17 @@ Returns 0 on success. """ + def extendbisectrange(nodes, good): + # bisect is incomplete when it ends on a merge node and + # one of the parent was not checked. + parents = repo[nodes[0]].parents() + if len(parents) > 1: + side = good and state['bad'] or state['good'] + num = len(set(i.node() for i in parents) & set(side)) + if num == 1: + return parents[0].ancestor(parents[1]) + return None + def print_result(nodes, good): displayer = cmdutil.show_changeset(ui, repo, {}) if len(nodes) == 1: @@ -336,14 +348,12 @@ ui.write(_("The first bad revision is:\n")) displayer.show(repo[nodes[0]]) parents = repo[nodes[0]].parents() - if len(parents) > 1: - side = good and state['bad'] or state['good'] - num = len(set(i.node() for i in parents) & set(side)) - if num == 1: - common = parents[0].ancestor(parents[1]) - ui.write(_('Not all ancestors of this changeset have been' - ' checked.\nTo check the other ancestors, start' - ' from the common ancestor, %s.\n' % common)) + extendnode = extendbisectrange(nodes, good) + if extendnode is not None: + ui.write(_('Not all ancestors of this changeset have been' + ' checked.\nUse bisect --extend to continue the ' + 'bisection from\nthe common ancestor, %s.\n') + % short(extendnode.node())) else: # multiple possible revisions if good: @@ -376,7 +386,7 @@ bad = True else: reset = True - elif extra or good + bad + skip + reset + bool(command) > 1: + elif extra or good + bad + skip + reset + extend + bool(command) > 1: raise util.Abort(_('incompatible arguments')) if reset: @@ -440,6 +450,18 @@ # actually bisect nodes, changesets, good = hbisect.bisect(repo.changelog, state) + if extend: + if not changesets: + extendnode = extendbisectrange(nodes, good) + if extendnode is not None: + ui.write(_("Extending search to changeset %d:%s\n" + % (extendnode.rev(), short(extendnode.node())))) + if noupdate: + return + cmdutil.bail_if_changed(repo) + return hg.clean(repo, extendnode.node()) + raise util.Abort(_("nothing to extend")) + if changesets == 0: print_result(nodes, good) else: @@ -1175,6 +1197,7 @@ if len(items) > 1 or items and sections: raise util.Abort(_('only one config item permitted')) for section, name, value in ui.walkconfig(untrusted=untrusted): + value = str(value).replace('\n', '\\n') sectname = section + '.' + name if values: for v in values: @@ -1595,7 +1618,7 @@ msg = _('cannot specify --rev and --change at the same time') raise util.Abort(msg) elif change: - node2 = repo.lookup(change) + node2 = cmdutil.revsingle(repo, change, None).node() node1 = repo[node2].parents()[0].node() else: node1, node2 = cmdutil.revpair(repo, revs) @@ -1962,7 +1985,7 @@ Returns 0 if successful. """ option_lists = [] - textwidth = ui.termwidth() - 2 + textwidth = min(ui.termwidth(), 80) - 2 def addglobalopts(aliases): if ui.verbose: @@ -2141,6 +2164,8 @@ 'extensions\n')) help.addtopichook('revsets', revset.makedoc) + help.addtopichook('templates', templatekw.makedoc) + help.addtopichook('templates', templatefilters.makedoc) if name and name != 'shortlist': i = None @@ -2267,6 +2292,7 @@ output = [] revs = [] + bms = [] if source: source, branches = hg.parseurl(ui.expandpath(source)) repo = hg.repository(ui, source) @@ -2277,10 +2303,19 @@ rev = revs[0] if not rev: rev = "tip" - if num or branch or tags or bookmarks: - raise util.Abort(_("can't query remote revision number," - " branch, tags, or bookmarks")) - output = [hexfunc(repo.lookup(rev))] + if num or branch or tags: + raise util.Abort( + _("can't query remote revision number, branch, or tags")) + + remoterev = repo.lookup(rev) + if default or id: + output = [hexfunc(remoterev)] + + if 'bookmarks' in repo.listkeys('namespaces'): + hexremoterev = hex(remoterev) + bms = [bm for bm, bmrev in repo.listkeys('bookmarks').iteritems() + if bmrev == hexremoterev] + elif not rev: ctx = repo[None] parents = ctx.parents() @@ -2300,6 +2335,9 @@ if num: output.append(str(ctx.rev())) + if repo.local(): + bms = ctx.bookmarks() + if repo.local() and default and not ui.quiet: b = ctx.branch() if b != 'default': @@ -2310,8 +2348,9 @@ if t: output.append(t) + if default and not ui.quiet: # multiple bookmarks for a single parent separated by '/' - bm = '/'.join(ctx.bookmarks()) + bm = '/'.join(bms) if bm: output.append(bm) @@ -2322,7 +2361,7 @@ output.extend(ctx.tags()) if bookmarks: - output.extend(ctx.bookmarks()) + output.extend(bms) ui.write("%s\n" % ' '.join(output)) @@ -2938,6 +2977,7 @@ raise util.Abort(err) modheads = repo.pull(other, heads=revs, force=opts.get('force')) + bookmarks.updatefromremote(ui, repo, other) if checkout: checkout = str(repo.changelog.rev(other.lookup(checkout))) repo._subtoppath = source @@ -4053,7 +4093,7 @@ if rev and node: raise util.Abort(_("please specify just one revision")) - if not rev: + if rev is None or rev == '': rev = node # if we defined a bookmark, we have to remember the original bookmark name @@ -4269,6 +4309,7 @@ ('g', 'good', False, _('mark changeset good')), ('b', 'bad', False, _('mark changeset bad')), ('s', 'skip', False, _('skip testing changeset')), + ('e', 'extend', False, _('extend the bisect range')), ('c', 'command', '', _('use command to check changeset state'), _('CMD')), ('U', 'noupdate', False, _('do not update to target'))], diff -r 9777df929035 -r 30a0e3519f69 mercurial/help.py --- a/mercurial/help.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/help.py Tue Mar 15 16:53:46 2011 -0500 @@ -115,3 +115,19 @@ def addtopichook(topic, rewriter): helphooks.setdefault(topic, []).append(rewriter) + +def makeitemsdoc(topic, doc, marker, items): + """Extract docstring from the items key to function mapping, build a + .single documentation block and use it to overwrite the marker in doc + """ + entries = [] + for name in sorted(items): + text = (items[name].__doc__ or '').rstrip() + if not text: + continue + text = gettext(text) + lines = text.splitlines() + lines[1:] = [(' ' + l.strip()) for l in lines[1:]] + entries.append('\n'.join(lines)) + entries = '\n\n'.join(entries) + return doc.replace(marker, entries) diff -r 9777df929035 -r 30a0e3519f69 mercurial/help/templates.txt --- a/mercurial/help/templates.txt Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/help/templates.txt Tue Mar 15 16:53:46 2011 -0500 @@ -23,52 +23,7 @@ keywords depends on the exact context of the templater. These keywords are usually available for templating a log-like command: -:author: String. The unmodified author of the changeset. - -:branch: String. The name of the branch on which the changeset was - committed. - -:branches: List of strings. The name of the branch on which the - changeset was committed. Will be empty if the branch name was - default. - -:children: List of strings. The children of the changeset. - -:date: Date information. The date when the changeset was committed. - -:desc: String. The text of the changeset description. - -:diffstat: String. Statistics of changes with the following format: - "modified files: +added/-removed lines" - -:files: List of strings. All files modified, added, or removed by this - changeset. - -:file_adds: List of strings. Files added by this changeset. - -:file_copies: List of strings. Files copied in this changeset with - their sources. - -:file_copies_switch: List of strings. Like "file_copies" but displayed - only if the --copied switch is set. - -:file_mods: List of strings. Files modified by this changeset. - -:file_dels: List of strings. Files removed by this changeset. - -:node: String. The changeset identification hash, as a 40 hexadecimal - digit string. - -:parents: List of strings. The parents of the changeset. - -:rev: Integer. The repository-local changeset revision number. - -:tags: List of strings. Any tags associated with the changeset. - -:latesttag: String. Most recent global tag in the ancestors of this - changeset. - -:latesttagdistance: Integer. Longest path to the latest tag. +.. keywordsmarker The "date" keyword does not produce human-readable output. If you want to use a date in your output, you can use a filter to process @@ -82,82 +37,4 @@ List of filters: -:addbreaks: Any text. Add an XHTML "
" tag before the end of - every line except the last. - -:age: Date. Returns a human-readable date/time difference between the - given date/time and the current date/time. - -:basename: Any text. Treats the text as a path, and returns the last - component of the path after splitting by the path separator - (ignoring trailing separators). For example, "foo/bar/baz" becomes - "baz" and "foo/bar//" becomes "bar". - -:stripdir: Treat the text as path and strip a directory level, if - possible. For example, "foo" and "foo/bar" becomes "foo". - -:date: Date. Returns a date in a Unix date format, including the - timezone: "Mon Sep 04 15:13:13 2006 0700". - -:domain: Any text. Finds the first string that looks like an email - address, and extracts just the domain component. Example: ``User - `` becomes ``example.com``. - -:email: Any text. Extracts the first string that looks like an email - address. Example: ``User `` becomes - ``user@example.com``. - -:escape: Any text. Replaces the special XML/XHTML characters "&", "<" - and ">" with XML entities. - -:hex: Any text. Convert a binary Mercurial node identifier into - its long hexadecimal representation. - -:fill68: Any text. Wraps the text to fit in 68 columns. - -:fill76: Any text. Wraps the text to fit in 76 columns. - -:firstline: Any text. Returns the first line of text. - -:nonempty: Any text. Returns '(none)' if the string is empty. - -:hgdate: Date. Returns the date as a pair of numbers: "1157407993 - 25200" (Unix timestamp, timezone offset). - -:isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00 - +0200". - -:isodatesec: Date. Returns the date in ISO 8601 format, including - seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date - filter. - -:localdate: Date. Converts a date to local date. - -:obfuscate: Any text. Returns the input text rendered as a sequence of - XML entities. - -:person: Any text. Returns the text before an email address. - -:rfc822date: Date. Returns a date using the same format used in email - headers: "Tue, 18 Aug 2009 13:00:13 +0200". - -:rfc3339date: Date. Returns a date using the Internet date format - specified in RFC 3339: "2009-08-18T13:00:13+02:00". - -:short: Changeset hash. Returns the short form of a changeset hash, - i.e. a 12 hexadecimal digit string. - -:shortdate: Date. Returns a date like "2006-09-18". - -:stringify: Any type. Turns the value into text by converting values into - text and concatenating them. - -:strip: Any text. Strips all leading and trailing whitespace. - -:tabindent: Any text. Returns the text, with every line except the - first starting with a tab character. - -:urlescape: Any text. Escapes all "special" characters. For example, - "foo bar" becomes "foo%20bar". - -:user: Any text. Returns the user portion of an email address. +.. filtersmarker diff -r 9777df929035 -r 30a0e3519f69 mercurial/hg.py --- a/mercurial/hg.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/hg.py Tue Mar 15 16:53:46 2011 -0500 @@ -9,7 +9,7 @@ from i18n import _ from lock import release from node import hex, nullid, nullrev, short -import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo +import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks import lock, util, extensions, error, encoding, node import cmdutil, discovery, url import merge as mergemod @@ -366,6 +366,21 @@ dest_repo.ui.status(_("updating to branch %s\n") % bn) _update(dest_repo, uprev) + # clone all bookmarks + if dest_repo.local() and src_repo.capable("pushkey"): + rb = src_repo.listkeys('bookmarks') + for k, n in rb.iteritems(): + try: + m = dest_repo.lookup(n) + dest_repo._bookmarks[k] = m + except: + pass + if rb: + bookmarks.write(dest_repo) + elif src_repo.local() and dest_repo.capable("pushkey"): + for k, n in src_repo._bookmarks.iteritems(): + dest_repo.pushkey('bookmarks', k, '', hex(n)) + return src_repo, dest_repo finally: release(src_lock, dest_lock) diff -r 9777df929035 -r 30a0e3519f69 mercurial/hgweb/common.py --- a/mercurial/hgweb/common.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/hgweb/common.py Tue Mar 15 16:53:46 2011 -0500 @@ -73,10 +73,29 @@ def __init__(self, code, message=None, headers=[]): if message is None: message = _statusmessage(code) - Exception.__init__(self, code, message) + Exception.__init__(self) self.code = code self.message = message self.headers = headers + def __str__(self): + return self.message + +class continuereader(object): + def __init__(self, f, write): + self.f = f + self._write = write + self.continued = False + + def read(self, amt=-1): + if not self.continued: + self.continued = True + self._write('HTTP/1.1 100 Continue\r\n\r\n') + return self.f.read(amt) + + def __getattr__(self, attr): + if attr in ('close', 'readline', 'readlines', '__iter__'): + return getattr(self.f, attr) + raise AttributeError() def _statusmessage(code): from BaseHTTPServer import BaseHTTPRequestHandler diff -r 9777df929035 -r 30a0e3519f69 mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/hgweb/hgweb_mod.py Tue Mar 15 16:53:46 2011 -0500 @@ -121,7 +121,11 @@ self.check_perm(req, perms[cmd]) return protocol.call(self.repo, req, cmd) except ErrorResponse, inst: - if cmd == 'unbundle': + # A client that sends unbundle without 100-continue will + # break if we respond early. + if (cmd == 'unbundle' and + req.env.get('HTTP_EXPECT', + '').lower() != '100-continue'): req.drain() req.respond(inst, protocol.HGTYPE) return '0\n%s\n' % inst.message diff -r 9777df929035 -r 30a0e3519f69 mercurial/hgweb/hgwebdir_mod.py --- a/mercurial/hgweb/hgwebdir_mod.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/hgweb/hgwebdir_mod.py Tue Mar 15 16:53:46 2011 -0500 @@ -40,9 +40,10 @@ def urlrepos(prefix, roothead, paths): """yield url paths and filesystem paths from a list of repo paths - >>> list(urlrepos('hg', '/opt', ['/opt/r', '/opt/r/r', '/opt'])) + >>> conv = lambda seq: [(v, util.pconvert(p)) for v,p in seq] + >>> conv(urlrepos('hg', '/opt', ['/opt/r', '/opt/r/r', '/opt'])) [('hg/r', '/opt/r'), ('hg/r/r', '/opt/r/r'), ('hg', '/opt')] - >>> list(urlrepos('', '/opt', ['/opt/r', '/opt/r/r', '/opt'])) + >>> conv(urlrepos('', '/opt', ['/opt/r', '/opt/r/r', '/opt'])) [('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')] """ for path in paths: diff -r 9777df929035 -r 30a0e3519f69 mercurial/hgweb/server.py --- a/mercurial/hgweb/server.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/hgweb/server.py Tue Mar 15 16:53:46 2011 -0500 @@ -8,6 +8,7 @@ import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback from mercurial import util, error +from mercurial.hgweb import common from mercurial.i18n import _ def _splitURI(uri): @@ -111,6 +112,9 @@ env['SERVER_PROTOCOL'] = self.request_version env['wsgi.version'] = (1, 0) env['wsgi.url_scheme'] = self.url_scheme + if env.get('HTTP_EXPECT', '').lower() == '100-continue': + self.rfile = common.continuereader(self.rfile, self.wfile.write) + env['wsgi.input'] = self.rfile env['wsgi.errors'] = _error_logger(self) env['wsgi.multithread'] = isinstance(self.server, diff -r 9777df929035 -r 30a0e3519f69 mercurial/hgweb/wsgicgi.py --- a/mercurial/hgweb/wsgicgi.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/hgweb/wsgicgi.py Tue Mar 15 16:53:46 2011 -0500 @@ -10,6 +10,7 @@ import os, sys from mercurial import util +from mercurial.hgweb import common def launch(application): util.set_binary(sys.stdin) @@ -23,7 +24,11 @@ if environ['PATH_INFO'].startswith(scriptname): environ['PATH_INFO'] = environ['PATH_INFO'][len(scriptname):] - environ['wsgi.input'] = sys.stdin + stdin = sys.stdin + if environ.get('HTTP_EXPECT', '').lower() == '100-continue': + stdin = common.continuereader(stdin, sys.stdout.write) + + environ['wsgi.input'] = stdin environ['wsgi.errors'] = sys.stderr environ['wsgi.version'] = (1, 0) environ['wsgi.multithread'] = False diff -r 9777df929035 -r 30a0e3519f69 mercurial/httprepo.py --- a/mercurial/httprepo.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/httprepo.py Tue Mar 15 16:53:46 2011 -0500 @@ -52,10 +52,13 @@ # look up capabilities only when needed + def _fetchcaps(self): + self.caps = set(self._call('capabilities').split()) + def get_caps(self): if self.caps is None: try: - self.caps = set(self._call('capabilities').split()) + self._fetchcaps() except error.RepoError: self.caps = set() self.ui.debug('capabilities: %s\n' % @@ -73,8 +76,7 @@ data = args.pop('data', None) headers = args.pop('headers', {}) self.ui.debug("sending %s command\n" % cmd) - q = {"cmd": cmd} - q.update(args) + q = [('cmd', cmd)] + sorted(args.items()) qs = '?%s' % urllib.urlencode(q) cu = "%s%s" % (self._url, qs) req = urllib2.Request(cu, data, headers) @@ -196,7 +198,13 @@ inst = httpsrepository(ui, path) else: inst = httprepository(ui, path) - inst.between([(nullid, nullid)]) + try: + # Try to do useful work when checking compatibility. + # Usually saves a roundtrip since we want the caps anyway. + inst._fetchcaps() + except error.RepoError: + # No luck, try older compatibility check. + inst.between([(nullid, nullid)]) return inst except error.RepoError: ui.note('(falling back to static-http)\n') diff -r 9777df929035 -r 30a0e3519f69 mercurial/localrepo.py --- a/mercurial/localrepo.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/localrepo.py Tue Mar 15 16:53:46 2011 -0500 @@ -1346,27 +1346,6 @@ finally: lock.release() - self.ui.debug("checking for updated bookmarks\n") - rb = remote.listkeys('bookmarks') - changed = False - for k in rb.keys(): - if k in self._bookmarks: - nr, nl = rb[k], self._bookmarks[k] - if nr in self: - cr = self[nr] - cl = self[nl] - if cl.rev() >= cr.rev(): - continue - if cr in cl.descendants(): - self._bookmarks[k] = cr.node() - changed = True - self.ui.status(_("updating bookmark %s\n") % k) - else: - self.ui.warn(_("not updating divergent" - " bookmark %s\n") % k) - if changed: - bookmarks.write(self) - return result def checkpush(self, force, revs): diff -r 9777df929035 -r 30a0e3519f69 mercurial/merge.py --- a/mercurial/merge.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/merge.py Tue Mar 15 16:53:46 2011 -0500 @@ -493,7 +493,6 @@ p1, p2 = pl[0], repo[node] pa = p1.ancestor(p2) fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2) - fastforward = False ### check phase if not overwrite and len(pl) > 1: @@ -503,9 +502,7 @@ raise util.Abort(_("merging with a working directory ancestor" " has no effect")) elif pa == p1: - if p1.branch() != p2.branch(): - fastforward = True - else: + if p1.branch() == p2.branch(): raise util.Abort(_("nothing to merge (use 'hg update'" " or check 'hg heads')")) if not force and (wc.files() or wc.deleted()): @@ -550,7 +547,7 @@ if not partial: repo.dirstate.setparents(fp1, fp2) recordupdates(repo, action, branchmerge) - if not branchmerge and not fastforward: + if not branchmerge: repo.dirstate.setbranch(p2.branch()) finally: wlock.release() diff -r 9777df929035 -r 30a0e3519f69 mercurial/revset.py --- a/mercurial/revset.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/revset.py Tue Mar 15 16:53:46 2011 -0500 @@ -6,10 +6,10 @@ # GNU General Public License version 2 or any later version. import re -import parser, util, error, discovery +import parser, util, error, discovery, help, hbisect import bookmarks as bookmarksmod import match as matchmod -from i18n import _, gettext +from i18n import _ elements = { "(": (20, ("group", 1, ")"), ("func", 1, ")")), @@ -683,12 +683,27 @@ for r in bookmarksmod.listbookmarks(repo).values()]) return [r for r in subset if r in bms] +def bisected(repo, subset, x): + """``bisected(string)`` + Changesets marked in the specified bisect state (good, bad, skip). + """ + state = getstring(x, _("bisect requires a string")).lower() + if state not in ('good', 'bad', 'skip', 'unknown'): + raise ParseError(_('invalid bisect state')) + marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state]) + l = [] + for r in subset: + if r in marked: + l.append(r) + return l + symbols = { "adds": adds, "all": getall, "ancestor": ancestor, "ancestors": ancestors, "author": author, + "bisected": bisected, "bookmark": bookmark, "branch": branch, "children": children, @@ -815,19 +830,7 @@ return mfunc def makedoc(topic, doc): - """Generate and include predicates help in revsets topic.""" - predicates = [] - for name in sorted(symbols): - text = symbols[name].__doc__ - if not text: - continue - text = gettext(text.rstrip()) - lines = text.splitlines() - lines[1:] = [(' ' + l.strip()) for l in lines[1:]] - predicates.append('\n'.join(lines)) - predicates = '\n\n'.join(predicates) - doc = doc.replace('.. predicatesmarker', predicates) - return doc + return help.makeitemsdoc(topic, doc, '.. predicatesmarker', symbols) # tell hggettext to extract docstrings from these functions: i18nfunctions = symbols.values() diff -r 9777df929035 -r 30a0e3519f69 mercurial/statichttprepo.py --- a/mercurial/statichttprepo.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/statichttprepo.py Tue Mar 15 16:53:46 2011 -0500 @@ -71,7 +71,7 @@ """return a function that opens files over http""" p = base def o(path, mode="r", atomictemp=None): - if 'a' in mode or 'w' in mode: + if mode not in ('r', 'rb'): raise IOError('Permission denied') f = "/".join((p, urllib.quote(path))) return httprangereader(f, urlopener) diff -r 9777df929035 -r 30a0e3519f69 mercurial/subrepo.py --- a/mercurial/subrepo.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/subrepo.py Tue Mar 15 16:53:46 2011 -0500 @@ -8,7 +8,7 @@ import errno, os, re, xml.dom.minidom, shutil, urlparse, posixpath import stat, subprocess, tarfile from i18n import _ -import config, util, node, error, cmdutil +import config, util, node, error, cmdutil, bookmarks hg = None nullstate = ('', '', 'empty') @@ -441,6 +441,7 @@ % (subrelpath(self), srcurl)) other = hg.repository(self._repo.ui, srcurl) self._repo.pull(other) + bookmarks.updatefromremote(self._repo.ui, self._repo, other) def get(self, state, overwrite=False): self._get(state) @@ -714,6 +715,12 @@ current = None return current + def _gitremote(self, remote): + out = self._gitcommand(['remote', 'show', '-n', remote]) + line = out.split('\n')[1] + i = line.index('URL: ') + len('URL: ') + return line[i:] + def _githavelocally(self, revision): out, code = self._gitdir(['cat-file', '-e', revision]) return code == 0 @@ -762,11 +769,14 @@ def _fetch(self, source, revision): if self._gitmissing(): - self._ui.status(_('cloning subrepo %s\n') % self._relpath) - self._gitnodir(['clone', self._abssource(source), self._abspath]) + source = self._abssource(source) + self._ui.status(_('cloning subrepo %s from %s\n') % + (self._relpath, source)) + self._gitnodir(['clone', source, self._abspath]) if self._githavelocally(revision): return - self._ui.status(_('pulling subrepo %s\n') % self._relpath) + self._ui.status(_('pulling subrepo %s from %s\n') % + (self._relpath, self._gitremote('origin'))) # try only origin: the originally cloned repo self._gitcommand(['fetch']) if not self._githavelocally(revision): diff -r 9777df929035 -r 30a0e3519f69 mercurial/templatefilters.py --- a/mercurial/templatefilters.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/templatefilters.py Tue Mar 15 16:53:46 2011 -0500 @@ -6,13 +6,13 @@ # GNU General Public License version 2 or any later version. import cgi, re, os, time, urllib -import encoding, node, util +import encoding, node, util, help -def stringify(thing): - '''turn nested template iterator into string.''' - if hasattr(thing, '__iter__') and not isinstance(thing, str): - return "".join([stringify(t) for t in thing if t is not None]) - return str(thing) +def addbreaks(text): + """:addbreaks: Any text. Add an XHTML "
" tag before the end of + every line except the last. + """ + return text.replace('\n', '
\n') agescales = [("year", 3600 * 24 * 365), ("month", 3600 * 24 * 30), @@ -23,7 +23,9 @@ ("second", 1)] def age(date): - '''turn a (timestamp, tzoff) tuple into an age string.''' + """:age: Date. Returns a human-readable date/time difference between the + given date/time and the current date/time. + """ def plural(t, c): if c == 1: @@ -46,6 +48,47 @@ if n >= 2 or s == 1: return '%s ago' % fmt(t, n) +def basename(path): + """:basename: Any text. Treats the text as a path, and returns the last + component of the path after splitting by the path separator + (ignoring trailing separators). For example, "foo/bar/baz" becomes + "baz" and "foo/bar//" becomes "bar". + """ + return os.path.basename(path) + +def datefilter(text): + """:date: Date. Returns a date in a Unix date format, including the + timezone: "Mon Sep 04 15:13:13 2006 0700". + """ + return util.datestr(text) + +def domain(author): + """:domain: Any text. Finds the first string that looks like an email + address, and extracts just the domain component. Example: ``User + `` becomes ``example.com``. + """ + f = author.find('@') + if f == -1: + return '' + author = author[f + 1:] + f = author.find('>') + if f >= 0: + author = author[:f] + return author + +def email(text): + """:email: Any text. Extracts the first string that looks like an email + address. Example: ``User `` becomes + ``user@example.com``. + """ + return util.email(text) + +def escape(text): + """:escape: Any text. Replaces the special XML/XHTML characters "&", "<" + and ">" with XML entities. + """ + return cgi.escape(text, True) + para_re = None space_re = None @@ -74,40 +117,45 @@ return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest for para, rest in findparas()]) +def fill68(text): + """:fill68: Any text. Wraps the text to fit in 68 columns.""" + return fill(text, 68) + +def fill76(text): + """:fill76: Any text. Wraps the text to fit in 76 columns.""" + return fill(text, 76) + def firstline(text): - '''return the first line of text''' + """:firstline: Any text. Returns the first line of text.""" try: return text.splitlines(True)[0].rstrip('\r\n') except IndexError: return '' -def nl2br(text): - '''replace raw newlines with xhtml line breaks.''' - return text.replace('\n', '
\n') +def hexfilter(text): + """:hex: Any text. Convert a binary Mercurial node identifier into + its long hexadecimal representation. + """ + return node.hex(text) -def obfuscate(text): - text = unicode(text, encoding.encoding, 'replace') - return ''.join(['&#%d;' % ord(c) for c in text]) +def hgdate(text): + """:hgdate: Date. Returns the date as a pair of numbers: "1157407993 + 25200" (Unix timestamp, timezone offset). + """ + return "%d %d" % text -def domain(author): - '''get domain of author, or empty string if none.''' - f = author.find('@') - if f == -1: - return '' - author = author[f + 1:] - f = author.find('>') - if f >= 0: - author = author[:f] - return author +def isodate(text): + """:isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00 + +0200". + """ + return util.datestr(text, '%Y-%m-%d %H:%M %1%2') -def person(author): - '''get name of author, or else username.''' - if not '@' in author: - return author - f = author.find('<') - if f == -1: - return util.shortuser(author) - return author[:f].rstrip() +def isodatesec(text): + """:isodatesec: Date. Returns the date in ISO 8601 format, including + seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date + filter. + """ + return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2') def indent(text, prefix): '''indent each non-empty line of text after first with prefix.''' @@ -124,38 +172,6 @@ yield '\n' return "".join(indenter()) -def permissions(flags): - if "l" in flags: - return "lrwxrwxrwx" - if "x" in flags: - return "-rwxr-xr-x" - return "-rw-r--r--" - -def xmlescape(text): - text = (text - .replace('&', '&') - .replace('<', '<') - .replace('>', '>') - .replace('"', '"') - .replace("'", ''')) # ' invalid in HTML - return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text) - -def uescape(c): - if ord(c) < 0x80: - return c - else: - return '\\u%04x' % ord(c) - -_escapes = [ - ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'), - ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'), -] - -def jsonescape(s): - for k, v in _escapes: - s = s.replace(k, v) - return ''.join(uescape(c) for c in s) - def json(obj): if obj is None or obj is False or obj is True: return {None: 'null', False: 'false', True: 'true'}[obj] @@ -180,49 +196,163 @@ else: raise TypeError('cannot encode type %s' % obj.__class__.__name__) +def _uescape(c): + if ord(c) < 0x80: + return c + else: + return '\\u%04x' % ord(c) + +_escapes = [ + ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'), + ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'), +] + +def jsonescape(s): + for k, v in _escapes: + s = s.replace(k, v) + return ''.join(_uescape(c) for c in s) + +def localdate(text): + """:localdate: Date. Converts a date to local date.""" + return (text[0], util.makedate()[1]) + +def nonempty(str): + """:nonempty: Any text. Returns '(none)' if the string is empty.""" + return str or "(none)" + +def obfuscate(text): + """:obfuscate: Any text. Returns the input text rendered as a sequence of + XML entities. + """ + text = unicode(text, encoding.encoding, 'replace') + return ''.join(['&#%d;' % ord(c) for c in text]) + +def permissions(flags): + if "l" in flags: + return "lrwxrwxrwx" + if "x" in flags: + return "-rwxr-xr-x" + return "-rw-r--r--" + +def person(author): + """:person: Any text. Returns the text before an email address.""" + if not '@' in author: + return author + f = author.find('<') + if f == -1: + return util.shortuser(author) + return author[:f].rstrip() + +def rfc3339date(text): + """:rfc3339date: Date. Returns a date using the Internet date format + specified in RFC 3339: "2009-08-18T13:00:13+02:00". + """ + return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2") + +def rfc822date(text): + """:rfc822date: Date. Returns a date using the same format used in email + headers: "Tue, 18 Aug 2009 13:00:13 +0200". + """ + return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2") + +def short(text): + """:short: Changeset hash. Returns the short form of a changeset hash, + i.e. a 12 hexadecimal digit string. + """ + return text[:12] + +def shortdate(text): + """:shortdate: Date. Returns a date like "2006-09-18".""" + return util.shortdate(text) + +def stringescape(text): + return text.encode('string_escape') + +def stringify(thing): + """:stringify: Any type. Turns the value into text by converting values into + text and concatenating them. + """ + if hasattr(thing, '__iter__') and not isinstance(thing, str): + return "".join([stringify(t) for t in thing if t is not None]) + return str(thing) + +def strip(text): + """:strip: Any text. Strips all leading and trailing whitespace.""" + return text.strip() + def stripdir(text): - '''Treat the text as path and strip a directory level, if possible.''' + """:stripdir: Treat the text as path and strip a directory level, if + possible. For example, "foo" and "foo/bar" becomes "foo". + """ dir = os.path.dirname(text) if dir == "": return os.path.basename(text) else: return dir -def nonempty(str): - return str or "(none)" +def tabindent(text): + """:tabindent: Any text. Returns the text, with every line except the + first starting with a tab character. + """ + return indent(text, '\t') + +def urlescape(text): + """:urlescape: Any text. Escapes all "special" characters. For example, + "foo bar" becomes "foo%20bar". + """ + return urllib.quote(text) + +def userfilter(text): + """:user: Any text. Returns the user portion of an email address.""" + return util.shortuser(text) + +def xmlescape(text): + text = (text + .replace('&', '&') + .replace('<', '<') + .replace('>', '>') + .replace('"', '"') + .replace("'", ''')) # ' invalid in HTML + return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text) filters = { - "addbreaks": nl2br, - "basename": os.path.basename, - "stripdir": stripdir, + "addbreaks": addbreaks, "age": age, - "date": lambda x: util.datestr(x), + "basename": basename, + "date": datefilter, "domain": domain, - "email": util.email, - "escape": lambda x: cgi.escape(x, True), - "fill68": lambda x: fill(x, width=68), - "fill76": lambda x: fill(x, width=76), + "email": email, + "escape": escape, + "fill68": fill68, + "fill76": fill76, "firstline": firstline, - "tabindent": lambda x: indent(x, '\t'), - "hgdate": lambda x: "%d %d" % x, - "isodate": lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2'), - "isodatesec": lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2'), + "hex": hexfilter, + "hgdate": hgdate, + "isodate": isodate, + "isodatesec": isodatesec, "json": json, "jsonescape": jsonescape, - "localdate": lambda x: (x[0], util.makedate()[1]), + "localdate": localdate, "nonempty": nonempty, "obfuscate": obfuscate, "permissions": permissions, "person": person, - "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2"), - "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2"), - "hex": node.hex, - "short": lambda x: x[:12], - "shortdate": util.shortdate, + "rfc3339date": rfc3339date, + "rfc822date": rfc822date, + "short": short, + "shortdate": shortdate, + "stringescape": stringescape, "stringify": stringify, - "strip": lambda x: x.strip(), - "urlescape": lambda x: urllib.quote(x), - "user": lambda x: util.shortuser(x), - "stringescape": lambda x: x.encode('string_escape'), + "strip": strip, + "stripdir": stripdir, + "tabindent": tabindent, + "urlescape": urlescape, + "user": userfilter, "xmlescape": xmlescape, } + +def makedoc(topic, doc): + return help.makeitemsdoc(topic, doc, '.. filtersmarker', filters) + +# tell hggettext to extract docstrings from these functions: +i18nfunctions = filters.values() diff -r 9777df929035 -r 30a0e3519f69 mercurial/templatekw.py --- a/mercurial/templatekw.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/templatekw.py Tue Mar 15 16:53:46 2011 -0500 @@ -6,7 +6,7 @@ # GNU General Public License version 2 or any later version. from node import hex -import encoding, patch, util, error +import encoding, patch, util, error, help def showlist(name, values, plural=None, **args): '''expand set of values. @@ -143,32 +143,49 @@ def showauthor(repo, ctx, templ, **args): + """:author: String. The unmodified author of the changeset.""" return ctx.user() def showbranch(**args): + """:branch: String. The name of the branch on which the changeset was + committed. + """ return args['ctx'].branch() def showbranches(**args): + """:branches: List of strings. The name of the branch on which the + changeset was committed. Will be empty if the branch name was + default. + """ branch = args['ctx'].branch() if branch != 'default': return showlist('branch', [branch], plural='branches', **args) def showbookmarks(**args): + """:bookmarks: List of strings. Any bookmarks associated with the + changeset. + """ bookmarks = args['ctx'].bookmarks() return showlist('bookmark', bookmarks, **args) def showchildren(**args): + """:children: List of strings. The children of the changeset.""" ctx = args['ctx'] childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()] return showlist('children', childrevs, **args) def showdate(repo, ctx, templ, **args): + """:date: Date information. The date when the changeset was committed.""" return ctx.date() def showdescription(repo, ctx, templ, **args): + """:desc: String. The text of the changeset description.""" return ctx.description().strip() def showdiffstat(repo, ctx, templ, **args): + """:diffstat: String. Statistics of changes with the following format: + "modified files: +added/-removed lines" + """ files, adds, removes = 0, 0, 0 for i in patch.diffstatdata(util.iterlines(ctx.diff())): files += 1 @@ -184,10 +201,14 @@ yield templ('extra', **args) def showfileadds(**args): + """:file_adds: List of strings. Files added by this changeset.""" repo, ctx, revcache = args['repo'], args['ctx'], args['revcache'] return showlist('file_add', getfiles(repo, ctx, revcache)[1], **args) def showfilecopies(**args): + """:file_copies: List of strings. Files copied in this changeset with + their sources. + """ cache, ctx = args['cache'], args['ctx'] copies = args['revcache'].get('copies') if copies is None: @@ -207,25 +228,37 @@ # provided before calling the templater, usually with a --copies # command line switch. def showfilecopiesswitch(**args): + """:file_copies_switch: List of strings. Like "file_copies" but displayed + only if the --copied switch is set. + """ copies = args['revcache'].get('copies') or [] c = [{'name': x[0], 'source': x[1]} for x in copies] return showlist('file_copy', c, plural='file_copies', **args) def showfiledels(**args): + """:file_dels: List of strings. Files removed by this changeset.""" repo, ctx, revcache = args['repo'], args['ctx'], args['revcache'] return showlist('file_del', getfiles(repo, ctx, revcache)[2], **args) def showfilemods(**args): + """:file_mods: List of strings. Files modified by this changeset.""" repo, ctx, revcache = args['repo'], args['ctx'], args['revcache'] return showlist('file_mod', getfiles(repo, ctx, revcache)[0], **args) def showfiles(**args): + """:files: List of strings. All files modified, added, or removed by this + changeset. + """ return showlist('file', args['ctx'].files(), **args) def showlatesttag(repo, ctx, templ, cache, **args): + """:latesttag: String. Most recent global tag in the ancestors of this + changeset. + """ return getlatesttags(repo, ctx, cache)[2] def showlatesttagdistance(repo, ctx, templ, cache, **args): + """:latesttagdistance: Integer. Longest path to the latest tag.""" return getlatesttags(repo, ctx, cache)[1] def showmanifest(**args): @@ -236,12 +269,17 @@ return templ('manifest', **args) def shownode(repo, ctx, templ, **args): + """:node: String. The changeset identification hash, as a 40 hexadecimal + digit string. + """ return ctx.hex() def showrev(repo, ctx, templ, **args): + """:rev: Integer. The repository-local changeset revision number.""" return ctx.rev() def showtags(**args): + """:tags: List of strings. Any tags associated with the changeset.""" return showlist('tag', args['ctx'].tags(), **args) # keywords are callables like: @@ -276,3 +314,8 @@ 'tags': showtags, } +def makedoc(topic, doc): + return help.makeitemsdoc(topic, doc, '.. keywordsmarker', keywords) + +# tell hggettext to extract docstrings from these functions: +i18nfunctions = keywords.values() diff -r 9777df929035 -r 30a0e3519f69 mercurial/ui.py --- a/mercurial/ui.py Mon Mar 14 21:35:31 2011 +0100 +++ b/mercurial/ui.py Tue Mar 15 16:53:46 2011 -0500 @@ -273,7 +273,7 @@ cfg = self._data(untrusted) for section in cfg.sections(): for name, value in self.configitems(section, untrusted): - yield section, name, str(value).replace('\n', '\\n') + yield section, name, value def plain(self): '''is plain mode active? diff -r 9777df929035 -r 30a0e3519f69 setup.py --- a/setup.py Mon Mar 14 21:35:31 2011 +0100 +++ b/setup.py Tue Mar 15 16:53:46 2011 -0500 @@ -98,24 +98,8 @@ try: import py2exe py2exeloaded = True - - # Help py2exe to find win32com.shell - try: - import modulefinder - import win32com - for p in win32com.__path__[1:]: # Take the path to win32comext - modulefinder.AddPackagePath("win32com", p) - pn = "win32com.shell" - __import__(pn) - m = sys.modules[pn] - for p in m.__path__[1:]: - modulefinder.AddPackagePath(pn, p) - except ImportError: - pass - except ImportError: py2exeloaded = False - pass def runcmd(cmd, env): p = subprocess.Popen(cmd, stdout=subprocess.PIPE, diff -r 9777df929035 -r 30a0e3519f69 tests/run-tests.py --- a/tests/run-tests.py Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/run-tests.py Tue Mar 15 16:53:46 2011 -0500 @@ -227,8 +227,8 @@ continue for line in f.readlines(): - line = line.strip() - if line and not line.startswith('#'): + line = line.split('#', 1)[0].strip() + if line: blacklist[line] = filename f.close() diff -r 9777df929035 -r 30a0e3519f69 tests/test-basic.t --- a/tests/test-basic.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-basic.t Tue Mar 15 16:53:46 2011 -0500 @@ -20,6 +20,22 @@ summary: test +Verify that updating to revision 0 via commands.update() works properly + + $ cat < update_to_rev0.py + > from mercurial import ui, hg, commands + > myui = ui.ui() + > repo = hg.repository(myui, path='.') + > commands.update(myui, repo, rev=0) + > EOF + $ hg up null + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ python ./update_to_rev0.py + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg identify -n + 0 + + Poke around at hashes: $ hg manifest --debug diff -r 9777df929035 -r 30a0e3519f69 tests/test-bisect.t --- a/tests/test-bisect.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-bisect.t Tue Mar 15 16:53:46 2011 -0500 @@ -377,6 +377,44 @@ date: Thu Jan 01 00:00:06 1970 +0000 summary: msg 6 + $ hg log -r "bisected(good)" + changeset: 0:b99c7b9c8e11 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: msg 0 + + changeset: 5:7874a09ea728 + user: test + date: Thu Jan 01 00:00:05 1970 +0000 + summary: msg 5 + + $ hg log -r "bisected(bad)" + changeset: 6:a3d5c6fdf0d3 + user: test + date: Thu Jan 01 00:00:06 1970 +0000 + summary: msg 6 + + $ hg log -r "bisected(skip)" + changeset: 1:5cd978ea5149 + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: msg 1 + + changeset: 2:db07c04beaca + user: test + date: Thu Jan 01 00:00:02 1970 +0000 + summary: msg 2 + + changeset: 3:b53bea5e2fcb + user: test + date: Thu Jan 01 00:00:03 1970 +0000 + summary: msg 3 + + changeset: 4:9b2ba8336a65 + user: test + date: Thu Jan 01 00:00:04 1970 +0000 + summary: msg 4 + $ set +e diff -r 9777df929035 -r 30a0e3519f69 tests/test-bisect2.t --- a/tests/test-bisect2.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-bisect2.t Tue Mar 15 16:53:46 2011 -0500 @@ -416,10 +416,14 @@ summary: merge 10,13 Not all ancestors of this changeset have been checked. - To check the other ancestors, start from the common ancestor, dab8161ac8fc. - $ hg bisect -g 8 # dab8161ac8fc + Use bisect --extend to continue the bisection from + the common ancestor, dab8161ac8fc. + $ hg bisect --extend + Extending search to changeset 8:dab8161ac8fc + 2 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg bisect -g # dab8161ac8fc Testing changeset 9:3c77083deb4a (3 changesets remaining, ~1 tests) - 1 files updated, 0 files merged, 2 files removed, 0 files unresolved + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg bisect -b The first bad revision is: changeset: 9:3c77083deb4a diff -r 9777df929035 -r 30a0e3519f69 tests/test-bookmarks-pushpull.t --- a/tests/test-bookmarks-pushpull.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-bookmarks-pushpull.t Tue Mar 15 16:53:46 2011 -0500 @@ -176,5 +176,19 @@ no changes found not updating divergent bookmark X importing bookmark Z + $ hg clone http://localhost:$HGPORT/ cloned-bookmarks + requesting all changes + adding changesets + adding manifests + adding file changes + added 3 changesets with 3 changes to 3 files (+1 heads) + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg -R cloned-bookmarks bookmarks + X 1:9b140be10808 + Y 0:4e3505fd9583 + Z 2:0d2164f0ce0d + foo -1:000000000000 + foobar -1:000000000000 $ kill `cat ../hg.pid` diff -r 9777df929035 -r 30a0e3519f69 tests/test-bookmarks.t --- a/tests/test-bookmarks.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-bookmarks.t Tue Mar 15 16:53:46 2011 -0500 @@ -244,3 +244,47 @@ $ hg id db815d6d32e6 tip Y/Z/x y + +test clone + + $ hg bookmarks + X2 1:925d80f479bb + Y 2:db815d6d32e6 + * Z 2:db815d6d32e6 + x y 2:db815d6d32e6 + $ hg clone . cloned-bookmarks + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg -R cloned-bookmarks bookmarks + X2 1:925d80f479bb + Y 2:db815d6d32e6 + Z 2:db815d6d32e6 + x y 2:db815d6d32e6 + +test clone with pull protocol + + $ hg clone --pull . cloned-bookmarks-pull + requesting all changes + adding changesets + adding manifests + adding file changes + added 3 changesets with 3 changes to 3 files (+1 heads) + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg -R cloned-bookmarks-pull bookmarks + X2 1:925d80f479bb + Y 2:db815d6d32e6 + Z 2:db815d6d32e6 + x y 2:db815d6d32e6 + +test clone with a specific revision + + $ hg clone -r 925d80 . cloned-bookmarks-rev + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg -R cloned-bookmarks-rev bookmarks + X2 1:925d80f479bb diff -r 9777df929035 -r 30a0e3519f69 tests/test-convert-hg-startrev.t --- a/tests/test-convert-hg-startrev.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-convert-hg-startrev.t Tue Mar 15 16:53:46 2011 -0500 @@ -1,5 +1,5 @@ - $ cat > $HGRCPATH <> $HGRCPATH < [extensions] > graphlog = > convert = diff -r 9777df929035 -r 30a0e3519f69 tests/test-convert-svn-branches.t --- a/tests/test-convert-svn-branches.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-convert-svn-branches.t Tue Mar 15 16:53:46 2011 -0500 @@ -1,7 +1,7 @@ $ "$TESTDIR/hghave" svn svn-bindings || exit 80 - $ cat > $HGRCPATH <> $HGRCPATH < [extensions] > convert = > graphlog = diff -r 9777df929035 -r 30a0e3519f69 tests/test-convert-svn-encoding.t --- a/tests/test-convert-svn-encoding.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-convert-svn-encoding.t Tue Mar 15 16:53:46 2011 -0500 @@ -1,7 +1,7 @@ $ "$TESTDIR/hghave" svn svn-bindings || exit 80 - $ cat > $HGRCPATH <> $HGRCPATH < [extensions] > convert = > graphlog = diff -r 9777df929035 -r 30a0e3519f69 tests/test-convert-svn-move.t --- a/tests/test-convert-svn-move.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-convert-svn-move.t Tue Mar 15 16:53:46 2011 -0500 @@ -5,7 +5,7 @@ > { > tr '\\' / > } - $ cat > $HGRCPATH <> $HGRCPATH < [extensions] > convert = > graphlog = diff -r 9777df929035 -r 30a0e3519f69 tests/test-convert-svn-sink.t --- a/tests/test-convert-svn-sink.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-convert-svn-sink.t Tue Mar 15 16:53:46 2011 -0500 @@ -1,5 +1,5 @@ - $ "$TESTDIR/hghave" svn svn-bindings no-outer-repo || exit 80 + $ "$TESTDIR/hghave" svn no-outer-repo || exit 80 $ fixpath() > { @@ -22,7 +22,7 @@ > ) > } - $ cat > $HGRCPATH <> $HGRCPATH < [extensions] > convert = > graphlog = diff -r 9777df929035 -r 30a0e3519f69 tests/test-convert-svn-source.t --- a/tests/test-convert-svn-source.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-convert-svn-source.t Tue Mar 15 16:53:46 2011 -0500 @@ -5,7 +5,7 @@ > { > tr '\\' / > } - $ cat > $HGRCPATH <> $HGRCPATH < [extensions] > convert = > graphlog = diff -r 9777df929035 -r 30a0e3519f69 tests/test-convert-svn-startrev.t --- a/tests/test-convert-svn-startrev.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-convert-svn-startrev.t Tue Mar 15 16:53:46 2011 -0500 @@ -1,7 +1,7 @@ $ "$TESTDIR/hghave" svn svn-bindings || exit 80 - $ cat > $HGRCPATH <> $HGRCPATH < [extensions] > convert = > graphlog = diff -r 9777df929035 -r 30a0e3519f69 tests/test-convert-svn-tags.t --- a/tests/test-convert-svn-tags.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-convert-svn-tags.t Tue Mar 15 16:53:46 2011 -0500 @@ -1,7 +1,7 @@ $ "$TESTDIR/hghave" svn svn-bindings || exit 80 - $ cat > $HGRCPATH <> $HGRCPATH < [extensions] > convert = > graphlog = diff -r 9777df929035 -r 30a0e3519f69 tests/test-debugcomplete.t --- a/tests/test-debugcomplete.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-debugcomplete.t Tue Mar 15 16:53:46 2011 -0500 @@ -199,7 +199,7 @@ addremove: similarity, include, exclude, dry-run archive: no-decode, prefix, rev, type, subrepos, include, exclude backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user - bisect: reset, good, bad, skip, command, noupdate + bisect: reset, good, bad, skip, extend, command, noupdate bookmarks: force, rev, delete, rename branch: force, clean branches: active, closed diff -r 9777df929035 -r 30a0e3519f69 tests/test-eol-add.t --- a/tests/test-eol-add.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-eol-add.t Tue Mar 15 16:53:46 2011 -0500 @@ -1,6 +1,6 @@ Test adding .hgeol - $ cat > $HGRCPATH <> $HGRCPATH < [diff] > git = 1 > EOF diff -r 9777df929035 -r 30a0e3519f69 tests/test-eol-clone.t --- a/tests/test-eol-clone.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-eol-clone.t Tue Mar 15 16:53:46 2011 -0500 @@ -1,9 +1,6 @@ Testing cloning with the EOL extension - $ cat > $HGRCPATH < [diff] - > git = True - > + $ cat >> $HGRCPATH < [extensions] > eol = > diff -r 9777df929035 -r 30a0e3519f69 tests/test-eol-hook.t --- a/tests/test-eol-hook.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-eol-hook.t Tue Mar 15 16:53:46 2011 -0500 @@ -1,14 +1,7 @@ Test the EOL hook - $ cat > $HGRCPATH < [diff] - > git = True - > EOF $ hg init main $ cat > main/.hg/hgrc < [extensions] - > eol = - > > [hooks] > pretxnchangegroup = python:hgext.eol.hook > EOF @@ -47,10 +40,12 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - error: pretxnchangegroup hook failed: a.txt should not have CRLF line endings + error: pretxnchangegroup hook failed: end-of-line check failed: + a.txt in a8ee6548cd86 should not have CRLF line endings transaction abort! rollback completed - abort: a.txt should not have CRLF line endings + abort: end-of-line check failed: + a.txt in a8ee6548cd86 should not have CRLF line endings [255] $ printf "first\nsecond\nthird\n" > a.txt @@ -73,10 +68,12 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - error: pretxnchangegroup hook failed: crlf.txt should not have LF line endings + error: pretxnchangegroup hook failed: end-of-line check failed: + crlf.txt in 004ba2132725 should not have LF line endings transaction abort! rollback completed - abort: crlf.txt should not have LF line endings + abort: end-of-line check failed: + crlf.txt in 004ba2132725 should not have LF line endings [255] $ printf "first\r\nsecond\r\nthird\r\n" > crlf.txt @@ -88,3 +85,133 @@ adding manifests adding file changes added 2 changesets with 2 changes to 1 files + + $ printf "first\r\nsecond" > b.txt + $ hg add b.txt + $ hg commit -m 'CRLF b.txt' + $ hg push ../main + pushing to ../main + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + error: pretxnchangegroup hook failed: end-of-line check failed: + b.txt in fbcf9b1025f5 should not have CRLF line endings + transaction abort! + rollback completed + abort: end-of-line check failed: + b.txt in fbcf9b1025f5 should not have CRLF line endings + [255] + + $ hg up -r -2 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ printf "some\nother\nfile" > c.txt + $ hg add c.txt + $ hg commit -m "LF c.txt, b.txt doesn't exist here" + created new head + $ hg push -f ../main + pushing to ../main + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files (+1 heads) + error: pretxnchangegroup hook failed: end-of-line check failed: + b.txt in fbcf9b1025f5 should not have CRLF line endings + transaction abort! + rollback completed + abort: end-of-line check failed: + b.txt in fbcf9b1025f5 should not have CRLF line endings + [255] + +Test checkheadshook alias + + $ cat > ../main/.hg/hgrc < [hooks] + > pretxnchangegroup = python:hgext.eol.checkheadshook + > EOF + $ hg push -f ../main + pushing to ../main + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files (+1 heads) + error: pretxnchangegroup hook failed: end-of-line check failed: + b.txt in fbcf9b1025f5 should not have CRLF line endings + transaction abort! + rollback completed + abort: end-of-line check failed: + b.txt in fbcf9b1025f5 should not have CRLF line endings + [255] + +We can fix the head and push again + + $ hg up 6 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ printf "first\nsecond" > b.txt + $ hg ci -m "remove CRLF from b.txt" + $ hg push -f ../main + pushing to ../main + searching for changes + adding changesets + adding manifests + adding file changes + added 3 changesets with 3 changes to 2 files (+1 heads) + $ hg -R ../main rollback + repository tip rolled back to revision 5 (undo push) + working directory now based on revision -1 + +Test it still fails with checkallhook + + $ cat > ../main/.hg/hgrc < [hooks] + > pretxnchangegroup = python:hgext.eol.checkallhook + > EOF + $ hg push -f ../main + pushing to ../main + searching for changes + adding changesets + adding manifests + adding file changes + added 3 changesets with 3 changes to 2 files (+1 heads) + error: pretxnchangegroup hook failed: end-of-line check failed: + b.txt in fbcf9b1025f5 should not have CRLF line endings + transaction abort! + rollback completed + abort: end-of-line check failed: + b.txt in fbcf9b1025f5 should not have CRLF line endings + [255] + +But we can push the clean head + + $ hg push -r7 -f ../main + pushing to ../main + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + +Test multiple files/revisions output + + $ printf "another\r\nbad\r\none" > d.txt + $ hg add d.txt + $ hg ci -m "add d.txt" + $ hg push -f ../main + pushing to ../main + searching for changes + adding changesets + adding manifests + adding file changes + added 3 changesets with 3 changes to 2 files (+1 heads) + error: pretxnchangegroup hook failed: end-of-line check failed: + d.txt in a7040e68714f should not have CRLF line endings + b.txt in fbcf9b1025f5 should not have CRLF line endings + transaction abort! + rollback completed + abort: end-of-line check failed: + d.txt in a7040e68714f should not have CRLF line endings + b.txt in fbcf9b1025f5 should not have CRLF line endings + [255] diff -r 9777df929035 -r 30a0e3519f69 tests/test-eol-patch.t --- a/tests/test-eol-patch.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-eol-patch.t Tue Mar 15 16:53:46 2011 -0500 @@ -1,6 +1,6 @@ Test EOL patching - $ cat > $HGRCPATH <> $HGRCPATH < [diff] > git = 1 > EOF diff -r 9777df929035 -r 30a0e3519f69 tests/test-eol-tag.t --- a/tests/test-eol-tag.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-eol-tag.t Tue Mar 15 16:53:46 2011 -0500 @@ -2,10 +2,7 @@ Testing tagging with the EOL extension - $ cat > $HGRCPATH < [diff] - > git = True - > + $ cat >> $HGRCPATH < [extensions] > eol = > diff -r 9777df929035 -r 30a0e3519f69 tests/test-eol-update.t --- a/tests/test-eol-update.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-eol-update.t Tue Mar 15 16:53:46 2011 -0500 @@ -1,6 +1,6 @@ Test EOL update - $ cat > $HGRCPATH <> $HGRCPATH < [diff] > git = 1 > EOF diff -r 9777df929035 -r 30a0e3519f69 tests/test-eol.t --- a/tests/test-eol.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-eol.t Tue Mar 15 16:53:46 2011 -0500 @@ -1,6 +1,6 @@ Test EOL extension - $ cat > $HGRCPATH <> $HGRCPATH < [diff] > git = True > EOF diff -r 9777df929035 -r 30a0e3519f69 tests/test-help.t --- a/tests/test-help.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-help.t Tue Mar 15 16:53:46 2011 -0500 @@ -765,6 +765,14 @@ working directory is checked out, it is equivalent to null. If an uncommitted merge is in progress, "." is the revision of the first parent. +Test templating help + + $ hg help templating | egrep '(desc|diffstat|firstline|nonempty) ' + desc String. The text of the changeset description. + diffstat String. Statistics of changes with the following format: + firstline Any text. Returns the first line of text. + nonempty Any text. Returns '(none)' if the string is empty. + Test help hooks $ cat > helphook1.py < A - $ hg -q ci -Am 0 + $ echo c0 > f1 + $ hg ci -Aqm0 - $ hg -q up -C null - $ echo 1 > A - $ hg -q ci -Am 1 + $ hg up null -q + $ echo c1 > f1 + $ hg ci -Aqm1 - $ hg -q up -C 0 + $ hg up 0 -q $ hg merge 1 -q --tool internal:local - $ echo 2 > A - $ hg -q ci -m 2 + $ echo c2 > f1 + $ hg ci -qm2 - $ hg -q up -C 1 - $ hg mv A a - $ hg -q ci -Am 3 + $ hg up 1 -q + $ hg mv f1 f2 + $ hg ci -Aqm3 - $ hg -q up -C 2 + $ hg up 2 -q $ hg merge 3 - merging A and a to a + merging f1 and f2 to f2 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) - $ cat a - 2 + $ cat f2 + c2 diff -r 9777df929035 -r 30a0e3519f69 tests/test-revset-outgoing.t --- a/tests/test-revset-outgoing.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-revset-outgoing.t Tue Mar 15 16:53:46 2011 -0500 @@ -39,7 +39,7 @@ $ cd b $ cat .hg/hgrc [paths] - default = */a#stable (glob) + default = $TESTTMP/a#stable $ echo red >> a $ hg ci -qm3 @@ -60,7 +60,7 @@ $ hg tout - comparing with */a (glob) + comparing with $TESTTMP/a searching for changes 2:1d4099801a4e: '3' stable @@ -79,11 +79,11 @@ $ cat .hg/hgrc [paths] - default = */a#stable (glob) + default = $TESTTMP/a#stable green = ../a#default $ hg tout green - comparing with */a (glob) + comparing with $TESTTMP/a searching for changes 3:f0461977a3db: '4' diff -r 9777df929035 -r 30a0e3519f69 tests/test-schemes.t --- a/tests/test-schemes.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-schemes.t Tue Mar 15 16:53:46 2011 -0500 @@ -25,7 +25,7 @@ $ hg incoming --debug parts://localhost using http://localhost:$HGPORT/ - sending between command + sending capabilities command comparing with parts://localhost sending heads command searching for changes diff -r 9777df929035 -r 30a0e3519f69 tests/test-serve --- a/tests/test-serve Mon Mar 14 21:35:31 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -#!/bin/sh - -hgserve() -{ - hg serve -a localhost -d --pid-file=hg.pid -E errors.log -v $@ \ - | sed -e "s/:$HGPORT1\\([^0-9]\\)/:HGPORT1\1/g" \ - -e "s/:$HGPORT2\\([^0-9]\\)/:HGPORT2\1/g" \ - -e 's/http:\/\/[^/]*\//http:\/\/localhost\//' - cat hg.pid >> "$DAEMON_PIDS" - echo % errors - cat errors.log - sleep 1 - if [ "$KILLQUIETLY" = "Y" ]; then - kill `cat hg.pid` 2>/dev/null - else - kill `cat hg.pid` - fi - sleep 1 -} - -hg init test -cd test - -echo '[web]' > .hg/hgrc -echo 'accesslog = access.log' >> .hg/hgrc -echo "port = $HGPORT1" >> .hg/hgrc - -echo % Without -v -hg serve -a localhost -p $HGPORT -d --pid-file=hg.pid -E errors.log -cat hg.pid >> "$DAEMON_PIDS" -if [ -f access.log ]; then - echo 'access log created - .hg/hgrc respected' -fi -echo % errors -cat errors.log - -echo % With -v -hgserve - -echo % With -v and -p HGPORT2 -hgserve -p "$HGPORT2" - -echo '% With -v and -p daytime (should fail because low port)' -KILLQUIETLY=Y -hgserve -p daytime -KILLQUIETLY=N - -echo % With --prefix foo -hgserve --prefix foo - -echo % With --prefix /foo -hgserve --prefix /foo - -echo % With --prefix foo/ -hgserve --prefix foo/ - -echo % With --prefix /foo/ -hgserve --prefix /foo/ diff -r 9777df929035 -r 30a0e3519f69 tests/test-serve.out --- a/tests/test-serve.out Mon Mar 14 21:35:31 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -% Without -v -access log created - .hg/hgrc respected -% errors -% With -v -listening at http://localhost/ (bound to 127.0.0.1:HGPORT1) -% errors -% With -v and -p HGPORT2 -listening at http://localhost/ (bound to 127.0.0.1:HGPORT2) -% errors -% With -v and -p daytime (should fail because low port) -abort: cannot start server at 'localhost:13': Permission denied -abort: child process failed to start -% errors -% With --prefix foo -listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1) -% errors -% With --prefix /foo -listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1) -% errors -% With --prefix foo/ -listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1) -% errors -% With --prefix /foo/ -listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1) -% errors diff -r 9777df929035 -r 30a0e3519f69 tests/test-serve.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-serve.t Tue Mar 15 16:53:46 2011 -0500 @@ -0,0 +1,82 @@ + + $ hgserve() + > { + > hg serve -a localhost -d --pid-file=hg.pid -E errors.log -v $@ \ + > | sed -e "s/:$HGPORT1\\([^0-9]\\)/:HGPORT1\1/g" \ + > -e "s/:$HGPORT2\\([^0-9]\\)/:HGPORT2\1/g" \ + > -e 's/http:\/\/[^/]*\//http:\/\/localhost\//' + > cat hg.pid >> "$DAEMON_PIDS" + > echo % errors + > cat errors.log + > sleep 1 + > if [ "$KILLQUIETLY" = "Y" ]; then + > kill `cat hg.pid` 2>/dev/null + > else + > kill `cat hg.pid` + > fi + > sleep 1 + > } + + $ hg init test + $ cd test + $ echo '[web]' > .hg/hgrc + $ echo 'accesslog = access.log' >> .hg/hgrc + $ echo "port = $HGPORT1" >> .hg/hgrc + +Without -v + + $ hg serve -a localhost -p $HGPORT -d --pid-file=hg.pid -E errors.log + $ cat hg.pid >> "$DAEMON_PIDS" + $ if [ -f access.log ]; then + $ echo 'access log created - .hg/hgrc respected' + access log created - .hg/hgrc respected + $ fi + +errors + + $ cat errors.log + +With -v + + $ hgserve + listening at http://localhost/ (bound to 127.0.0.1:HGPORT1) + % errors + +With -v and -p HGPORT2 + + $ hgserve -p "$HGPORT2" + listening at http://localhost/ (bound to 127.0.0.1:HGPORT2) + % errors + +With -v and -p daytime (should fail because low port) + + $ KILLQUIETLY=Y + $ hgserve -p daytime + abort: cannot start server at 'localhost:13': Permission denied + abort: child process failed to start + % errors + $ KILLQUIETLY=N + +With --prefix foo + + $ hgserve --prefix foo + listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1) + % errors + +With --prefix /foo + + $ hgserve --prefix /foo + listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1) + % errors + +With --prefix foo/ + + $ hgserve --prefix foo/ + listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1) + % errors + +With --prefix /foo/ + + $ hgserve --prefix /foo/ + listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1) + % errors diff -r 9777df929035 -r 30a0e3519f69 tests/test-ssh.t --- a/tests/test-ssh.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-ssh.t Tue Mar 15 16:53:46 2011 -0500 @@ -263,6 +263,22 @@ summary: z +clone bookmarks + + $ hg -R ../remote bookmark test + $ hg -R ../remote bookmarks + * test 2:6c0482d977a3 + $ hg clone -e "python ../dummyssh" ssh://user@dummy/remote local-bookmarks + requesting all changes + adding changesets + adding manifests + adding file changes + added 4 changesets with 5 changes to 4 files (+1 heads) + updating to branch default + 3 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg -R local-bookmarks bookmarks + test 2:6c0482d977a3 + passwords in ssh urls are not supported $ hg push ssh://user:erroneouspwd@dummy/remote @@ -289,3 +305,4 @@ Got arguments 1:user@dummy 2:hg -R remote serve --stdio Got arguments 1:user@dummy 2:hg -R remote serve --stdio Got arguments 1:user@dummy 2:hg -R remote serve --stdio + Got arguments 1:user@dummy 2:hg -R remote serve --stdio diff -r 9777df929035 -r 30a0e3519f69 tests/test-subrepo-git.t --- a/tests/test-subrepo-git.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-subrepo-git.t Tue Mar 15 16:53:46 2011 -0500 @@ -73,7 +73,7 @@ $ cd t $ hg clone . ../tc updating to branch default - cloning subrepo s + cloning subrepo s from $TESTTMP/gitroot 3 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cd ../tc $ hg debugsub @@ -96,7 +96,7 @@ $ cd ../t $ hg clone . ../ta updating to branch default - cloning subrepo s + cloning subrepo s from $TESTTMP/gitroot 3 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cd ../ta @@ -115,7 +115,7 @@ $ cd ../t $ hg clone . ../tb updating to branch default - cloning subrepo s + cloning subrepo s from $TESTTMP/gitroot 3 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cd ../tb/s @@ -155,7 +155,7 @@ added 1 changesets with 1 changes to 1 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge) $ hg merge 2>/dev/null - pulling subrepo s + pulling subrepo s from $TESTTMP/gitroot 0 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) $ cat s/f @@ -199,7 +199,7 @@ $ cd ../t $ hg clone . ../td updating to branch default - cloning subrepo s + cloning subrepo s from $TESTTMP/gitroot checking out detached HEAD in subrepo s check out a git branch if you intend to make changes 3 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -232,7 +232,7 @@ $ cd ../tb $ hg pull -q $ hg update 2>/dev/null - pulling subrepo s + pulling subrepo s from $TESTTMP/gitroot 2 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg debugsub path s @@ -262,7 +262,7 @@ $ cd ../tc $ hg pull -q $ hg archive --subrepos -r 5 ../archive 2>/dev/null - pulling subrepo s + pulling subrepo s from $TESTTMP/gitroot $ cd ../archive $ cat s/f f @@ -282,7 +282,7 @@ $ hg clone ../t inner updating to branch default - cloning subrepo s + cloning subrepo s from $TESTTMP/gitroot 3 files updated, 0 files merged, 0 files removed, 0 files unresolved $ echo inner = inner > .hgsub $ hg add .hgsub @@ -311,7 +311,7 @@ $ mkdir d $ hg clone t d/t updating to branch default - cloning subrepo s + cloning subrepo s from $TESTTMP/gitroot 3 files updated, 0 files merged, 0 files removed, 0 files unresolved Don't crash if the subrepo is missing @@ -329,7 +329,7 @@ abort: subrepo s is missing [255] $ hg update -C - cloning subrepo s + cloning subrepo s from $TESTTMP/gitroot 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg sum | grep commit commit: (clean) diff -r 9777df929035 -r 30a0e3519f69 tests/test-subrepo-paths.t --- a/tests/test-subrepo-paths.t Mon Mar 14 21:35:31 2011 +0100 +++ b/tests/test-subrepo-paths.t Tue Mar 15 16:53:46 2011 -0500 @@ -21,6 +21,15 @@ source C:\libs\foo-lib\ revision +test cumulative remapping, the $HGRCPATH file is loaded first + + $ echo '[subpaths]' >> $HGRCPATH + $ echo 'libfoo = libbar' >> $HGRCPATH + $ hg debugsub + path sub + source C:\libs\bar-lib\ + revision + test bad subpaths pattern $ cat > .hg/hgrc <test-filter-environment + > #!/bin/sh + > echo "Transplant by $HGUSER" >> $1 + > echo "Transplant from rev $HGREVISION" >> $1 + > EOF + $ chmod +x test-filter-environment + $ hg transplant -s ../t --filter ./test-filter-environment 0 + filtering * (glob) + applying 17ab29e464c6 + 17ab29e464c6 transplanted to 5190e68026a0 + + $ hg log --template '{rev} {parents} {desc}\n' + 0 r1 + Transplant by test + Transplant from rev 17ab29e464c6ca53e329470efe2a9918ac617a6f + $ cd .. + test with a win32ext like setup (differing EOLs)