Mercurial > hg
changeset 29459:fd93b15b5c30
merge with stable
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Fri, 01 Jul 2016 16:02:56 -0500 |
parents | 59058549a611 (diff) ddbff1435e7b (current diff) |
children | 7d88fde2309f |
files | mercurial/help/config.txt mercurial/sslutil.py |
diffstat | 250 files changed, 7475 insertions(+), 3643 deletions(-) [+] |
line wrap: on
line diff
--- a/contrib/bash_completion Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/bash_completion Fri Jul 01 16:02:56 2016 -0500 @@ -184,7 +184,7 @@ return fi - opts=$(_hg_cmd debugcomplete --options "$cmd") + opts=$(HGPLAINEXCEPT=alias _hg_cmd debugcomplete --options "$cmd") COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$opts' -- "$cur")) _hg_fix_wordlist
--- a/contrib/bdiff-torture.py Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/bdiff-torture.py Fri Jul 01 16:02:56 2016 -0500 @@ -1,7 +1,9 @@ # Randomized torture test generation for bdiff from __future__ import absolute_import, print_function -import random, sys +import random +import sys + from mercurial import ( bdiff, mpatch,
--- a/contrib/check-code.py Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/check-code.py Fri Jul 01 16:02:56 2016 -0500 @@ -26,6 +26,15 @@ import os import re import sys +if sys.version_info[0] < 3: + opentext = open +else: + def opentext(f): + return open(f, encoding='ascii') +try: + xrange +except NameError: + xrange = range try: import re2 except ImportError: @@ -41,26 +50,26 @@ pass return re.compile(pat) +# check "rules depending on implementation of repquote()" in each +# patterns (especially pypats), before changing around repquote() +_repquotefixedmap = {' ': ' ', '\n': '\n', '.': 'p', ':': 'q', + '%': '%', '\\': 'b', '*': 'A', '+': 'P', '-': 'M'} +def _repquoteencodechr(i): + if i > 255: + return 'u' + c = chr(i) + if c in _repquotefixedmap: + return _repquotefixedmap[c] + if c.isalpha(): + return 'x' + if c.isdigit(): + return 'n' + return 'o' +_repquotett = ''.join(_repquoteencodechr(i) for i in xrange(256)) + def repquote(m): - fromc = '.:' - tochr = 'pq' - def encodechr(i): - if i > 255: - return 'u' - c = chr(i) - if c in ' \n': - return c - if c.isalpha(): - return 'x' - if c.isdigit(): - return 'n' - try: - return tochr[fromc.find(c)] - except (ValueError, IndexError): - return 'o' t = m.group('text') - tt = ''.join(encodechr(i) for i in xrange(256)) - t = t.translate(tt) + t = t.translate(_repquotett) return m.group('quote') + t + m.group('quote') def reppython(m): @@ -103,7 +112,7 @@ (r'tail -n', "don't use the '-n' option to tail, just use '-<num>'"), (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"), (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"), - (r'printf.*[^\\]\\([1-9]|0\d)', "don't use 'printf \NNN', use Python"), + (r'printf.*[^\\]\\([1-9]|0\d)', r"don't use 'printf \NNN', use Python"), (r'printf.*[^\\]\\x', "don't use printf \\x, use Python"), (r'\$\(.*\)', "don't use $(expr), use `expr`"), (r'rm -rf \*', "don't use naked rm -rf, target a directory"), @@ -114,7 +123,7 @@ (r'export .*=', "don't export and assign at once"), (r'^source\b', "don't use 'source', use '.'"), (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"), - (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"), + (r'\bls +[^|\n-]+ +-', "options to 'ls' must come before filenames"), (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"), (r'^stop\(\)', "don't use 'stop' as a shell function name"), (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"), @@ -133,6 +142,7 @@ (r'\|&', "don't use |&, use 2>&1"), (r'\w = +\w', "only one space after = allowed"), (r'\bsed\b.*[^\\]\\n', "don't use 'sed ... \\n', use a \\ and a newline"), + (r'env.*-u', "don't use 'env -u VAR', use 'unset VAR'") ], # warnings [ @@ -238,7 +248,6 @@ (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"), (r'\w\s=\s\s+\w', "gratuitous whitespace after ="), (r'.{81}', "line too long"), - (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'), (r'[^\n]\Z', "no trailing newline"), (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"), # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', @@ -305,8 +314,6 @@ (r'^\s*except\s([^\(,]+|\([^\)]+\))\s*,', 'legacy exception syntax; use "as" instead of ","'), (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"), - (r'ui\.(status|progress|write|note|warn)\([\'\"]x', - "missing _() in ui message (use () to hide false-positives)"), (r'release\(.*wlock, .*lock\)', "wrong lock release order"), (r'\b__bool__\b', "__bool__ should be __nonzero__ in Python 2"), (r'os\.path\.join\(.*, *(""|\'\')\)', @@ -318,9 +325,36 @@ (r'^import Queue', "don't use Queue, use util.queue + util.empty"), (r'^import cStringIO', "don't use cStringIO.StringIO, use util.stringio"), (r'^import urllib', "don't use urllib, use util.urlreq/util.urlerr"), + (r'^import SocketServer', "don't use SockerServer, use util.socketserver"), + (r'^import urlparse', "don't use urlparse, use util.urlparse"), + (r'^import xmlrpclib', "don't use xmlrpclib, use util.xmlrpclib"), + (r'^import cPickle', "don't use cPickle, use util.pickle"), + (r'^import pickle', "don't use pickle, use util.pickle"), + (r'^import httplib', "don't use httplib, use util.httplib"), + (r'\.next\(\)', "don't use .next(), use next(...)"), + + # rules depending on implementation of repquote() + (r' x+[xpqo%APM][\'"]\n\s+[\'"]x', + 'string join across lines with no space'), + (r'''(?x)ui\.(status|progress|write|note|warn)\( + [ \t\n#]* + (?# any strings/comments might precede a string, which + # contains translatable message) + ((['"]|\'\'\'|""")[ \npq%bAPMxno]*(['"]|\'\'\'|""")[ \t\n#]+)* + (?# sequence consisting of below might precede translatable message + # - formatting string: "% 10s", "%05d", "% -3.2f", "%*s", "%%" ... + # - escaped character: "\\", "\n", "\0" ... + # - character other than '%', 'b' as '\', and 'x' as alphabet) + (['"]|\'\'\'|""") + ((%([ n]?[PM]?([np]+|A))?x)|%%|b[bnx]|[ \nnpqAPMo])*x + (?# this regexp can't use [^...] style, + # because _preparepats forcibly adds "\n" into [^...], + # even though this regexp wants match it against "\n")''', + "missing _() in ui message (use () to hide false-positives)"), ], # warnings [ + # rules depending on implementation of repquote() (r'(^| )pp +xxxxqq[ \n][^\n]', "add two newlines after '.. note::'"), ] ] @@ -365,9 +399,13 @@ (r'^\s*#import\b', "use only #include in standard C code"), (r'strcpy\(', "don't use strcpy, use strlcpy or memcpy"), (r'strcat\(', "don't use strcat"), + + # rules depending on implementation of repquote() ], # warnings - [] + [ + # rules depending on implementation of repquote() + ] ] cfilters = [ @@ -486,12 +524,15 @@ result = True try: - fp = open(f) + with opentext(f) as fp: + try: + pre = post = fp.read() + except UnicodeDecodeError as e: + print("%s while reading %s" % (e, f)) + return result except IOError as e: print("Skipping %s, %s" % (f, str(e).split(':', 1)[0])) return result - pre = post = fp.read() - fp.close() for name, match, magic, filters, pats in checks: if debug:
--- a/contrib/check-commit Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/check-commit Fri Jul 01 16:02:56 2016 -0500 @@ -15,7 +15,11 @@ # # See also: https://mercurial-scm.org/wiki/ContributingChanges -import re, sys, os +from __future__ import absolute_import, print_function + +import os +import re +import sys commitheader = r"^(?:# [^\n]*\n)*" afterheader = commitheader + r"(?!#)" @@ -69,9 +73,9 @@ break if not printed: printed = True - print "node: %s" % node - print "%d: %s" % (n, msg) - print " %s" % nonempty(l, last)[:-1] + print("node: %s" % node) + print("%d: %s" % (n, msg)) + print(" %s" % nonempty(l, last)[:-1]) if "BYPASS" not in os.environ: exitcode = 1 del hits[0]
--- a/contrib/chg/README Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/chg/README Fri Jul 01 16:02:56 2016 -0500 @@ -28,3 +28,5 @@ * CHGDEBUG enables debug messages. * CHGSOCKNAME specifies the socket path of the background cmdserver. + * CHGTIMEOUT specifies how many seconds chg will wait before giving up + connecting to a cmdserver. If it is 0, chg will wait forever. Default: 60
--- a/contrib/chg/chg.c Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/chg/chg.c Fri Jul 01 16:02:56 2016 -0500 @@ -249,7 +249,13 @@ int pst = 0; debugmsg("try connect to %s repeatedly", opts->sockname); - for (unsigned int i = 0; i < 10 * 100; i++) { + + unsigned int timeoutsec = 60; /* default: 60 seconds */ + const char *timeoutenv = getenv("CHGTIMEOUT"); + if (timeoutenv) + sscanf(timeoutenv, "%u", &timeoutsec); + + for (unsigned int i = 0; !timeoutsec || i < timeoutsec * 100; i++) { hgclient_t *hgc = hgc_open(opts->sockname); if (hgc) return hgc; @@ -332,6 +338,7 @@ } } +static pid_t pagerpid = 0; static pid_t peerpid = 0; static void forwardsignal(int sig) @@ -374,6 +381,17 @@ abortmsgerrno("failed to handle stop signal"); } +static void handlechildsignal(int sig UNUSED_) +{ + if (peerpid == 0 || pagerpid == 0) + return; + /* if pager exits, notify the server with SIGPIPE immediately. + * otherwise the server won't get SIGPIPE if it does not write + * anything. (issue5278) */ + if (waitpid(pagerpid, NULL, WNOHANG) == pagerpid) + kill(peerpid, SIGPIPE); +} + static void setupsignalhandler(pid_t pid) { if (pid <= 0) @@ -410,6 +428,11 @@ sa.sa_flags = SA_RESTART; if (sigaction(SIGTSTP, &sa, NULL) < 0) goto error; + /* get notified when pager exits */ + sa.sa_handler = handlechildsignal; + sa.sa_flags = SA_RESTART; + if (sigaction(SIGCHLD, &sa, NULL) < 0) + goto error; return; @@ -417,21 +440,56 @@ abortmsgerrno("failed to set up signal handlers"); } -/* This implementation is based on hgext/pager.py (pre 369741ef7253) */ -static void setuppager(hgclient_t *hgc, const char *const args[], +static void restoresignalhandler() +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sa.sa_flags = SA_RESTART; + if (sigemptyset(&sa.sa_mask) < 0) + goto error; + + if (sigaction(SIGHUP, &sa, NULL) < 0) + goto error; + if (sigaction(SIGTERM, &sa, NULL) < 0) + goto error; + if (sigaction(SIGWINCH, &sa, NULL) < 0) + goto error; + if (sigaction(SIGCONT, &sa, NULL) < 0) + goto error; + if (sigaction(SIGTSTP, &sa, NULL) < 0) + goto error; + if (sigaction(SIGCHLD, &sa, NULL) < 0) + goto error; + + /* ignore Ctrl+C while shutting down to make pager exits cleanly */ + sa.sa_handler = SIG_IGN; + if (sigaction(SIGINT, &sa, NULL) < 0) + goto error; + + peerpid = 0; + return; + +error: + abortmsgerrno("failed to restore signal handlers"); +} + +/* This implementation is based on hgext/pager.py (post 369741ef7253) + * Return 0 if pager is not started, or pid of the pager */ +static pid_t setuppager(hgclient_t *hgc, const char *const args[], size_t argsize) { const char *pagercmd = hgc_getpager(hgc, args, argsize); if (!pagercmd) - return; + return 0; int pipefds[2]; if (pipe(pipefds) < 0) - return; + return 0; pid_t pid = fork(); if (pid < 0) goto error; - if (pid == 0) { + if (pid > 0) { close(pipefds[0]); if (dup2(pipefds[1], fileno(stdout)) < 0) goto error; @@ -441,7 +499,7 @@ } close(pipefds[1]); hgc_attachio(hgc); /* reattach to pager */ - return; + return pid; } else { dup2(pipefds[0], fileno(stdin)); close(pipefds[0]); @@ -451,13 +509,27 @@ if (r < 0) { abortmsgerrno("cannot start pager '%s'", pagercmd); } - return; + return 0; } error: close(pipefds[0]); close(pipefds[1]); abortmsgerrno("failed to prepare pager"); + return 0; +} + +static void waitpager(pid_t pid) +{ + /* close output streams to notify the pager its input ends */ + fclose(stdout); + fclose(stderr); + while (1) { + pid_t ret = waitpid(pid, NULL, 0); + if (ret == -1 && errno == EINTR) + continue; + break; + } } /* Run instructions sent from the server like unlink and set redirect path @@ -585,9 +657,13 @@ } setupsignalhandler(hgc_peerpid(hgc)); - setuppager(hgc, argv + 1, argc - 1); + pagerpid = setuppager(hgc, argv + 1, argc - 1); int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1); + restoresignalhandler(); hgc_close(hgc); freecmdserveropts(&opts); + if (pagerpid) + waitpager(pagerpid); + return exitcode; }
--- a/contrib/chg/util.h Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/chg/util.h Fri Jul 01 16:02:56 2016 -0500 @@ -12,8 +12,10 @@ #ifdef __GNUC__ #define PRINTF_FORMAT_ __attribute__((format(printf, 1, 2))) +#define UNUSED_ __attribute__((unused)) #else #define PRINTF_FORMAT_ +#define UNUSED_ #endif void abortmsg(const char *fmt, ...) PRINTF_FORMAT_;
--- a/contrib/debugshell.py Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/debugshell.py Fri Jul 01 16:02:56 2016 -0500 @@ -52,7 +52,7 @@ with demandimport.deactivated(): __import__(pdbmap[debugger]) except ImportError: - ui.warn("%s debugger specified but %s module was not found\n" + ui.warn(("%s debugger specified but %s module was not found\n") % (debugger, pdbmap[debugger])) debugger = 'pdb'
--- a/contrib/dirstatenonnormalcheck.py Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/dirstatenonnormalcheck.py Fri Jul 01 16:02:56 2016 -0500 @@ -25,10 +25,10 @@ """Compute nonnormalset from dmap, check that it matches _nonnormalset""" nonnormalcomputedmap = nonnormalentries(dmap) if _nonnormalset != nonnormalcomputedmap: - ui.develwarn("%s call to %s\n" % (label, orig)) - ui.develwarn("inconsistency in nonnormalset\n") - ui.develwarn("[nonnormalset] %s\n" % _nonnormalset) - ui.develwarn("[map] %s\n" % nonnormalcomputedmap) + ui.develwarn("%s call to %s\n" % (label, orig), config='dirstate') + ui.develwarn("inconsistency in nonnormalset\n", config='dirstate') + ui.develwarn("[nonnormalset] %s\n" % _nonnormalset, config='dirstate') + ui.develwarn("[map] %s\n" % nonnormalcomputedmap, config='dirstate') def _checkdirstate(orig, self, arg): """Check nonnormal set consistency before and after the call to orig"""
--- a/contrib/dumprevlog Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/dumprevlog Fri Jul 01 16:02:56 2016 -0500 @@ -2,8 +2,14 @@ # Dump revlogs as raw data stream # $ find .hg/store/ -name "*.i" | xargs dumprevlog > repo.dump +from __future__ import absolute_import, print_function + import sys -from mercurial import revlog, node, util +from mercurial import ( + node, + revlog, + util, +) for fp in (sys.stdin, sys.stdout, sys.stderr): util.setbinary(fp) @@ -11,15 +17,15 @@ for f in sys.argv[1:]: binopen = lambda fn: open(fn, 'rb') r = revlog.revlog(binopen, f) - print "file:", f + print("file:", f) for i in r: n = r.node(i) p = r.parents(n) d = r.revision(n) - print "node:", node.hex(n) - print "linkrev:", r.linkrev(i) - print "parents:", node.hex(p[0]), node.hex(p[1]) - print "length:", len(d) - print "-start-" - print d - print "-end-" + print("node:", node.hex(n)) + print("linkrev:", r.linkrev(i)) + print("parents:", node.hex(p[0]), node.hex(p[1])) + print("length:", len(d)) + print("-start-") + print(d) + print("-end-")
--- a/contrib/import-checker.py Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/import-checker.py Fri Jul 01 16:02:56 2016 -0500 @@ -11,8 +11,9 @@ # Import a minimal set of stdlib modules needed for list_stdlib_modules() # to work when run from a virtualenv. The modules were chosen empirically # so that the return value matches the return value without virtualenv. -import BaseHTTPServer -import zlib +if True: # disable lexical sorting checks + import BaseHTTPServer + import zlib # Whitelist of modules that symbols can be directly imported from. allowsymbolimports = ( @@ -126,22 +127,32 @@ False >>> fromlocal(None, 1) ('foo', 'foo.__init__', True) + >>> fromlocal('foo1', 1) + ('foo.foo1', 'foo.foo1', False) >>> fromlocal2 = fromlocalfunc('foo.xxx.yyy', localmods) >>> fromlocal2(None, 2) ('foo', 'foo.__init__', True) + >>> fromlocal2('bar2', 1) + False + >>> fromlocal2('bar', 2) + ('foo.bar', 'foo.bar.__init__', True) """ prefix = '.'.join(modulename.split('.')[:-1]) if prefix: prefix += '.' def fromlocal(name, level=0): - # name is None when relative imports are used. - if name is None: + # name is false value when relative imports are used. + if not name: # If relative imports are used, level must not be absolute. assert level > 0 candidates = ['.'.join(modulename.split('.')[:-level])] else: - # Check relative name first. - candidates = [prefix + name, name] + if not level: + # Check relative name first. + candidates = [prefix + name, name] + else: + candidates = ['.'.join(modulename.split('.')[:-level]) + + '.' + name] for n in candidates: if n in localmods: @@ -175,6 +186,9 @@ >>> 'cStringIO' in mods True + + >>> 'cffi' in mods + True """ for m in sys.builtin_module_names: yield m @@ -187,6 +201,8 @@ yield m for m in 'cPickle', 'datetime': # in Python (not C) on PyPy yield m + for m in ['cffi']: + yield m stdlib_prefixes = set([sys.prefix, sys.exec_prefix]) # We need to supplement the list of prefixes for the search to work # when run from within a virtualenv. @@ -360,7 +376,7 @@ * Symbols can only be imported from specific modules (see `allowsymbolimports`). For other modules, first import the module then assign the symbol to a module-level variable. In addition, these imports - must be performed before other relative imports. This rule only + must be performed before other local imports. This rule only applies to import statements outside of any blocks. * Relative imports from the standard library are not allowed. * Certain modules must be aliased to alternate names to avoid aliasing @@ -371,8 +387,8 @@ # Whether a local/non-stdlib import has been performed. seenlocal = None - # Whether a relative, non-symbol import has been seen. - seennonsymbolrelative = False + # Whether a local/non-stdlib, non-symbol import has been seen. + seennonsymbollocal = False # The last name to be imported (for sorting). lastname = None # Relative import levels encountered so far. @@ -446,26 +462,26 @@ # Direct symbol import is only allowed from certain modules and # must occur before non-symbol imports. + found = fromlocal(node.module, node.level) + if found and found[2]: # node.module is a package + prefix = found[0] + '.' + symbols = [n.name for n in node.names + if not fromlocal(prefix + n.name)] + else: + symbols = [n.name for n in node.names] if node.module and node.col_offset == root_col_offset: - found = fromlocal(node.module, node.level) - if found and found[2]: # node.module is a package - prefix = found[0] + '.' - symbols = [n.name for n in node.names - if not fromlocal(prefix + n.name)] - else: - symbols = [n.name for n in node.names] - if symbols and fullname not in allowsymbolimports: yield msg('direct symbol import %s from %s', ', '.join(symbols), fullname) - if symbols and seennonsymbolrelative: + if symbols and seennonsymbollocal: yield msg('symbol import follows non-symbol import: %s', fullname) + if not symbols and fullname not in stdlib_modules: + seennonsymbollocal = True if not node.module: assert node.level - seennonsymbolrelative = True # Only allow 1 group per level. if (node.level in seenlevels @@ -652,7 +668,7 @@ the input file. """ py = False - if f.endswith('.py'): + if not f.endswith('.t'): with open(f) as src: yield src.read(), modname, f, 0 py = True
--- a/contrib/revsetbenchmarks.py Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/revsetbenchmarks.py Fri Jul 01 16:02:56 2016 -0500 @@ -10,41 +10,32 @@ from __future__ import absolute_import, print_function import math +import optparse # cannot use argparse, python 2.7 only import os import re +import subprocess import sys -from subprocess import ( - CalledProcessError, - check_call, - PIPE, - Popen, - STDOUT, -) -# cannot use argparse, python 2.7 only -from optparse import ( - OptionParser, -) DEFAULTVARIANTS = ['plain', 'min', 'max', 'first', 'last', 'reverse', 'reverse+first', 'reverse+last', 'sort', 'sort+first', 'sort+last'] def check_output(*args, **kwargs): - kwargs.setdefault('stderr', PIPE) - kwargs.setdefault('stdout', PIPE) - proc = Popen(*args, **kwargs) + kwargs.setdefault('stderr', subprocess.PIPE) + kwargs.setdefault('stdout', subprocess.PIPE) + proc = subprocess.Popen(*args, **kwargs) output, error = proc.communicate() if proc.returncode != 0: - raise CalledProcessError(proc.returncode, ' '.join(args[0])) + raise subprocess.CalledProcessError(proc.returncode, ' '.join(args[0])) return output def update(rev): """update the repo to a revision""" try: - check_call(['hg', 'update', '--quiet', '--check', str(rev)]) + subprocess.check_call(['hg', 'update', '--quiet', '--check', str(rev)]) check_output(['make', 'local'], stderr=None) # suppress output except for error/warning - except CalledProcessError as exc: + except subprocess.CalledProcessError as exc: print('update to revision %s failed, aborting'%rev, file=sys.stderr) sys.exit(exc.returncode) @@ -60,7 +51,7 @@ fullcmd += ['--config', 'extensions.perf=' + os.path.join(contribdir, 'perf.py')] fullcmd += cmd - return check_output(fullcmd, stderr=STDOUT) + return check_output(fullcmd, stderr=subprocess.STDOUT) def perf(revset, target=None, contexts=False): """run benchmark for this very revset""" @@ -70,7 +61,7 @@ args.append('--contexts') output = hg(args, repo=target) return parseoutput(output) - except CalledProcessError as exc: + except subprocess.CalledProcessError as exc: print('abort: cannot run revset benchmark: %s'%exc.cmd, file=sys.stderr) if getattr(exc, 'output', None) is None: # no output before 2.7 print('(no output)', file=sys.stderr) @@ -103,9 +94,9 @@ """print data about a revision""" sys.stdout.write("Revision ") sys.stdout.flush() - check_call(['hg', 'log', '--rev', str(rev), '--template', - '{if(tags, " ({tags})")} ' - '{rev}:{node|short}: {desc|firstline}\n']) + subprocess.check_call(['hg', 'log', '--rev', str(rev), '--template', + '{if(tags, " ({tags})")} ' + '{rev}:{node|short}: {desc|firstline}\n']) def idxwidth(nbidx): """return the max width of number used for index @@ -215,7 +206,7 @@ """get the list of rev matched by a revset""" try: out = check_output(['hg', 'log', '--template={rev}\n', '--rev', spec]) - except CalledProcessError as exc: + except subprocess.CalledProcessError as exc: print("abort, can't get revision from %s"%spec, file=sys.stderr) sys.exit(exc.returncode) return [r for r in out.split() if r] @@ -234,8 +225,8 @@ point regressions. Revsets to run are specified in a file (or from stdin), one revsets per line. Line starting with '#' will be ignored, allowing insertion of comments.""" -parser = OptionParser(usage="usage: %prog [options] <revs>", - description=helptext) +parser = optparse.OptionParser(usage="usage: %prog [options] <revs>", + description=helptext) parser.add_option("-f", "--file", help="read revset from FILE (stdin if omitted)", metavar="FILE")
--- a/contrib/synthrepo.py Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/synthrepo.py Fri Jul 01 16:02:56 2016 -0500 @@ -45,6 +45,13 @@ import random import sys import time + +from mercurial.i18n import _ +from mercurial.node import ( + nullid, + nullrev, + short, +) from mercurial import ( cmdutil, context, @@ -54,12 +61,6 @@ scmutil, util, ) -from mercurial.i18n import _ -from mercurial.node import ( - nullid, - nullrev, - short, -) # Note for extension authors: ONLY specify testedwith = 'internal' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should @@ -506,7 +507,7 @@ head = rename(head) else: head = '' - renamed = os.path.join(head, wordgen.next()) + renamed = os.path.join(head, next(wordgen)) replacements[dirpath] = renamed return renamed result = []
--- a/contrib/undumprevlog Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/undumprevlog Fri Jul 01 16:02:56 2016 -0500 @@ -3,8 +3,16 @@ # $ hg init # $ undumprevlog < repo.dump +from __future__ import absolute_import + import sys -from mercurial import revlog, node, scmutil, util, transaction +from mercurial import ( + node, + revlog, + scmutil, + transaction, + util, +) for fp in (sys.stdin, sys.stdout, sys.stderr): util.setbinary(fp)
--- a/contrib/win32/hgwebdir_wsgi.py Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/win32/hgwebdir_wsgi.py Fri Jul 01 16:02:56 2016 -0500 @@ -79,6 +79,8 @@ # - Restart the web server and see if things are running. # +from __future__ import absolute_import + # Configuration file location hgweb_config = r'c:\your\directory\wsgi.config' @@ -87,7 +89,6 @@ path_prefix = 1 # This many path elements are prefixes (depends on the # virtual path of the IIS application). -from __future__ import absolute_import import sys # Adjust python path if this is not a system-wide install
--- a/contrib/win32/mercurial.ini Fri Jul 01 15:12:33 2016 -0500 +++ b/contrib/win32/mercurial.ini Fri Jul 01 16:02:56 2016 -0500 @@ -46,7 +46,6 @@ ;extdiff = ;fetch = ;gpg = -;hgcia = ;hgk = ;highlight = ;histedit =
--- a/doc/docchecker Fri Jul 01 15:12:33 2016 -0500 +++ b/doc/docchecker Fri Jul 01 16:02:56 2016 -0500 @@ -6,8 +6,11 @@ # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. + +from __future__ import absolute_import, print_function + +import re import sys -import re leadingline = re.compile(r'(^\s*)(\S.*)$')
--- a/doc/gendoc.py Fri Jul 01 15:12:33 2016 -0500 +++ b/doc/gendoc.py Fri Jul 01 16:02:56 2016 -0500 @@ -117,11 +117,11 @@ ui.write(_("This section contains help for extensions that are " "distributed together with Mercurial. Help for other " "extensions is available in the help system.")) - ui.write("\n\n" + ui.write(("\n\n" ".. contents::\n" " :class: htmlonly\n" " :local:\n" - " :depth: 1\n\n") + " :depth: 1\n\n")) for extensionname in sorted(allextensionnames()): mod = extensions.load(ui, extensionname, None)
--- a/doc/hgmanpage.py Fri Jul 01 15:12:33 2016 -0500 +++ b/doc/hgmanpage.py Fri Jul 01 16:02:56 2016 -0500 @@ -415,7 +415,7 @@ else: self._docinfo[name] = node.astext() self._docinfo_keys.append(name) - raise nodes.SkipNode + raise nodes.SkipNode() def depart_docinfo_item(self, node): pass @@ -469,7 +469,7 @@ def visit_citation_reference(self, node): self.body.append('['+node.astext()+']') - raise nodes.SkipNode + raise nodes.SkipNode() def visit_classifier(self, node): pass @@ -489,7 +489,7 @@ def visit_comment(self, node, sub=re.compile('-(?=-)').sub): self.body.append(self.comment(node.astext())) - raise nodes.SkipNode + raise nodes.SkipNode() def visit_contact(self, node): self.visit_docinfo_item(node, 'contact') @@ -643,7 +643,7 @@ name_normalized = self._field_name.lower().replace(" ","_") self._docinfo_names[name_normalized] = self._field_name self.visit_docinfo_item(node, name_normalized) - raise nodes.SkipNode + raise nodes.SkipNode() def depart_field_body(self, node): pass @@ -657,7 +657,7 @@ def visit_field_name(self, node): if self._in_docinfo: self._field_name = node.astext() - raise nodes.SkipNode + raise nodes.SkipNode() else: self.body.append(self.defs['field_name'][0]) @@ -693,7 +693,7 @@ def visit_footnote_reference(self, node): self.body.append('['+self.deunicode(node.astext())+']') - raise nodes.SkipNode + raise nodes.SkipNode() def depart_footnote_reference(self, node): pass @@ -705,7 +705,7 @@ pass def visit_header(self, node): - raise NotImplementedError, node.astext() + raise NotImplementedError(node.astext()) def depart_header(self, node): pass @@ -742,7 +742,7 @@ if 'uri' in node.attributes: text.append(node.attributes['uri']) self.body.append('[image: %s]\n' % ('/'.join(text))) - raise nodes.SkipNode + raise nodes.SkipNode() def visit_important(self, node): self.visit_admonition(node, 'important') @@ -753,7 +753,7 @@ # footnote and citation if (isinstance(node.parent, nodes.footnote) or isinstance(node.parent, nodes.citation)): - raise nodes.SkipNode + raise nodes.SkipNode() self.document.reporter.warning('"unsupported "label"', base_node=node) self.body.append('[') @@ -793,7 +793,7 @@ def visit_list_item(self, node): # man 7 man argues to use ".IP" instead of ".TP" self.body.append('.IP %s %d\n' % ( - self._list_char[-1].next(), + next(self._list_char[-1]), self._list_char[-1].get_width(),)) def depart_list_item(self, node): @@ -814,7 +814,7 @@ self.body.append(self.defs['literal_block'][1]) def visit_meta(self, node): - raise NotImplementedError, node.astext() + raise NotImplementedError(node.astext()) def depart_meta(self, node): pass @@ -924,7 +924,7 @@ if node.get('format') == 'manpage': self.body.append(node.astext() + "\n") # Keep non-manpage raw text out of output: - raise nodes.SkipNode + raise nodes.SkipNode() def visit_reference(self, node): """E.g. link or email address.""" @@ -963,7 +963,7 @@ def visit_substitution_definition(self, node): """Internal only.""" - raise nodes.SkipNode + raise nodes.SkipNode() def visit_substitution_reference(self, node): self.document.reporter.warning('"substitution_reference" not supported', @@ -1009,7 +1009,7 @@ def visit_target(self, node): # targets are in-document hyper targets, without any use for man-pages. - raise nodes.SkipNode + raise nodes.SkipNode() def visit_tbody(self, node): pass @@ -1053,7 +1053,7 @@ self._docinfo['title'] = node.astext() # document title for .TH self._docinfo['title_upper'] = node.astext().upper() - raise nodes.SkipNode + raise nodes.SkipNode() elif self.section_level == 1: self.body.append('.SH ') for n in node.traverse(nodes.Text):
--- a/hg Fri Jul 01 15:12:33 2016 -0500 +++ b/hg Fri Jul 01 16:02:56 2016 -0500 @@ -11,9 +11,11 @@ import sys if os.environ.get('HGUNICODEPEDANTRY', False): - reload(sys) - sys.setdefaultencoding("undefined") - + try: + reload(sys) + sys.setdefaultencoding("undefined") + except NameError: + pass libdir = '@LIBDIR@' @@ -26,9 +28,9 @@ # enable importing on demand to reduce startup time try: - from mercurial import demandimport; demandimport.enable() + if sys.version_info[0] < 3: + from mercurial import demandimport; demandimport.enable() except ImportError: - import sys sys.stderr.write("abort: couldn't find mercurial libraries in [%s]\n" % ' '.join(sys.path)) sys.stderr.write("(check your install and PYTHONPATH)\n")
--- a/hgext/automv.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/automv.py Fri Jul 01 16:02:56 2016 -0500 @@ -26,6 +26,7 @@ from __future__ import absolute_import +from mercurial.i18n import _ from mercurial import ( commands, copies, @@ -34,7 +35,6 @@ scmutil, similar ) -from mercurial.i18n import _ def extsetup(ui): entry = extensions.wrapcommand(
--- a/hgext/bugzilla.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/bugzilla.py Fri Jul 01 16:02:56 2016 -0500 @@ -281,8 +281,6 @@ import re import time -import urlparse -import xmlrpclib from mercurial.i18n import _ from mercurial.node import short @@ -293,6 +291,9 @@ util, ) +urlparse = util.urlparse +xmlrpclib = util.xmlrpclib + # Note for extension authors: ONLY specify testedwith = 'internal' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should # be specifying the version(s) of Mercurial they are tested with, or
--- a/hgext/chgserver.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/chgserver.py Fri Jul 01 16:02:56 2016 -0500 @@ -40,13 +40,14 @@ from __future__ import absolute_import -import SocketServer import errno import gc +import hashlib import inspect import os import random import re +import signal import struct import sys import threading @@ -66,6 +67,8 @@ util, ) +socketserver = util.socketserver + # Note for extension authors: ONLY specify testedwith = 'internal' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should # be specifying the version(s) of Mercurial they are tested with, or @@ -76,10 +79,11 @@ def _hashlist(items): """return sha1 hexdigest for a list""" - return util.sha1(str(items)).hexdigest() + return hashlib.sha1(str(items)).hexdigest() # sensitive config sections affecting confighash _configsections = [ + 'alias', # affects global state commands.table 'extdiff', # uisetup will register new commands 'extensions', ] @@ -213,18 +217,6 @@ ui.setconfig('ui', 'interactive', False, 'pager') return p -_envvarre = re.compile(r'\$[a-zA-Z_]+') - -def _clearenvaliases(cmdtable): - """Remove stale command aliases referencing env vars; variable expansion - is done at dispatch.addaliases()""" - for name, tab in cmdtable.items(): - cmddef = tab[0] - if (isinstance(cmddef, dispatch.cmdalias) and - not cmddef.definition.startswith('!') and # shell alias - _envvarre.search(cmddef.definition)): - del cmdtable[name] - def _newchgui(srcui, csystem): class chgui(srcui.__class__): def __init__(self, src=None): @@ -508,6 +500,11 @@ pagercmd = _setuppagercmd(self.ui, options, cmd) if pagercmd: + # Python's SIGPIPE is SIG_IGN by default. change to SIG_DFL so + # we can exit if the pipe to the pager is closed + if util.safehasattr(signal, 'SIGPIPE') and \ + signal.getsignal(signal.SIGPIPE) == signal.SIG_IGN: + signal.signal(signal.SIGPIPE, signal.SIG_DFL) self.cresult.write(pagercmd) else: self.cresult.write('\0') @@ -525,7 +522,6 @@ _log('setenv: %r\n' % sorted(newenv.keys())) os.environ.clear() os.environ.update(newenv) - _clearenvaliases(commands.table) capabilities = commandserver.server.capabilities.copy() capabilities.update({'attachio': attachio, @@ -535,7 +531,7 @@ 'setumask': setumask}) # copied from mercurial/commandserver.py -class _requesthandler(SocketServer.StreamRequestHandler): +class _requesthandler(socketserver.StreamRequestHandler): def handle(self): # use a different process group from the master process, making this # process pass kernel "is_current_pgrp_orphaned" check so signals like @@ -608,7 +604,7 @@ def process_request(self, request, address): self.lastactive = time.time() - return SocketServer.ForkingMixIn.process_request( + return socketserver.ForkingMixIn.process_request( self, request, address) def server_bind(self): @@ -661,8 +657,8 @@ self.repo = None self._inithashstate() self._checkextensions() - class cls(AutoExitMixIn, SocketServer.ForkingMixIn, - SocketServer.UnixStreamServer): + class cls(AutoExitMixIn, socketserver.ForkingMixIn, + socketserver.UnixStreamServer): ui = self.ui repo = self.repo hashstate = self.hashstate
--- a/hgext/color.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/color.py Fri Jul 01 16:02:56 2016 -0500 @@ -156,6 +156,8 @@ from __future__ import absolute_import import os + +from mercurial.i18n import _ from mercurial import ( cmdutil, commands, @@ -165,7 +167,6 @@ ui as uimod, util, ) -from mercurial.i18n import _ cmdtable = {} command = cmdutil.command(cmdtable)
--- a/hgext/convert/__init__.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/convert/__init__.py Fri Jul 01 16:02:56 2016 -0500 @@ -9,11 +9,11 @@ from __future__ import absolute_import +from mercurial.i18n import _ from mercurial import ( cmdutil, registrar, ) -from mercurial.i18n import _ from . import ( convcmd,
--- a/hgext/convert/bzr.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/convert/bzr.py Fri Jul 01 16:02:56 2016 -0500 @@ -10,11 +10,12 @@ from __future__ import absolute_import import os + +from mercurial.i18n import _ from mercurial import ( demandimport, error ) -from mercurial.i18n import _ from . import common # these do not work with demandimport, blacklist
--- a/hgext/convert/common.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/convert/common.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,20 +7,20 @@ from __future__ import absolute_import import base64 -import cPickle as pickle import datetime import errno import os import re import subprocess +from mercurial.i18n import _ from mercurial import ( error, phases, util, ) -from mercurial.i18n import _ +pickle = util.pickle propertycache = util.propertycache def encodeargs(args):
--- a/hgext/convert/convcmd.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/convert/convcmd.py Fri Jul 01 16:02:56 2016 -0500 @@ -10,13 +10,13 @@ import shlex import shutil +from mercurial.i18n import _ from mercurial import ( encoding, error, hg, util, ) -from mercurial.i18n import _ from . import ( bzr,
--- a/hgext/convert/cvs.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/convert/cvs.py Fri Jul 01 16:02:56 2016 -0500 @@ -11,12 +11,12 @@ import re import socket +from mercurial.i18n import _ from mercurial import ( encoding, error, util, ) -from mercurial.i18n import _ from . import ( common,
--- a/hgext/convert/cvsps.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/convert/cvsps.py Fri Jul 01 16:02:56 2016 -0500 @@ -6,15 +6,16 @@ # GNU General Public License version 2 or any later version. from __future__ import absolute_import -import cPickle as pickle import os import re +from mercurial.i18n import _ from mercurial import ( hook, util, ) -from mercurial.i18n import _ + +pickle = util.pickle class logentry(object): '''Class logentry has the following attributes:
--- a/hgext/convert/filemap.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/convert/filemap.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,10 +7,11 @@ import posixpath import shlex + +from mercurial.i18n import _ from mercurial import ( error, ) -from mercurial.i18n import _ from . import common SKIPREV = common.SKIPREV
--- a/hgext/convert/git.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/convert/git.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,12 +7,13 @@ from __future__ import absolute_import import os + +from mercurial.i18n import _ from mercurial import ( config, error, node as nodemod, ) -from mercurial.i18n import _ from . import ( common,
--- a/hgext/convert/gnuarch.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/convert/gnuarch.py Fri Jul 01 16:02:56 2016 -0500 @@ -12,12 +12,13 @@ import shutil import stat import tempfile + +from mercurial.i18n import _ from mercurial import ( encoding, error, util, ) -from mercurial.i18n import _ from . import common class gnuarch_source(common.converter_source, common.commandline):
--- a/hgext/convert/hg.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/convert/hg.py Fri Jul 01 16:02:56 2016 -0500 @@ -22,6 +22,7 @@ import re import time +from mercurial.i18n import _ from mercurial import ( bookmarks, context, @@ -37,7 +38,6 @@ ) stringio = util.stringio -from mercurial.i18n import _ from . import common mapfile = common.mapfile NoRepo = common.NoRepo
--- a/hgext/convert/monotone.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/convert/monotone.py Fri Jul 01 16:02:56 2016 -0500 @@ -10,11 +10,11 @@ import os import re +from mercurial.i18n import _ from mercurial import ( error, util, ) -from mercurial.i18n import _ from . import common
--- a/hgext/convert/p4.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/convert/p4.py Fri Jul 01 16:02:56 2016 -0500 @@ -9,11 +9,11 @@ import marshal import re +from mercurial.i18n import _ from mercurial import ( error, util, ) -from mercurial.i18n import _ from . import common
--- a/hgext/convert/subversion.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/convert/subversion.py Fri Jul 01 16:02:56 2016 -0500 @@ -3,13 +3,13 @@ # Copyright(C) 2007 Daniel Holth et al from __future__ import absolute_import -import cPickle as pickle import os import re import sys import tempfile import xml.dom.minidom +from mercurial.i18n import _ from mercurial import ( encoding, error, @@ -17,10 +17,10 @@ strutil, util, ) -from mercurial.i18n import _ from . import common +pickle = util.pickle stringio = util.stringio propertycache = util.propertycache urlerr = util.urlerr
--- a/hgext/factotum.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/factotum.py Fri Jul 01 16:02:56 2016 -0500 @@ -102,8 +102,7 @@ @monkeypatch_method(passwordmgr) def find_user_password(self, realm, authuri): - user, passwd = urlreq.httppasswordmgrwithdefaultrealm.find_user_password( - self, realm, authuri) + user, passwd = self.passwddb.find_user_password(realm, authuri) if user and passwd: self._writedebug(user, passwd) return (user, passwd)
--- a/hgext/fetch.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/fetch.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,12 +7,23 @@ '''pull, update and merge in one command (DEPRECATED)''' +from __future__ import absolute_import + from mercurial.i18n import _ -from mercurial.node import short -from mercurial import commands, cmdutil, hg, util, error -from mercurial.lock import release -from mercurial import exchange +from mercurial.node import ( + short, +) +from mercurial import ( + cmdutil, + commands, + error, + exchange, + hg, + lock, + util, +) +release = lock.release cmdtable = {} command = cmdutil.command(cmdtable) # Note for extension authors: ONLY specify testedwith = 'internal' for
--- a/hgext/fsmonitor/__init__.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/fsmonitor/__init__.py Fri Jul 01 16:02:56 2016 -0500 @@ -91,10 +91,12 @@ from __future__ import absolute_import +import hashlib import os import stat import sys +from mercurial.i18n import _ from mercurial import ( context, extensions, @@ -105,7 +107,6 @@ util, ) from mercurial import match as matchmod -from mercurial.i18n import _ from . import ( state, @@ -141,7 +142,7 @@ copy. """ - sha1 = util.sha1() + sha1 = hashlib.sha1() if util.safehasattr(ignore, 'includepat'): sha1.update(ignore.includepat) sha1.update('\0\0')
--- a/hgext/fsmonitor/state.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/fsmonitor/state.py Fri Jul 01 16:02:56 2016 -0500 @@ -12,8 +12,8 @@ import socket import struct +from mercurial.i18n import _ from mercurial import pathutil -from mercurial.i18n import _ _version = 4 _versionformat = ">I"
--- a/hgext/gpg.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/gpg.py Fri Jul 01 16:02:56 2016 -0500 @@ -5,10 +5,21 @@ '''commands to sign and verify changesets''' -import os, tempfile, binascii -from mercurial import util, commands, match, cmdutil, error -from mercurial import node as hgnode +from __future__ import absolute_import + +import binascii +import os +import tempfile + from mercurial.i18n import _ +from mercurial import ( + cmdutil, + commands, + error, + match, + node as hgnode, + util, +) cmdtable = {} command = cmdutil.command(cmdtable) @@ -187,7 +198,7 @@ return # print summary - ui.write("%s is signed by:\n" % hgnode.short(rev)) + ui.write(_("%s is signed by:\n") % hgnode.short(rev)) for key in keys: ui.write(" %s\n" % keystr(ui, key))
--- a/hgext/graphlog.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/graphlog.py Fri Jul 01 16:02:56 2016 -0500 @@ -15,8 +15,13 @@ revision graph is also shown. ''' +from __future__ import absolute_import + from mercurial.i18n import _ -from mercurial import cmdutil, commands +from mercurial import ( + cmdutil, + commands, +) cmdtable = {} command = cmdutil.command(cmdtable)
--- a/hgext/hgcia.py Fri Jul 01 15:12:33 2016 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,285 +0,0 @@ -# Copyright (C) 2007-8 Brendan Cully <brendan@kublai.com> -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -"""hooks for integrating with the CIA.vc notification service - -This is meant to be run as a changegroup or incoming hook. To -configure it, set the following options in your hgrc:: - - [cia] - # your registered CIA user name - user = foo - # the name of the project in CIA - project = foo - # the module (subproject) (optional) - #module = foo - # Append a diffstat to the log message (optional) - #diffstat = False - # Template to use for log messages (optional) - #template = {desc}\\n{baseurl}{webroot}/rev/{node}-- {diffstat} - # Style to use (optional) - #style = foo - # The URL of the CIA notification service (optional) - # You can use mailto: URLs to send by email, e.g. - # mailto:cia@cia.vc - # Make sure to set email.from if you do this. - #url = http://cia.vc/ - # print message instead of sending it (optional) - #test = False - # number of slashes to strip for url paths - #strip = 0 - - [hooks] - # one of these: - changegroup.cia = python:hgcia.hook - #incoming.cia = python:hgcia.hook - - [web] - # If you want hyperlinks (optional) - baseurl = http://server/path/to/repo -""" - -from mercurial.i18n import _ -from mercurial.node import bin, short -from mercurial import cmdutil, patch, util, mail, error -import email.Parser - -import socket, xmlrpclib -from xml.sax import saxutils -# Note for extension authors: ONLY specify testedwith = 'internal' for -# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should -# be specifying the version(s) of Mercurial they are tested with, or -# leave the attribute unspecified. -testedwith = 'internal' - -socket_timeout = 30 # seconds -if util.safehasattr(socket, 'setdefaulttimeout'): - # set a timeout for the socket so you don't have to wait so looooong - # when cia.vc is having problems. requires python >= 2.3: - socket.setdefaulttimeout(socket_timeout) - -HGCIA_VERSION = '0.1' -HGCIA_URL = 'http://hg.kublai.com/mercurial/hgcia' - - -class ciamsg(object): - """ A CIA message """ - def __init__(self, cia, ctx): - self.cia = cia - self.ctx = ctx - self.url = self.cia.url - if self.url: - self.url += self.cia.root - - def fileelem(self, path, uri, action): - if uri: - uri = ' uri=%s' % saxutils.quoteattr(uri) - return '<file%s action=%s>%s</file>' % ( - uri, saxutils.quoteattr(action), saxutils.escape(path)) - - def fileelems(self): - n = self.ctx.node() - f = self.cia.repo.status(self.ctx.p1().node(), n) - url = self.url or '' - if url and url[-1] == '/': - url = url[:-1] - elems = [] - for path in f.modified: - uri = '%s/diff/%s/%s' % (url, short(n), path) - elems.append(self.fileelem(path, url and uri, 'modify')) - for path in f.added: - # TODO: copy/rename ? - uri = '%s/file/%s/%s' % (url, short(n), path) - elems.append(self.fileelem(path, url and uri, 'add')) - for path in f.removed: - elems.append(self.fileelem(path, '', 'remove')) - - return '\n'.join(elems) - - def sourceelem(self, project, module=None, branch=None): - msg = ['<source>', '<project>%s</project>' % saxutils.escape(project)] - if module: - msg.append('<module>%s</module>' % saxutils.escape(module)) - if branch: - msg.append('<branch>%s</branch>' % saxutils.escape(branch)) - msg.append('</source>') - - return '\n'.join(msg) - - def diffstat(self): - class patchbuf(object): - def __init__(self): - self.lines = [] - # diffstat is stupid - self.name = 'cia' - def write(self, data): - self.lines += data.splitlines(True) - def close(self): - pass - - n = self.ctx.node() - pbuf = patchbuf() - cmdutil.export(self.cia.repo, [n], fp=pbuf) - return patch.diffstat(pbuf.lines) or '' - - def logmsg(self): - if self.cia.diffstat: - diffstat = self.diffstat() - else: - diffstat = '' - self.cia.ui.pushbuffer() - self.cia.templater.show(self.ctx, changes=self.ctx.changeset(), - baseurl=self.cia.ui.config('web', 'baseurl'), - url=self.url, diffstat=diffstat, - webroot=self.cia.root) - return self.cia.ui.popbuffer() - - def xml(self): - n = short(self.ctx.node()) - src = self.sourceelem(self.cia.project, module=self.cia.module, - branch=self.ctx.branch()) - # unix timestamp - dt = self.ctx.date() - timestamp = dt[0] - - author = saxutils.escape(self.ctx.user()) - rev = '%d:%s' % (self.ctx.rev(), n) - log = saxutils.escape(self.logmsg()) - - url = self.url - if url and url[-1] == '/': - url = url[:-1] - url = url and '<url>%s/rev/%s</url>' % (saxutils.escape(url), n) or '' - - msg = """ -<message> - <generator> - <name>Mercurial (hgcia)</name> - <version>%s</version> - <url>%s</url> - <user>%s</user> - </generator> - %s - <body> - <commit> - <author>%s</author> - <version>%s</version> - <log>%s</log> - %s - <files>%s</files> - </commit> - </body> - <timestamp>%d</timestamp> -</message> -""" % \ - (HGCIA_VERSION, saxutils.escape(HGCIA_URL), - saxutils.escape(self.cia.user), src, author, rev, log, url, - self.fileelems(), timestamp) - - return msg - - -class hgcia(object): - """ CIA notification class """ - - deftemplate = '{desc}' - dstemplate = '{desc}\n-- \n{diffstat}' - - def __init__(self, ui, repo): - self.ui = ui - self.repo = repo - - self.ciaurl = self.ui.config('cia', 'url', 'http://cia.vc') - self.user = self.ui.config('cia', 'user') - self.project = self.ui.config('cia', 'project') - self.module = self.ui.config('cia', 'module') - self.diffstat = self.ui.configbool('cia', 'diffstat') - self.emailfrom = self.ui.config('email', 'from') - self.dryrun = self.ui.configbool('cia', 'test') - self.url = self.ui.config('web', 'baseurl') - # Default to -1 for backward compatibility - self.stripcount = int(self.ui.config('cia', 'strip', -1)) - self.root = self.strip(self.repo.root) - - style = self.ui.config('cia', 'style') - template = self.ui.config('cia', 'template') - if not template: - if self.diffstat: - template = self.dstemplate - else: - template = self.deftemplate - t = cmdutil.changeset_templater(self.ui, self.repo, False, None, - template, style, False) - self.templater = t - - def strip(self, path): - '''strip leading slashes from local path, turn into web-safe path.''' - - path = util.pconvert(path) - count = self.stripcount - if count < 0: - return '' - while count > 0: - c = path.find('/') - if c == -1: - break - path = path[c + 1:] - count -= 1 - return path - - def sendrpc(self, msg): - srv = xmlrpclib.Server(self.ciaurl) - res = srv.hub.deliver(msg) - if res is not True and res != 'queued.': - raise error.Abort(_('%s returned an error: %s') % - (self.ciaurl, res)) - - def sendemail(self, address, data): - p = email.Parser.Parser() - msg = p.parsestr(data) - msg['Date'] = util.datestr(format="%a, %d %b %Y %H:%M:%S %1%2") - msg['To'] = address - msg['From'] = self.emailfrom - msg['Subject'] = 'DeliverXML' - msg['Content-type'] = 'text/xml' - msgtext = msg.as_string() - - self.ui.status(_('hgcia: sending update to %s\n') % address) - mail.sendmail(self.ui, util.email(self.emailfrom), - [address], msgtext) - - -def hook(ui, repo, hooktype, node=None, url=None, **kwargs): - """ send CIA notification """ - def sendmsg(cia, ctx): - msg = ciamsg(cia, ctx).xml() - if cia.dryrun: - ui.write(msg) - elif cia.ciaurl.startswith('mailto:'): - if not cia.emailfrom: - raise error.Abort(_('email.from must be defined when ' - 'sending by email')) - cia.sendemail(cia.ciaurl[7:], msg) - else: - cia.sendrpc(msg) - - n = bin(node) - cia = hgcia(ui, repo) - if not cia.user: - ui.debug('cia: no user specified') - return - if not cia.project: - ui.debug('cia: no project specified') - return - if hooktype == 'changegroup': - start = repo.changelog.rev(n) - end = len(repo.changelog) - for rev in xrange(start, end): - n = repo.changelog.node(rev) - ctx = repo.changectx(n) - sendmsg(cia, ctx) - else: - ctx = repo.changectx(n) - sendmsg(cia, ctx)
--- a/hgext/hgk.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/hgk.py Fri Jul 01 16:02:56 2016 -0500 @@ -34,10 +34,23 @@ vdiff on hovered and selected revisions. ''' +from __future__ import absolute_import + import os -from mercurial import cmdutil, commands, patch, scmutil, obsolete -from mercurial.node import nullid, nullrev, short + from mercurial.i18n import _ +from mercurial.node import ( + nullid, + nullrev, + short, +) +from mercurial import ( + cmdutil, + commands, + obsolete, + patch, + scmutil, +) cmdtable = {} command = cmdutil.command(cmdtable) @@ -68,13 +81,13 @@ for f in modified: # TODO get file permissions - ui.write(":100664 100664 %s %s M\t%s\t%s\n" % + ui.write((":100664 100664 %s %s M\t%s\t%s\n") % (short(mmap[f]), short(mmap2[f]), f, f)) for f in added: - ui.write(":000000 100664 %s %s N\t%s\t%s\n" % + ui.write((":000000 100664 %s %s N\t%s\t%s\n") % (empty, short(mmap2[f]), f, f)) for f in removed: - ui.write(":100664 000000 %s %s D\t%s\t%s\n" % + ui.write((":100664 000000 %s %s D\t%s\t%s\n") % (short(mmap[f]), empty, f, f)) ##
--- a/hgext/highlight/highlight.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/highlight/highlight.py Fri Jul 01 16:02:56 2016 -0500 @@ -68,7 +68,7 @@ coloriter = (s.encode(encoding.encoding, 'replace') for s in colorized.splitlines()) - tmpl.filters['colorize'] = lambda x: coloriter.next() + tmpl.filters['colorize'] = lambda x: next(coloriter) oldl = tmpl.cache[field] newl = oldl.replace('line|escape', 'line|colorize')
--- a/hgext/histedit.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/histedit.py Fri Jul 01 16:02:56 2016 -0500 @@ -169,30 +169,35 @@ """ -import pickle +from __future__ import absolute_import + import errno import os import sys -from mercurial import bundle2 -from mercurial import cmdutil -from mercurial import discovery -from mercurial import error -from mercurial import copies -from mercurial import context -from mercurial import destutil -from mercurial import exchange -from mercurial import extensions -from mercurial import hg -from mercurial import node -from mercurial import repair -from mercurial import scmutil -from mercurial import util -from mercurial import obsolete -from mercurial import merge as mergemod -from mercurial.lock import release from mercurial.i18n import _ +from mercurial import ( + bundle2, + cmdutil, + context, + copies, + destutil, + discovery, + error, + exchange, + extensions, + hg, + lock, + merge as mergemod, + node, + obsolete, + repair, + scmutil, + util, +) +pickle = util.pickle +release = lock.release cmdtable = {} command = cmdutil.command(cmdtable) @@ -408,7 +413,7 @@ raise error.ParseError(_('unknown changeset %s listed') % ha[:12]) - def torule(self): + def torule(self, initial=False): """build a histedit rule line for an action by default lines are in the form: @@ -418,6 +423,14 @@ summary = '' if ctx.description(): summary = ctx.description().splitlines()[0] + + fword = summary.split(' ', 1)[0].lower() + # if it doesn't end with the special character '!' just skip this + if (self.repo.ui.configbool("experimental", "histedit.autoverb") and + initial and fword.endswith('!')): + fword = fword[:-1] + if fword in primaryactions | secondaryactions | tertiaryactions: + self.verb = fword line = '%s %s %d %s' % (self.verb, ctx, ctx.rev(), summary) # trim to 75 columns by default so it's not stupidly wide in my editor # (the 5 more are left for verb) @@ -1304,7 +1317,7 @@ rules are in the format [ [act, ctx], ...] like in state.rules """ - rules = '\n'.join([act.torule() for act in actions]) + rules = '\n'.join([act.torule(initial=True) for act in actions]) rules += '\n\n' rules += editcomment rules = ui.edit(rules, ui.username(), {'prefix': 'histedit'})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/journal.py Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,296 @@ +# journal.py +# +# Copyright 2014-2016 Facebook, Inc. +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. +"""Track previous positions of bookmarks (EXPERIMENTAL) + +This extension adds a new command: `hg journal`, which shows you where +bookmarks were previously located. + +""" + +from __future__ import absolute_import + +import collections +import os + +from mercurial.i18n import _ + +from mercurial import ( + bookmarks, + cmdutil, + commands, + dispatch, + error, + extensions, + node, + util, +) + +cmdtable = {} +command = cmdutil.command(cmdtable) + +# Note for extension authors: ONLY specify testedwith = 'internal' for +# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should +# be specifying the version(s) of Mercurial they are tested with, or +# leave the attribute unspecified. +testedwith = 'internal' + +# storage format version; increment when the format changes +storageversion = 0 + +# namespaces +bookmarktype = 'bookmark' + +# Journal recording, register hooks and storage object +def extsetup(ui): + extensions.wrapfunction(dispatch, 'runcommand', runcommand) + extensions.wrapfunction(bookmarks.bmstore, '_write', recordbookmarks) + +def reposetup(ui, repo): + if repo.local(): + repo.journal = journalstorage(repo) + +def runcommand(orig, lui, repo, cmd, fullargs, *args): + """Track the command line options for recording in the journal""" + journalstorage.recordcommand(*fullargs) + return orig(lui, repo, cmd, fullargs, *args) + +def recordbookmarks(orig, store, fp): + """Records all bookmark changes in the journal.""" + repo = store._repo + if util.safehasattr(repo, 'journal'): + oldmarks = bookmarks.bmstore(repo) + for mark, value in store.iteritems(): + oldvalue = oldmarks.get(mark, node.nullid) + if value != oldvalue: + repo.journal.record(bookmarktype, mark, oldvalue, value) + return orig(store, fp) + +class journalentry(collections.namedtuple( + 'journalentry', + 'timestamp user command namespace name oldhashes newhashes')): + """Individual journal entry + + * timestamp: a mercurial (time, timezone) tuple + * user: the username that ran the command + * namespace: the entry namespace, an opaque string + * name: the name of the changed item, opaque string with meaning in the + namespace + * command: the hg command that triggered this record + * oldhashes: a tuple of one or more binary hashes for the old location + * newhashes: a tuple of one or more binary hashes for the new location + + Handles serialisation from and to the storage format. Fields are + separated by newlines, hashes are written out in hex separated by commas, + timestamp and timezone are separated by a space. + + """ + @classmethod + def fromstorage(cls, line): + (time, user, command, namespace, name, + oldhashes, newhashes) = line.split('\n') + timestamp, tz = time.split() + timestamp, tz = float(timestamp), int(tz) + oldhashes = tuple(node.bin(hash) for hash in oldhashes.split(',')) + newhashes = tuple(node.bin(hash) for hash in newhashes.split(',')) + return cls( + (timestamp, tz), user, command, namespace, name, + oldhashes, newhashes) + + def __str__(self): + """String representation for storage""" + time = ' '.join(map(str, self.timestamp)) + oldhashes = ','.join([node.hex(hash) for hash in self.oldhashes]) + newhashes = ','.join([node.hex(hash) for hash in self.newhashes]) + return '\n'.join(( + time, self.user, self.command, self.namespace, self.name, + oldhashes, newhashes)) + +class journalstorage(object): + """Storage for journal entries + + Entries are stored with NUL bytes as separators. See the journalentry + class for the per-entry structure. + + The file format starts with an integer version, delimited by a NUL. + + """ + _currentcommand = () + + def __init__(self, repo): + self.repo = repo + self.user = util.getuser() + self.vfs = repo.vfs + + # track the current command for recording in journal entries + @property + def command(self): + commandstr = ' '.join( + map(util.shellquote, journalstorage._currentcommand)) + if '\n' in commandstr: + # truncate multi-line commands + commandstr = commandstr.partition('\n')[0] + ' ...' + return commandstr + + @classmethod + def recordcommand(cls, *fullargs): + """Set the current hg arguments, stored with recorded entries""" + # Set the current command on the class because we may have started + # with a non-local repo (cloning for example). + cls._currentcommand = fullargs + + def record(self, namespace, name, oldhashes, newhashes): + """Record a new journal entry + + * namespace: an opaque string; this can be used to filter on the type + of recorded entries. + * name: the name defining this entry; for bookmarks, this is the + bookmark name. Can be filtered on when retrieving entries. + * oldhashes and newhashes: each a single binary hash, or a list of + binary hashes. These represent the old and new position of the named + item. + + """ + if not isinstance(oldhashes, list): + oldhashes = [oldhashes] + if not isinstance(newhashes, list): + newhashes = [newhashes] + + entry = journalentry( + util.makedate(), self.user, self.command, namespace, name, + oldhashes, newhashes) + + with self.repo.wlock(): + version = None + # open file in amend mode to ensure it is created if missing + with self.vfs('journal', mode='a+b', atomictemp=True) as f: + f.seek(0, os.SEEK_SET) + # Read just enough bytes to get a version number (up to 2 + # digits plus separator) + version = f.read(3).partition('\0')[0] + if version and version != str(storageversion): + # different version of the storage. Exit early (and not + # write anything) if this is not a version we can handle or + # the file is corrupt. In future, perhaps rotate the file + # instead? + self.repo.ui.warn( + _("unsupported journal file version '%s'\n") % version) + return + if not version: + # empty file, write version first + f.write(str(storageversion) + '\0') + f.seek(0, os.SEEK_END) + f.write(str(entry) + '\0') + + def filtered(self, namespace=None, name=None): + """Yield all journal entries with the given namespace or name + + Both the namespace and the name are optional; if neither is given all + entries in the journal are produced. + + """ + for entry in self: + if namespace is not None and entry.namespace != namespace: + continue + if name is not None and entry.name != name: + continue + yield entry + + def __iter__(self): + """Iterate over the storage + + Yields journalentry instances for each contained journal record. + + """ + if not self.vfs.exists('journal'): + return + + with self.vfs('journal') as f: + raw = f.read() + + lines = raw.split('\0') + version = lines and lines[0] + if version != str(storageversion): + version = version or _('not available') + raise error.Abort(_("unknown journal file version '%s'") % version) + + # Skip the first line, it's a version number. Reverse the rest. + lines = reversed(lines[1:]) + for line in lines: + if not line: + continue + yield journalentry.fromstorage(line) + +# journal reading +# log options that don't make sense for journal +_ignoreopts = ('no-merges', 'graph') +@command( + 'journal', [ + ('c', 'commits', None, 'show commit metadata'), + ] + [opt for opt in commands.logopts if opt[1] not in _ignoreopts], + '[OPTION]... [BOOKMARKNAME]') +def journal(ui, repo, *args, **opts): + """show the previous position of bookmarks + + The journal is used to see the previous commits of bookmarks. By default + the previous locations for all bookmarks are shown. Passing a bookmark + name will show all the previous positions of that bookmark. + + By default hg journal only shows the commit hash and the command that was + running at that time. -v/--verbose will show the prior hash, the user, and + the time at which it happened. + + Use -c/--commits to output log information on each commit hash; at this + point you can use the usual `--patch`, `--git`, `--stat` and `--template` + switches to alter the log output for these. + + `hg journal -T json` can be used to produce machine readable output. + + """ + bookmarkname = None + if args: + bookmarkname = args[0] + + fm = ui.formatter('journal', opts) + + if opts.get("template") != "json": + if bookmarkname is None: + name = _('all bookmarks') + else: + name = "'%s'" % bookmarkname + ui.status(_("previous locations of %s:\n") % name) + + limit = cmdutil.loglimit(opts) + entry = None + for count, entry in enumerate(repo.journal.filtered(name=bookmarkname)): + if count == limit: + break + newhashesstr = ','.join([node.short(hash) for hash in entry.newhashes]) + oldhashesstr = ','.join([node.short(hash) for hash in entry.oldhashes]) + + fm.startitem() + fm.condwrite(ui.verbose, 'oldhashes', '%s -> ', oldhashesstr) + fm.write('newhashes', '%s', newhashesstr) + fm.condwrite(ui.verbose, 'user', ' %s', entry.user.ljust(8)) + + timestring = util.datestr(entry.timestamp, '%Y-%m-%d %H:%M %1%2') + fm.condwrite(ui.verbose, 'date', ' %s', timestring) + fm.write('command', ' %s\n', entry.command) + + if opts.get("commits"): + displayer = cmdutil.show_changeset(ui, repo, opts, buffered=False) + for hash in entry.newhashes: + try: + ctx = repo[hash] + displayer.show(ctx) + except error.RepoLookupError as e: + fm.write('repolookuperror', "%s\n\n", str(e)) + displayer.close() + + fm.end() + + if entry is None: + ui.status(_("no recorded locations\n"))
--- a/hgext/keyword.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/keyword.py Fri Jul 01 16:02:56 2016 -0500 @@ -89,8 +89,8 @@ import re import tempfile +from mercurial.i18n import _ from mercurial.hgweb import webcommands -from mercurial.i18n import _ from mercurial import ( cmdutil, @@ -455,7 +455,7 @@ uisetup(ui) reposetup(ui, repo) - ui.write('[extensions]\nkeyword =\n') + ui.write(('[extensions]\nkeyword =\n')) demoitems('keyword', ui.configitems('keyword')) demoitems('keywordset', ui.configitems('keywordset')) demoitems('keywordmaps', kwmaps.iteritems()) @@ -735,7 +735,7 @@ def kwfilectx_cmp(orig, self, fctx): # keyword affects data size, comparing wdir and filelog size does # not make sense - if (fctx._filerev is None and + if (fctx._filenode is None and (self._repo._encodefilterpats or kwt.match(fctx.path()) and 'l' not in fctx.flags() or self.size() - 4 == fctx.size()) or
--- a/hgext/largefiles/__init__.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/largefiles/__init__.py Fri Jul 01 16:02:56 2016 -0500 @@ -104,14 +104,20 @@ explicitly do so with the --large flag passed to the :hg:`add` command. ''' +from __future__ import absolute_import -from mercurial import hg, localrepo +from mercurial import ( + hg, + localrepo, +) -import lfcommands -import proto -import reposetup -import uisetup as uisetupmod -import overrides +from . import ( + lfcommands, + overrides, + proto, + reposetup, + uisetup as uisetupmod, +) # Note for extension authors: ONLY specify testedwith = 'internal' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
--- a/hgext/largefiles/basestore.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/largefiles/basestore.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,13 +7,13 @@ # GNU General Public License version 2 or any later version. '''base class for store implementations and store-related utility code''' +from __future__ import absolute_import -import re - -from mercurial import util, node, hg, error from mercurial.i18n import _ -import lfutil +from mercurial import node, util + +from . import lfutil class StoreError(Exception): '''Raised when there is a problem getting files from or putting @@ -116,19 +116,26 @@ '''Verify the existence (and, optionally, contents) of every big file revision referenced by every changeset in revs. Return 0 if all is well, non-zero on any errors.''' - failed = False self.ui.status(_('searching %d changesets for largefiles\n') % len(revs)) verified = set() # set of (filename, filenode) tuples - + filestocheck = [] # list of (cset, filename, expectedhash) for rev in revs: cctx = self.repo[rev] cset = "%d:%s" % (cctx.rev(), node.short(cctx.node())) for standin in cctx: - if self._verifyfile(cctx, cset, contents, standin, verified): - failed = True + filename = lfutil.splitstandin(standin) + if filename: + fctx = cctx[standin] + key = (filename, fctx.filenode()) + if key not in verified: + verified.add(key) + expectedhash = fctx.data()[0:40] + filestocheck.append((cset, filename, expectedhash)) + + failed = self._verifyfiles(contents, filestocheck) numrevs = len(verified) numlfiles = len(set([fname for (fname, fnode) in verified])) @@ -150,72 +157,10 @@ exist in the store).''' raise NotImplementedError('abstract method') - def _verifyfile(self, cctx, cset, contents, standin, verified): - '''Perform the actual verification of a file in the store. - 'cset' is only used in warnings. + def _verifyfiles(self, contents, filestocheck): + '''Perform the actual verification of files in the store. 'contents' controls verification of content hash. - 'standin' is the standin path of the largefile to verify. - 'verified' is maintained as a set of already verified files. - Returns _true_ if it is a standin and any problems are found! + 'filestocheck' is list of files to check. + Returns _true_ if any problems are found! ''' raise NotImplementedError('abstract method') - -import localstore, wirestore - -_storeprovider = { - 'file': [localstore.localstore], - 'http': [wirestore.wirestore], - 'https': [wirestore.wirestore], - 'ssh': [wirestore.wirestore], - } - -_scheme_re = re.compile(r'^([a-zA-Z0-9+-.]+)://') - -# During clone this function is passed the src's ui object -# but it needs the dest's ui object so it can read out of -# the config file. Use repo.ui instead. -def _openstore(repo, remote=None, put=False): - ui = repo.ui - - if not remote: - lfpullsource = getattr(repo, 'lfpullsource', None) - if lfpullsource: - path = ui.expandpath(lfpullsource) - elif put: - path = ui.expandpath('default-push', 'default') - else: - path = ui.expandpath('default') - - # ui.expandpath() leaves 'default-push' and 'default' alone if - # they cannot be expanded: fallback to the empty string, - # meaning the current directory. - if path == 'default-push' or path == 'default': - path = '' - remote = repo - else: - path, _branches = hg.parseurl(path) - remote = hg.peer(repo, {}, path) - - # The path could be a scheme so use Mercurial's normal functionality - # to resolve the scheme to a repository and use its path - path = util.safehasattr(remote, 'url') and remote.url() or remote.path - - match = _scheme_re.match(path) - if not match: # regular filesystem path - scheme = 'file' - else: - scheme = match.group(1) - - try: - storeproviders = _storeprovider[scheme] - except KeyError: - raise error.Abort(_('unsupported URL scheme %r') % scheme) - - for classobj in storeproviders: - try: - return classobj(ui, repo, remote) - except lfutil.storeprotonotcapable: - pass - - raise error.Abort(_('%s does not appear to be a largefile store') % - util.hidepassword(path))
--- a/hgext/largefiles/lfcommands.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/largefiles/lfcommands.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,20 +7,39 @@ # GNU General Public License version 2 or any later version. '''High-level command function for lfconvert, plus the cmdtable.''' +from __future__ import absolute_import -import os, errno +import errno +import hashlib +import os import shutil -from mercurial import util, match as match_, hg, node, context, error, \ - cmdutil, scmutil, commands from mercurial.i18n import _ -from mercurial.lock import release -from hgext.convert import convcmd -from hgext.convert import filemap +from mercurial import ( + cmdutil, + commands, + context, + error, + hg, + lock, + match as matchmod, + node, + scmutil, + util, +) -import lfutil -import basestore +from ..convert import ( + convcmd, + filemap, +) + +from . import ( + lfutil, + storefactory +) + +release = lock.release # -- Commands ---------------------------------------------------------- @@ -92,7 +111,7 @@ if not pats: pats = ui.configlist(lfutil.longname, 'patterns', default=[]) if pats: - matcher = match_.match(rsrc.root, '', list(pats)) + matcher = matchmod.match(rsrc.root, '', list(pats)) else: matcher = None @@ -211,7 +230,7 @@ raise error.Abort(_('largefile %s becomes symlink') % f) # largefile was modified, update standins - m = util.sha1('') + m = hashlib.sha1('') m.update(ctx[f].data()) hash = m.hexdigest() if f not in lfiletohash or lfiletohash[f] != hash: @@ -337,7 +356,7 @@ if not files: return - store = basestore._openstore(rsrc, rdst, put=True) + store = storefactory.openstore(rsrc, rdst, put=True) at = 0 ui.debug("sending statlfile command for %d largefiles\n" % len(files)) @@ -368,7 +387,7 @@ else: revs = ['.'] - store = basestore._openstore(repo) + store = storefactory.openstore(repo) return store.verify(revs, contents=contents) def cachelfiles(ui, repo, node, filelist=None): @@ -394,7 +413,7 @@ toget.append((lfile, expectedhash)) if toget: - store = basestore._openstore(repo) + store = storefactory.openstore(repo) ret = store.get(toget) return ret
--- a/hgext/largefiles/lfutil.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/largefiles/lfutil.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,21 +7,30 @@ # GNU General Public License version 2 or any later version. '''largefiles utility code: must not import other modules in this package.''' +from __future__ import absolute_import +import copy +import hashlib import os import platform import stat -import copy + +from mercurial.i18n import _ -from mercurial import dirstate, httpconnection, match as match_, util, scmutil -from mercurial.i18n import _ -from mercurial import node, error +from mercurial import ( + dirstate, + error, + httpconnection, + match as matchmod, + node, + scmutil, + util, +) shortname = '.hglf' shortnameslash = shortname + '/' longname = 'largefiles' - # -- Private worker functions ------------------------------------------ def getminsize(ui, assumelfiles, opt, default=10): @@ -152,7 +161,7 @@ def lfdirstatestatus(lfdirstate, repo): wctx = repo['.'] - match = match_.always(repo.root, repo.getcwd()) + match = matchmod.always(repo.root, repo.getcwd()) unsure, s = lfdirstate.status(match, [], False, False, False) modified, clean = s.modified, s.clean for lfile in unsure: @@ -180,12 +189,11 @@ if rev is not None or repo.dirstate[f] != '?'] def instore(repo, hash, forcelocal=False): - '''Return true if a largefile with the given hash exists in the user - cache.''' + '''Return true if a largefile with the given hash exists in the store''' return os.path.exists(storepath(repo, hash, forcelocal)) def storepath(repo, hash, forcelocal=False): - '''Return the correct location in the repository largefiles cache for a + '''Return the correct location in the repository largefiles store for a file with the given hash.''' if not forcelocal and repo.shared(): return repo.vfs.reljoin(repo.sharedpath, longname, hash) @@ -251,7 +259,6 @@ realfile = splitstandin(filename) copytostore(repo, ctx.node(), realfile) - def copytostoreabsolute(repo, file, hash): if inusercache(repo.ui, hash): link(usercachepath(repo.ui, hash), storepath(repo, hash)) @@ -350,7 +357,7 @@ def copyandhash(instream, outfile): '''Read bytes from instream (iterable) and write them to outfile, computing the SHA-1 hash of the data along the way. Return the hash.''' - hasher = util.sha1('') + hasher = hashlib.sha1('') for data in instream: hasher.update(data) outfile.write(data) @@ -362,7 +369,7 @@ def hashfile(file): if not os.path.exists(file): return '' - hasher = util.sha1('') + hasher = hashlib.sha1('') fd = open(file, 'rb') for data in util.filechunkiter(fd, 128 * 1024): hasher.update(data) @@ -391,7 +398,7 @@ def hexsha1(data): """hexsha1 returns the hex-encoded sha1 sum of the data in the file-like object data""" - h = util.sha1() + h = hashlib.sha1() for chunk in util.filechunkiter(data): h.update(chunk) return h.hexdigest() @@ -533,7 +540,7 @@ # otherwise to update all standins if the largefiles are # large. lfdirstate = openlfdirstate(ui, repo) - dirtymatch = match_.always(repo.root, repo.getcwd()) + dirtymatch = matchmod.always(repo.root, repo.getcwd()) unsure, s = lfdirstate.status(dirtymatch, [], False, False, False) modifiedfiles = unsure + s.modified + s.added + s.removed
--- a/hgext/largefiles/localstore.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/largefiles/localstore.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,11 +7,14 @@ # GNU General Public License version 2 or any later version. '''store class for local filesystem''' +from __future__ import absolute_import from mercurial.i18n import _ -import lfutil -import basestore +from . import ( + basestore, + lfutil, +) class localstore(basestore.basestore): '''localstore first attempts to grab files out of the store in the remote @@ -33,7 +36,6 @@ retval[hash] = lfutil.instore(self.remote, hash) return retval - def _getfile(self, tmpfile, filename, hash): path = lfutil.findfile(self.remote, hash) if not path: @@ -42,29 +44,23 @@ with open(path, 'rb') as fd: return lfutil.copyandhash(fd, tmpfile) - def _verifyfile(self, cctx, cset, contents, standin, verified): - filename = lfutil.splitstandin(standin) - if not filename: - return False - fctx = cctx[standin] - key = (filename, fctx.filenode()) - if key in verified: - return False - - expecthash = fctx.data()[0:40] - storepath, exists = lfutil.findstorepath(self.remote, expecthash) - verified.add(key) - if not exists: - self.ui.warn( - _('changeset %s: %s references missing %s\n') - % (cset, filename, storepath)) - return True # failed - - if contents: - actualhash = lfutil.hashfile(storepath) - if actualhash != expecthash: + def _verifyfiles(self, contents, filestocheck): + failed = False + for cset, filename, expectedhash in filestocheck: + storepath, exists = lfutil.findstorepath(self.repo, expectedhash) + if not exists: + storepath, exists = lfutil.findstorepath( + self.remote, expectedhash) + if not exists: self.ui.warn( - _('changeset %s: %s references corrupted %s\n') + _('changeset %s: %s references missing %s\n') % (cset, filename, storepath)) - return True # failed - return False + failed = True + elif contents: + actualhash = lfutil.hashfile(storepath) + if actualhash != expectedhash: + self.ui.warn( + _('changeset %s: %s references corrupted %s\n') + % (cset, filename, storepath)) + failed = True + return failed
--- a/hgext/largefiles/overrides.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/largefiles/overrides.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,17 +7,31 @@ # GNU General Public License version 2 or any later version. '''Overridden Mercurial commands and functions for the largefiles extension''' +from __future__ import absolute_import -import os import copy +import os -from mercurial import hg, util, cmdutil, scmutil, match as match_, \ - archival, pathutil, registrar, revset, error from mercurial.i18n import _ -import lfutil -import lfcommands -import basestore +from mercurial import ( + archival, + cmdutil, + error, + hg, + match as matchmod, + pathutil, + registrar, + revset, + scmutil, + util, +) + +from . import ( + lfcommands, + lfutil, + storefactory, +) # -- Utility functions: commonly/repeatedly needed functionality --------------- @@ -99,13 +113,13 @@ if lfutil.islfilesrepo(repo): lfpats = ui.configlist(lfutil.longname, 'patterns', default=[]) if lfpats: - lfmatcher = match_.match(repo.root, '', list(lfpats)) + lfmatcher = matchmod.match(repo.root, '', list(lfpats)) lfnames = [] m = matcher wctx = repo[None] - for f in repo.walk(match_.badmatch(m, lambda x, y: None)): + for f in repo.walk(matchmod.badmatch(m, lambda x, y: None)): exact = m.exact(f) lfile = lfutil.standin(f) in wctx nfile = f in wctx @@ -307,7 +321,7 @@ if pat.startswith('set:'): return pat - kindpat = match_._patsplit(pat, None) + kindpat = matchmod._patsplit(pat, None) if kindpat[0] is not None: return kindpat[0] + ':' + tostandin(kindpat[1]) @@ -532,7 +546,6 @@ return orig(repo, actions, branchmerge) - # Override filemerge to prompt the user about how they wish to merge # largefiles. This will handle identical edits without prompting the user. def overridefilemerge(origfn, premerge, repo, mynode, orig, fcd, fco, fca, @@ -626,7 +639,7 @@ # The patterns were previously mangled to add the standin # directory; we need to remove that now for pat in pats: - if match_.patkind(pat) is None and lfutil.shortname in pat: + if matchmod.patkind(pat) is None and lfutil.shortname in pat: newpats.append(pat.replace(lfutil.shortname, '')) else: newpats.append(pat) @@ -644,7 +657,7 @@ oldmatch = installmatchfn(overridematch) listpats = [] for pat in pats: - if match_.patkind(pat) is not None: + if matchmod.patkind(pat) is not None: listpats.append(pat) else: listpats.append(makestandin(pat)) @@ -977,7 +990,7 @@ if subrepos: for subpath in sorted(ctx.substate): sub = ctx.workingsub(subpath) - submatch = match_.subdirmatcher(subpath, matchfn) + submatch = matchmod.subdirmatcher(subpath, matchfn) sub._repo.lfstatus = True sub.archive(archiver, prefix, submatch) @@ -1025,7 +1038,7 @@ for subpath in sorted(ctx.substate): sub = ctx.workingsub(subpath) - submatch = match_.subdirmatcher(subpath, match) + submatch = matchmod.subdirmatcher(subpath, match) sub._repo.lfstatus = True sub.archive(archiver, prefix + repo._path + '/', submatch) @@ -1109,7 +1122,7 @@ lfhashes.add(lfhash) lfutil.getlfilestoupload(repo, missing, dedup) if lfhashes: - lfexists = basestore._openstore(repo, other).exists(lfhashes) + lfexists = storefactory.openstore(repo, other).exists(lfhashes) for fn, lfhash in knowns: if not lfexists[lfhash]: # lfhash doesn't exist on "other" addfunc(fn, lfhash) @@ -1190,7 +1203,7 @@ return orig(repo, matcher, prefix, opts, dry_run, similarity) # Get the list of missing largefiles so we can remove them lfdirstate = lfutil.openlfdirstate(repo.ui, repo) - unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], + unsure, s = lfdirstate.status(matchmod.always(repo.root, repo.getcwd()), [], False, False, False) # Call into the normal remove code, but the removing of the standin, we want @@ -1338,7 +1351,7 @@ else: hash = lfutil.readstandin(repo, lf, ctx.rev()) if not lfutil.inusercache(repo.ui, hash): - store = basestore._openstore(repo) + store = storefactory.openstore(repo) success, missing = store.get([(lf, hash)]) if len(success) != 1: raise error.Abort( @@ -1375,7 +1388,7 @@ # (*1) deprecated, but used internally (e.g: "rebase --collapse") lfdirstate = lfutil.openlfdirstate(repo.ui, repo) - unsure, s = lfdirstate.status(match_.always(repo.root, + unsure, s = lfdirstate.status(matchmod.always(repo.root, repo.getcwd()), [], False, False, False) pctx = repo['.']
--- a/hgext/largefiles/proto.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/largefiles/proto.py Fri Jul 01 16:02:56 2016 -0500 @@ -2,18 +2,27 @@ # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import import os import re -from mercurial import error, httppeer, util, wireproto from mercurial.i18n import _ +from mercurial import ( + error, + httppeer, + util, + wireproto, +) + +from . import ( + lfutil, +) + urlerr = util.urlerr urlreq = util.urlreq -import lfutil - LARGEFILES_REQUIRED_MSG = ('\nThis repository uses the largefiles extension.' '\n\nPlease enable it in your Mercurial config ' 'file.\n')
--- a/hgext/largefiles/remotestore.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/largefiles/remotestore.py Fri Jul 01 16:02:56 2016 -0500 @@ -5,20 +5,30 @@ # GNU General Public License version 2 or any later version. '''remote largefile store; the base class for wirestore''' +from __future__ import absolute_import -from mercurial import util, wireproto, error from mercurial.i18n import _ +from mercurial import ( + error, + util, + wireproto, +) + +from . import ( + basestore, + lfutil, + localstore, +) + urlerr = util.urlerr urlreq = util.urlreq -import lfutil -import basestore - class remotestore(basestore.basestore): '''a largefile store accessed over a network''' def __init__(self, ui, repo, url): super(remotestore, self).__init__(ui, repo, url) + self._lstore = localstore.localstore(self.ui, self.repo, self.repo) def put(self, source, hash): if self.sendfile(source, hash): @@ -65,34 +75,43 @@ return lfutil.copyandhash(chunks, tmpfile) - def _verifyfile(self, cctx, cset, contents, standin, verified): - filename = lfutil.splitstandin(standin) - if not filename: - return False - fctx = cctx[standin] - key = (filename, fctx.filenode()) - if key in verified: - return False + def _hashesavailablelocally(self, hashes): + existslocallymap = self._lstore.exists(hashes) + localhashes = [hash for hash in hashes if existslocallymap[hash]] + return localhashes - verified.add(key) + def _verifyfiles(self, contents, filestocheck): + failed = False + expectedhashes = [expectedhash + for cset, filename, expectedhash in filestocheck] + localhashes = self._hashesavailablelocally(expectedhashes) + stats = self._stat([expectedhash for expectedhash in expectedhashes + if expectedhash not in localhashes]) - expecthash = fctx.data()[0:40] - stat = self._stat([expecthash])[expecthash] - if not stat: - return False - elif stat == 1: - self.ui.warn( - _('changeset %s: %s: contents differ\n') - % (cset, filename)) - return True # failed - elif stat == 2: - self.ui.warn( - _('changeset %s: %s missing\n') - % (cset, filename)) - return True # failed - else: - raise RuntimeError('verify failed: unexpected response from ' - 'statlfile (%r)' % stat) + for cset, filename, expectedhash in filestocheck: + if expectedhash in localhashes: + filetocheck = (cset, filename, expectedhash) + verifyresult = self._lstore._verifyfiles(contents, + [filetocheck]) + if verifyresult: + failed = True + else: + stat = stats[expectedhash] + if stat: + if stat == 1: + self.ui.warn( + _('changeset %s: %s: contents differ\n') + % (cset, filename)) + failed = True + elif stat == 2: + self.ui.warn( + _('changeset %s: %s missing\n') + % (cset, filename)) + failed = True + else: + raise RuntimeError('verify failed: unexpected response ' + 'from statlfile (%r)' % stat) + return failed def batch(self): '''Support for remote batching.'''
--- a/hgext/largefiles/reposetup.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/largefiles/reposetup.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,14 +7,23 @@ # GNU General Public License version 2 or any later version. '''setup for largefiles repositories: reposetup''' +from __future__ import absolute_import + import copy -from mercurial import error, match as match_, error from mercurial.i18n import _ -from mercurial import scmutil, localrepo -import lfcommands -import lfutil +from mercurial import ( + error, + localrepo, + match as matchmod, + scmutil, +) + +from . import ( + lfcommands, + lfutil, +) def reposetup(ui, repo): # wire repositories should be given new wireproto functions @@ -94,7 +103,7 @@ parentworking = working and ctx1 == self['.'] if match is None: - match = match_.always(self.root, self.getcwd()) + match = matchmod.always(self.root, self.getcwd()) wlock = None try:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/largefiles/storefactory.py Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,78 @@ +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from __future__ import absolute_import + +import re + +from mercurial.i18n import _ + +from mercurial import ( + error, + hg, + util, +) + +from . import ( + lfutil, + localstore, + wirestore, +) + +# During clone this function is passed the src's ui object +# but it needs the dest's ui object so it can read out of +# the config file. Use repo.ui instead. +def openstore(repo, remote=None, put=False): + ui = repo.ui + + if not remote: + lfpullsource = getattr(repo, 'lfpullsource', None) + if lfpullsource: + path = ui.expandpath(lfpullsource) + elif put: + path = ui.expandpath('default-push', 'default') + else: + path = ui.expandpath('default') + + # ui.expandpath() leaves 'default-push' and 'default' alone if + # they cannot be expanded: fallback to the empty string, + # meaning the current directory. + if path == 'default-push' or path == 'default': + path = '' + remote = repo + else: + path, _branches = hg.parseurl(path) + remote = hg.peer(repo, {}, path) + + # The path could be a scheme so use Mercurial's normal functionality + # to resolve the scheme to a repository and use its path + path = util.safehasattr(remote, 'url') and remote.url() or remote.path + + match = _scheme_re.match(path) + if not match: # regular filesystem path + scheme = 'file' + else: + scheme = match.group(1) + + try: + storeproviders = _storeprovider[scheme] + except KeyError: + raise error.Abort(_('unsupported URL scheme %r') % scheme) + + for classobj in storeproviders: + try: + return classobj(ui, repo, remote) + except lfutil.storeprotonotcapable: + pass + + raise error.Abort(_('%s does not appear to be a largefile store') % + util.hidepassword(path)) + +_storeprovider = { + 'file': [localstore.localstore], + 'http': [wirestore.wirestore], + 'https': [wirestore.wirestore], + 'ssh': [wirestore.wirestore], + } + +_scheme_re = re.compile(r'^([a-zA-Z0-9+-.]+)://')
--- a/hgext/largefiles/uisetup.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/largefiles/uisetup.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,14 +7,36 @@ # GNU General Public License version 2 or any later version. '''setup for largefiles extension: uisetup''' +from __future__ import absolute_import -from mercurial import archival, cmdutil, commands, extensions, filemerge, hg, \ - httppeer, merge, scmutil, sshpeer, wireproto, subrepo, copies, exchange from mercurial.i18n import _ -from mercurial.hgweb import hgweb_mod, webcommands + +from mercurial.hgweb import ( + hgweb_mod, + webcommands, +) -import overrides -import proto +from mercurial import ( + archival, + cmdutil, + commands, + copies, + exchange, + extensions, + filemerge, + hg, + httppeer, + merge, + scmutil, + sshpeer, + subrepo, + wireproto, +) + +from . import ( + overrides, + proto, +) def uisetup(ui): # Disable auto-status for some commands which assume that all
--- a/hgext/largefiles/wirestore.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/largefiles/wirestore.py Fri Jul 01 16:02:56 2016 -0500 @@ -4,9 +4,12 @@ # GNU General Public License version 2 or any later version. '''largefile store working over Mercurial's wire protocol''' +from __future__ import absolute_import -import lfutil -import remotestore +from . import ( + lfutil, + remotestore, +) class wirestore(remotestore.remotestore): def __init__(self, ui, repo, remote):
--- a/hgext/mq.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/mq.py Fri Jul 01 16:02:56 2016 -0500 @@ -62,19 +62,39 @@ in the strip extension. ''' +from __future__ import absolute_import + +import errno +import os +import re +import shutil from mercurial.i18n import _ -from mercurial.node import bin, hex, short, nullid, nullrev -from mercurial.lock import release -from mercurial import commands, cmdutil, hg, scmutil, util, revset -from mercurial import dispatch -from mercurial import extensions, error, phases -from mercurial import patch as patchmod -from mercurial import lock as lockmod -from mercurial import localrepo -from mercurial import registrar -from mercurial import subrepo -import os, re, errno, shutil - +from mercurial.node import ( + bin, + hex, + nullid, + nullrev, + short, +) +from mercurial import ( + cmdutil, + commands, + dispatch, + error, + extensions, + hg, + localrepo, + lock as lockmod, + patch as patchmod, + phases, + registrar, + revset, + scmutil, + subrepo, + util, +) + +release = lockmod.release seriesopts = [('s', 'summary', None, _('print first line of patch header'))] cmdtable = {}
--- a/hgext/notify.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/notify.py Fri Jul 01 16:02:56 2016 -0500 @@ -139,6 +139,7 @@ import socket import time +from mercurial.i18n import _ from mercurial import ( cmdutil, error, @@ -146,7 +147,6 @@ patch, util, ) -from mercurial.i18n import _ # Note for extension authors: ONLY specify testedwith = 'internal' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should @@ -363,7 +363,7 @@ s = patch.diffstat(difflines) # s may be nil, don't include the header if it is if s: - self.ui.write('\ndiffstat:\n\n%s' % s) + self.ui.write(_('\ndiffstat:\n\n%s') % s) if maxdiff == 0: return
--- a/hgext/pager.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/pager.py Fri Jul 01 16:02:56 2016 -0500 @@ -66,6 +66,7 @@ import subprocess import sys +from mercurial.i18n import _ from mercurial import ( cmdutil, commands, @@ -73,7 +74,6 @@ extensions, util, ) -from mercurial.i18n import _ # Note for extension authors: ONLY specify testedwith = 'internal' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
--- a/hgext/patchbomb.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/patchbomb.py Fri Jul 01 16:02:56 2016 -0500 @@ -71,6 +71,7 @@ import socket import tempfile +from mercurial.i18n import _ from mercurial import ( cmdutil, commands, @@ -83,7 +84,6 @@ util, ) stringio = util.stringio -from mercurial.i18n import _ cmdtable = {} command = cmdutil.command(cmdtable) @@ -708,13 +708,7 @@ fp.close() else: if not sendmail: - verifycert = ui.config('smtp', 'verifycert', 'strict') - if opts.get('insecure'): - ui.setconfig('smtp', 'verifycert', 'loose', 'patchbomb') - try: - sendmail = mail.connect(ui, mbox=mbox) - finally: - ui.setconfig('smtp', 'verifycert', verifycert, 'patchbomb') + sendmail = mail.connect(ui, mbox=mbox) ui.status(_('sending '), subj, ' ...\n') ui.progress(_('sending'), i, item=subj, total=len(msgs), unit=_('emails'))
--- a/hgext/purge.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/purge.py Fri Jul 01 16:02:56 2016 -0500 @@ -27,6 +27,7 @@ import os +from mercurial.i18n import _ from mercurial import ( cmdutil, commands, @@ -34,7 +35,6 @@ scmutil, util, ) -from mercurial.i18n import _ cmdtable = {} command = cmdutil.command(cmdtable) @@ -84,13 +84,13 @@ list of files that this program would delete, use the --print option. ''' - act = not opts['print'] + act = not opts.get('print') eol = '\n' - if opts['print0']: + if opts.get('print0'): eol = '\0' act = False # --print0 implies --print - removefiles = opts['files'] - removedirs = opts['dirs'] + removefiles = opts.get('files') + removedirs = opts.get('dirs') if not removefiles and not removedirs: removefiles = True removedirs = True @@ -101,7 +101,7 @@ remove_func(repo.wjoin(name)) except OSError: m = _('%s cannot be removed') % name - if opts['abort_on_err']: + if opts.get('abort_on_err'): raise error.Abort(m) ui.warn(_('warning: %s\n') % m) else: @@ -111,7 +111,7 @@ if removedirs: directories = [] match.explicitdir = match.traversedir = directories.append - status = repo.status(match=match, ignored=opts['all'], unknown=True) + status = repo.status(match=match, ignored=opts.get('all'), unknown=True) if removefiles: for f in sorted(status.unknown + status.ignored):
--- a/hgext/rebase.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/rebase.py Fri Jul 01 16:02:56 2016 -0500 @@ -14,14 +14,42 @@ https://mercurial-scm.org/wiki/RebaseExtension ''' -from mercurial import hg, util, repair, merge, cmdutil, commands, bookmarks -from mercurial import extensions, patch, scmutil, phases, obsolete, error -from mercurial import copies, destutil, repoview, registrar, revset -from mercurial.commands import templateopts -from mercurial.node import nullrev, nullid, hex, short -from mercurial.lock import release +from __future__ import absolute_import + +import errno +import os + from mercurial.i18n import _ -import os, errno +from mercurial.node import ( + hex, + nullid, + nullrev, + short, +) +from mercurial import ( + bookmarks, + cmdutil, + commands, + copies, + destutil, + error, + extensions, + hg, + lock, + merge, + obsolete, + patch, + phases, + registrar, + repair, + repoview, + revset, + scmutil, + util, +) + +release = lock.release +templateopts = commands.templateopts # The following constants are used throughout the rebase module. The ordering of # their values must be maintained. @@ -91,6 +119,114 @@ sourceset = revset.getset(repo, revset.fullreposet(repo), x) return subset & revset.baseset([_destrebase(repo, sourceset)]) +class rebaseruntime(object): + """This class is a container for rebase runtime state""" + def __init__(self, repo, ui, opts=None): + if opts is None: + opts = {} + + self.repo = repo + self.ui = ui + self.opts = opts + self.originalwd = None + self.external = nullrev + # Mapping between the old revision id and either what is the new rebased + # revision or what needs to be done with the old revision. The state + # dict will be what contains most of the rebase progress state. + self.state = {} + self.activebookmark = None + self.target = None + self.skipped = set() + self.targetancestors = set() + + self.collapsef = opts.get('collapse', False) + self.collapsemsg = cmdutil.logmessage(ui, opts) + self.date = opts.get('date', None) + + e = opts.get('extrafn') # internal, used by e.g. hgsubversion + self.extrafns = [_savegraft] + if e: + self.extrafns = [e] + + self.keepf = opts.get('keep', False) + self.keepbranchesf = opts.get('keepbranches', False) + # keepopen is not meant for use on the command line, but by + # other extensions + self.keepopen = opts.get('keepopen', False) + self.obsoletenotrebased = {} + + def restorestatus(self): + """Restore a previously stored status""" + repo = self.repo + keepbranches = None + target = None + collapse = False + external = nullrev + activebookmark = None + state = {} + + try: + f = repo.vfs("rebasestate") + for i, l in enumerate(f.read().splitlines()): + if i == 0: + originalwd = repo[l].rev() + elif i == 1: + target = repo[l].rev() + elif i == 2: + external = repo[l].rev() + elif i == 3: + collapse = bool(int(l)) + elif i == 4: + keep = bool(int(l)) + elif i == 5: + keepbranches = bool(int(l)) + elif i == 6 and not (len(l) == 81 and ':' in l): + # line 6 is a recent addition, so for backwards + # compatibility check that the line doesn't look like the + # oldrev:newrev lines + activebookmark = l + else: + oldrev, newrev = l.split(':') + if newrev in (str(nullmerge), str(revignored), + str(revprecursor), str(revpruned)): + state[repo[oldrev].rev()] = int(newrev) + elif newrev == nullid: + state[repo[oldrev].rev()] = revtodo + # Legacy compat special case + else: + state[repo[oldrev].rev()] = repo[newrev].rev() + + except IOError as err: + if err.errno != errno.ENOENT: + raise + cmdutil.wrongtooltocontinue(repo, _('rebase')) + + if keepbranches is None: + raise error.Abort(_('.hg/rebasestate is incomplete')) + + skipped = set() + # recompute the set of skipped revs + if not collapse: + seen = set([target]) + for old, new in sorted(state.items()): + if new != revtodo and new in seen: + skipped.add(old) + seen.add(new) + repo.ui.debug('computed skipped revs: %s\n' % + (' '.join(str(r) for r in sorted(skipped)) or None)) + repo.ui.debug('rebase status resumed\n') + _setrebasesetvisibility(repo, state.keys()) + + self.originalwd = originalwd + self.target = target + self.state = state + self.skipped = skipped + self.collapsef = collapse + self.keepf = keep + self.keepbranchesf = keepbranches + self.external = external + self.activebookmark = activebookmark + @command('rebase', [('s', 'source', '', _('rebase the specified changeset and descendants'), _('REV')), @@ -201,16 +337,7 @@ unresolved conflicts. """ - originalwd = target = None - activebookmark = None - external = nullrev - # Mapping between the old revision id and either what is the new rebased - # revision or what needs to be done with the old revision. The state dict - # will be what contains most of the rebase progress state. - state = {} - skipped = set() - targetancestors = set() - + rbsrt = rebaseruntime(repo, ui, opts) lock = wlock = None try: @@ -227,19 +354,6 @@ destspace = opts.get('_destspace') contf = opts.get('continue') abortf = opts.get('abort') - collapsef = opts.get('collapse', False) - collapsemsg = cmdutil.logmessage(ui, opts) - date = opts.get('date', None) - e = opts.get('extrafn') # internal, used by e.g. hgsubversion - extrafns = [_savegraft] - if e: - extrafns = [e] - keepf = opts.get('keep', False) - keepbranchesf = opts.get('keepbranches', False) - # keepopen is not meant for use on the command line, but by - # other extensions - keepopen = opts.get('keepopen', False) - if opts.get('interactive'): try: if extensions.find('histedit'): @@ -251,14 +365,14 @@ "'histedit' extension (see \"%s\")") % help raise error.Abort(msg) - if collapsemsg and not collapsef: + if rbsrt.collapsemsg and not rbsrt.collapsef: raise error.Abort( _('message can only be specified with collapse')) if contf or abortf: if contf and abortf: raise error.Abort(_('cannot use both abort and continue')) - if collapsef: + if rbsrt.collapsef: raise error.Abort( _('cannot use collapse with continue or abort')) if srcf or basef or destf: @@ -268,9 +382,8 @@ ui.warn(_('tool option will be ignored\n')) try: - (originalwd, target, state, skipped, collapsef, keepf, - keepbranchesf, external, activebookmark) = restorestatus(repo) - collapsemsg = restorecollapsemsg(repo) + rbsrt.restorestatus() + rbsrt.collapsemsg = restorecollapsemsg(repo) except error.RepoLookupError: if abortf: clearstatus(repo) @@ -283,19 +396,19 @@ hint = _('use "hg rebase --abort" to clear broken state') raise error.Abort(msg, hint=hint) if abortf: - return abort(repo, originalwd, target, state, - activebookmark=activebookmark) + return abort(repo, rbsrt.originalwd, rbsrt.target, + rbsrt.state, + activebookmark=rbsrt.activebookmark) - obsoletenotrebased = {} + rbsrt.obsoletenotrebased = {} if ui.configbool('experimental', 'rebaseskipobsolete', default=True): - rebaseobsrevs = set([r for r, status in state.items() - if status == revprecursor]) - rebasesetrevs = set(state.keys()) - obsoletenotrebased = _computeobsoletenotrebased(repo, - rebaseobsrevs, - target) - rebaseobsskipped = set(obsoletenotrebased) + rebaseobsrevs = set([r for r, st in rbsrt.state.items() + if st == revprecursor]) + rebasesetrevs = set(rbsrt.state.keys()) + rbsrt.obsoletenotrebased = _computeobsoletenotrebased(repo, + rebaseobsrevs, rbsrt.target) + rebaseobsskipped = set(rbsrt.obsoletenotrebased) _checkobsrebase(repo, ui, rebaseobsrevs, rebasesetrevs, rebaseobsskipped) else: @@ -305,7 +418,7 @@ return _nothingtorebase() allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt) - if (not (keepf or allowunstable) + if (not (rbsrt.keepf or allowunstable) and repo.revs('first(children(%ld) - %ld)', rebaseset, rebaseset)): raise error.Abort( @@ -313,21 +426,21 @@ " unrebased descendants"), hint=_('use --keep to keep original changesets')) - obsoletenotrebased = {} + rbsrt.obsoletenotrebased = {} if ui.configbool('experimental', 'rebaseskipobsolete', default=True): rebasesetrevs = set(rebaseset) rebaseobsrevs = _filterobsoleterevs(repo, rebasesetrevs) - obsoletenotrebased = _computeobsoletenotrebased(repo, + rbsrt.obsoletenotrebased = _computeobsoletenotrebased(repo, rebaseobsrevs, dest) - rebaseobsskipped = set(obsoletenotrebased) + rebaseobsskipped = set(rbsrt.obsoletenotrebased) _checkobsrebase(repo, ui, rebaseobsrevs, rebasesetrevs, rebaseobsskipped) - result = buildstate(repo, dest, rebaseset, collapsef, - obsoletenotrebased) + result = buildstate(repo, dest, rebaseset, rbsrt.collapsef, + rbsrt.obsoletenotrebased) if not result: # Empty state built, nothing to rebase @@ -335,46 +448,48 @@ return _nothingtorebase() root = min(rebaseset) - if not keepf and not repo[root].mutable(): + if not rbsrt.keepf and not repo[root].mutable(): raise error.Abort(_("can't rebase public changeset %s") % repo[root], hint=_('see "hg help phases" for details')) - originalwd, target, state = result - if collapsef: - targetancestors = repo.changelog.ancestors([target], - inclusive=True) - external = externalparent(repo, state, targetancestors) + (rbsrt.originalwd, rbsrt.target, rbsrt.state) = result + if rbsrt.collapsef: + rbsrt.targetancestors = repo.changelog.ancestors([rbsrt.target], + inclusive=True) + rbsrt.external = externalparent(repo, rbsrt.state, + rbsrt.targetancestors) - if dest.closesbranch() and not keepbranchesf: + if dest.closesbranch() and not rbsrt.keepbranchesf: ui.status(_('reopening closed branch head %s\n') % dest) - if keepbranchesf: + if rbsrt.keepbranchesf: # insert _savebranch at the start of extrafns so if # there's a user-provided extrafn it can clobber branch if # desired - extrafns.insert(0, _savebranch) - if collapsef: + rbsrt.extrafns.insert(0, _savebranch) + if rbsrt.collapsef: branches = set() - for rev in state: + for rev in rbsrt.state: branches.add(repo[rev].branch()) if len(branches) > 1: raise error.Abort(_('cannot collapse multiple named ' 'branches')) # Rebase - if not targetancestors: - targetancestors = repo.changelog.ancestors([target], inclusive=True) + if not rbsrt.targetancestors: + rbsrt.targetancestors = repo.changelog.ancestors([rbsrt.target], + inclusive=True) # Keep track of the current bookmarks in order to reset them later currentbookmarks = repo._bookmarks.copy() - activebookmark = activebookmark or repo._activebookmark - if activebookmark: + rbsrt.activebookmark = rbsrt.activebookmark or repo._activebookmark + if rbsrt.activebookmark: bookmarks.deactivate(repo) - extrafn = _makeextrafn(extrafns) + extrafn = _makeextrafn(rbsrt.extrafns) - sortedstate = sorted(state) + sortedstate = sorted(rbsrt.state) total = len(sortedstate) pos = 0 for rev in sortedstate: @@ -385,37 +500,41 @@ if names: desc += ' (%s)' % ' '.join(names) pos += 1 - if state[rev] == revtodo: + if rbsrt.state[rev] == revtodo: ui.status(_('rebasing %s\n') % desc) ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)), _('changesets'), total) - p1, p2, base = defineparents(repo, rev, target, state, - targetancestors) - storestatus(repo, originalwd, target, state, collapsef, keepf, - keepbranchesf, external, activebookmark) - storecollapsemsg(repo, collapsemsg) + p1, p2, base = defineparents(repo, rev, rbsrt.target, + rbsrt.state, + rbsrt.targetancestors, + rbsrt.obsoletenotrebased) + storestatus(repo, rbsrt.originalwd, rbsrt.target, + rbsrt.state, rbsrt.collapsef, rbsrt.keepf, + rbsrt.keepbranchesf, rbsrt.external, + rbsrt.activebookmark) + storecollapsemsg(repo, rbsrt.collapsemsg) if len(repo[None].parents()) == 2: repo.ui.debug('resuming interrupted rebase\n') else: try: ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'rebase') - stats = rebasenode(repo, rev, p1, base, state, - collapsef, target) + stats = rebasenode(repo, rev, p1, base, rbsrt.state, + rbsrt.collapsef, rbsrt.target) if stats and stats[3] > 0: raise error.InterventionRequired( _('unresolved conflicts (see hg ' 'resolve, then hg rebase --continue)')) finally: ui.setconfig('ui', 'forcemerge', '', 'rebase') - if not collapsef: + if not rbsrt.collapsef: merging = p2 != nullrev editform = cmdutil.mergeeditform(merging, 'rebase') editor = cmdutil.getcommiteditor(editform=editform, **opts) newnode = concludenode(repo, rev, p1, p2, extrafn=extrafn, editor=editor, - keepbranches=keepbranchesf, - date=date) + keepbranches=rbsrt.keepbranchesf, + date=rbsrt.date) else: # Skip commit if we are collapsing repo.dirstate.beginparentchange() @@ -424,107 +543,117 @@ newnode = None # Update the state if newnode is not None: - state[rev] = repo[newnode].rev() + rbsrt.state[rev] = repo[newnode].rev() ui.debug('rebased as %s\n' % short(newnode)) else: - if not collapsef: + if not rbsrt.collapsef: ui.warn(_('note: rebase of %d:%s created no changes ' 'to commit\n') % (rev, ctx)) - skipped.add(rev) - state[rev] = p1 + rbsrt.skipped.add(rev) + rbsrt.state[rev] = p1 ui.debug('next revision set to %s\n' % p1) - elif state[rev] == nullmerge: + elif rbsrt.state[rev] == nullmerge: ui.debug('ignoring null merge rebase of %s\n' % rev) - elif state[rev] == revignored: + elif rbsrt.state[rev] == revignored: ui.status(_('not rebasing ignored %s\n') % desc) - elif state[rev] == revprecursor: - targetctx = repo[obsoletenotrebased[rev]] + elif rbsrt.state[rev] == revprecursor: + targetctx = repo[rbsrt.obsoletenotrebased[rev]] desctarget = '%d:%s "%s"' % (targetctx.rev(), targetctx, targetctx.description().split('\n', 1)[0]) msg = _('note: not rebasing %s, already in destination as %s\n') ui.status(msg % (desc, desctarget)) - elif state[rev] == revpruned: + elif rbsrt.state[rev] == revpruned: msg = _('note: not rebasing %s, it has no successor\n') ui.status(msg % desc) else: ui.status(_('already rebased %s as %s\n') % - (desc, repo[state[rev]])) + (desc, repo[rbsrt.state[rev]])) ui.progress(_('rebasing'), None) ui.note(_('rebase merging completed\n')) - if collapsef and not keepopen: - p1, p2, _base = defineparents(repo, min(state), target, - state, targetancestors) + if rbsrt.collapsef and not rbsrt.keepopen: + p1, p2, _base = defineparents(repo, min(rbsrt.state), + rbsrt.target, rbsrt.state, + rbsrt.targetancestors, + rbsrt.obsoletenotrebased) editopt = opts.get('edit') editform = 'rebase.collapse' - if collapsemsg: - commitmsg = collapsemsg + if rbsrt.collapsemsg: + commitmsg = rbsrt.collapsemsg else: commitmsg = 'Collapsed revision' - for rebased in state: - if rebased not in skipped and state[rebased] > nullmerge: + for rebased in rbsrt.state: + if rebased not in rbsrt.skipped and\ + rbsrt.state[rebased] > nullmerge: commitmsg += '\n* %s' % repo[rebased].description() editopt = True editor = cmdutil.getcommiteditor(edit=editopt, editform=editform) - newnode = concludenode(repo, rev, p1, external, commitmsg=commitmsg, + newnode = concludenode(repo, rev, p1, rbsrt.external, + commitmsg=commitmsg, extrafn=extrafn, editor=editor, - keepbranches=keepbranchesf, - date=date) + keepbranches=rbsrt.keepbranchesf, + date=rbsrt.date) if newnode is None: - newrev = target + newrev = rbsrt.target else: newrev = repo[newnode].rev() - for oldrev in state.iterkeys(): - if state[oldrev] > nullmerge: - state[oldrev] = newrev + for oldrev in rbsrt.state.iterkeys(): + if rbsrt.state[oldrev] > nullmerge: + rbsrt.state[oldrev] = newrev if 'qtip' in repo.tags(): - updatemq(repo, state, skipped, **opts) + updatemq(repo, rbsrt.state, rbsrt.skipped, **opts) if currentbookmarks: # Nodeids are needed to reset bookmarks nstate = {} - for k, v in state.iteritems(): + for k, v in rbsrt.state.iteritems(): if v > nullmerge: nstate[repo[k].node()] = repo[v].node() + elif v == revprecursor: + succ = rbsrt.obsoletenotrebased[k] + nstate[repo[k].node()] = repo[succ].node() # XXX this is the same as dest.node() for the non-continue path -- # this should probably be cleaned up - targetnode = repo[target].node() + targetnode = repo[rbsrt.target].node() # restore original working directory # (we do this before stripping) - newwd = state.get(originalwd, originalwd) - if newwd < 0: + newwd = rbsrt.state.get(rbsrt.originalwd, rbsrt.originalwd) + if newwd == revprecursor: + newwd = rbsrt.obsoletenotrebased[rbsrt.originalwd] + elif newwd < 0: # original directory is a parent of rebase set root or ignored - newwd = originalwd + newwd = rbsrt.originalwd if newwd not in [c.rev() for c in repo[None].parents()]: ui.note(_("update back to initial working directory parent\n")) hg.updaterepo(repo, newwd, False) - if not keepf: + if not rbsrt.keepf: collapsedas = None - if collapsef: + if rbsrt.collapsef: collapsedas = newnode - clearrebased(ui, repo, state, skipped, collapsedas) + clearrebased(ui, repo, rbsrt.state, rbsrt.skipped, collapsedas) with repo.transaction('bookmark') as tr: if currentbookmarks: updatebookmarks(repo, targetnode, nstate, currentbookmarks, tr) - if activebookmark not in repo._bookmarks: + if rbsrt.activebookmark not in repo._bookmarks: # active bookmark was divergent one and has been deleted - activebookmark = None + rbsrt.activebookmark = None clearstatus(repo) clearcollapsemsg(repo) ui.note(_("rebase completed\n")) util.unlinkpath(repo.sjoin('undo'), ignoremissing=True) - if skipped: - ui.note(_("%d revisions have been skipped\n") % len(skipped)) + if rbsrt.skipped: + skippedlen = len(rbsrt.skipped) + ui.note(_("%d revisions have been skipped\n") % skippedlen) - if (activebookmark and - repo['.'].node() == repo._bookmarks[activebookmark]): - bookmarks.activate(repo, activebookmark) + if (rbsrt.activebookmark and + repo['.'].node() == repo._bookmarks[rbsrt.activebookmark]): + bookmarks.activate(repo, rbsrt.activebookmark) finally: release(lock, wlock) @@ -733,21 +862,12 @@ "experimental.allowdivergence=True") raise error.Abort(msg % (",".join(divhashes),), hint=h) - # - plain prune (no successor) changesets are rebased - # - split changesets are not rebased if at least one of the - # changeset resulting from the split is an ancestor of dest - rebaseset = rebasesetrevs - rebaseobsskipped - if rebasesetrevs and not rebaseset: - msg = _('all requested changesets have equivalents ' - 'or were marked as obsolete') - hint = _('to force the rebase, set the config ' - 'experimental.rebaseskipobsolete to False') - raise error.Abort(msg, hint=hint) - -def defineparents(repo, rev, target, state, targetancestors): +def defineparents(repo, rev, target, state, targetancestors, + obsoletenotrebased): 'Return the new parent relationship of the revision that will be rebased' parents = repo[rev].parents() p1 = p2 = nullrev + rp1 = None p1n = parents[0].rev() if p1n in targetancestors: @@ -771,6 +891,8 @@ if p2n in state: if p1 == target: # p1n in targetancestors or external p1 = state[p2n] + if p1 == revprecursor: + rp1 = obsoletenotrebased[p2n] elif state[p2n] in revskipped: p2 = nearestrebased(repo, p2n, state) if p2 is None: @@ -784,7 +906,7 @@ 'would have 3 parents') % rev) p2 = p2n repo.ui.debug(" future parents are %d and %d\n" % - (repo[p1].rev(), repo[p2].rev())) + (repo[rp1 or p1].rev(), repo[p2].rev())) if not any(p.rev() in state for p in parents): # Case (1) root changeset of a non-detaching rebase set. @@ -828,6 +950,8 @@ # make it feasible to consider different cases separately. In these # other cases we currently just leave it to the user to correctly # resolve an impossible merge using a wrong ancestor. + # + # xx, p1 could be -4, and both parents could probably be -4... for p in repo[rev].parents(): if state.get(p.rev()) == p1: base = p.rev() @@ -838,7 +962,7 @@ # Raise because this function is called wrong (see issue 4106) raise AssertionError('no base found to rebase on ' '(defineparents called wrong)') - return p1, p2, base + return rp1 or p1, p2, base def isagitpatch(repo, patchname): 'Return true if the given patch is in git format' @@ -952,68 +1076,6 @@ _clearrebasesetvisibiliy(repo) util.unlinkpath(repo.join("rebasestate"), ignoremissing=True) -def restorestatus(repo): - 'Restore a previously stored status' - keepbranches = None - target = None - collapse = False - external = nullrev - activebookmark = None - state = {} - - try: - f = repo.vfs("rebasestate") - for i, l in enumerate(f.read().splitlines()): - if i == 0: - originalwd = repo[l].rev() - elif i == 1: - target = repo[l].rev() - elif i == 2: - external = repo[l].rev() - elif i == 3: - collapse = bool(int(l)) - elif i == 4: - keep = bool(int(l)) - elif i == 5: - keepbranches = bool(int(l)) - elif i == 6 and not (len(l) == 81 and ':' in l): - # line 6 is a recent addition, so for backwards compatibility - # check that the line doesn't look like the oldrev:newrev lines - activebookmark = l - else: - oldrev, newrev = l.split(':') - if newrev in (str(nullmerge), str(revignored), - str(revprecursor), str(revpruned)): - state[repo[oldrev].rev()] = int(newrev) - elif newrev == nullid: - state[repo[oldrev].rev()] = revtodo - # Legacy compat special case - else: - state[repo[oldrev].rev()] = repo[newrev].rev() - - except IOError as err: - if err.errno != errno.ENOENT: - raise - cmdutil.wrongtooltocontinue(repo, _('rebase')) - - if keepbranches is None: - raise error.Abort(_('.hg/rebasestate is incomplete')) - - skipped = set() - # recompute the set of skipped revs - if not collapse: - seen = set([target]) - for old, new in sorted(state.items()): - if new != revtodo and new in seen: - skipped.add(old) - seen.add(new) - repo.ui.debug('computed skipped revs: %s\n' % - (' '.join(str(r) for r in sorted(skipped)) or None)) - repo.ui.debug('rebase status resumed\n') - _setrebasesetvisibility(repo, state.keys()) - return (originalwd, target, state, skipped, - collapse, keep, keepbranches, external, activebookmark) - def needupdate(repo, state): '''check whether we should `update --clean` away from a merge, or if somehow the working dir got forcibly updated, e.g. by older hg''' @@ -1336,7 +1398,9 @@ if not os.path.exists(repo.join('rebasestate')): return try: - state = restorestatus(repo)[2] + rbsrt = rebaseruntime(repo, ui, {}) + rbsrt.restorestatus() + state = rbsrt.state except error.RepoLookupError: # i18n: column positioning for "hg summary" msg = _('rebase: (use "hg rebase --abort" to clear broken state)\n')
--- a/hgext/record.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/record.py Fri Jul 01 16:02:56 2016 -0500 @@ -12,13 +12,13 @@ from __future__ import absolute_import +from mercurial.i18n import _ from mercurial import ( cmdutil, commands, error, extensions, ) -from mercurial.i18n import _ cmdtable = {} command = cmdutil.command(cmdtable)
--- a/hgext/relink.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/relink.py Fri Jul 01 16:02:56 2016 -0500 @@ -11,13 +11,13 @@ import os import stat +from mercurial.i18n import _ from mercurial import ( cmdutil, error, hg, util, ) -from mercurial.i18n import _ cmdtable = {} command = cmdutil.command(cmdtable)
--- a/hgext/schemes.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/schemes.py Fri Jul 01 16:02:56 2016 -0500 @@ -43,6 +43,8 @@ import os import re + +from mercurial.i18n import _ from mercurial import ( cmdutil, error, @@ -51,7 +53,6 @@ templater, util, ) -from mercurial.i18n import _ cmdtable = {} command = cmdutil.command(cmdtable)
--- a/hgext/share.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/share.py Fri Jul 01 16:02:56 2016 -0500 @@ -135,7 +135,7 @@ if inst.errno != errno.ENOENT: raise return False - return 'bookmarks' in shared + return hg.sharedbookmarks in shared def _getsrcrepo(repo): """
--- a/hgext/shelve.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/shelve.py Fri Jul 01 16:02:56 2016 -0500 @@ -25,6 +25,8 @@ import collections import errno import itertools + +from mercurial.i18n import _ from mercurial import ( bundle2, bundlerepo, @@ -45,7 +47,6 @@ templatefilters, util, ) -from mercurial.i18n import _ from . import ( rebase, @@ -225,28 +226,10 @@ def _aborttransaction(repo): '''Abort current transaction for shelve/unshelve, but keep dirstate ''' - backupname = 'dirstate.shelve' - dirstatebackup = None - try: - # create backup of (un)shelved dirstate, because aborting transaction - # should restore dirstate to one at the beginning of the - # transaction, which doesn't include the result of (un)shelving - fp = repo.vfs.open(backupname, "w") - dirstatebackup = backupname - # clearing _dirty/_dirtypl of dirstate by _writedirstate below - # is unintentional. but it doesn't cause problem in this case, - # because no code path refers them until transaction is aborted. - repo.dirstate._writedirstate(fp) # write in-memory changes forcibly - - tr = repo.currenttransaction() - tr.abort() - - # restore to backuped dirstate - repo.vfs.rename(dirstatebackup, 'dirstate') - dirstatebackup = None - finally: - if dirstatebackup: - repo.vfs.unlink(dirstatebackup) + tr = repo.currenttransaction() + repo.dirstate.savebackup(tr, suffix='.shelve') + tr.abort() + repo.dirstate.restorebackup(None, suffix='.shelve') def createcmd(ui, repo, pats, opts): """subcommand that creates a new shelve"""
--- a/hgext/strip.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/strip.py Fri Jul 01 16:02:56 2016 -0500 @@ -5,6 +5,7 @@ """ from __future__ import absolute_import +from mercurial.i18n import _ from mercurial import ( bookmarks as bookmarksmod, cmdutil, @@ -17,7 +18,6 @@ scmutil, util, ) -from mercurial.i18n import _ nullid = nodemod.nullid release = lockmod.release
--- a/hgext/win32mbcs.py Fri Jul 01 15:12:33 2016 -0500 +++ b/hgext/win32mbcs.py Fri Jul 01 16:02:56 2016 -0500 @@ -49,11 +49,11 @@ import os import sys +from mercurial.i18n import _ from mercurial import ( encoding, error, ) -from mercurial.i18n import _ # Note for extension authors: ONLY specify testedwith = 'internal' for # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should @@ -192,5 +192,5 @@ # command line options is not yet applied when # extensions.loadall() is called. if '--debug' in sys.argv: - ui.write("[win32mbcs] activated with encoding: %s\n" + ui.write(("[win32mbcs] activated with encoding: %s\n") % _encoding)
--- a/i18n/hggettext Fri Jul 01 15:12:33 2016 -0500 +++ b/i18n/hggettext Fri Jul 01 16:02:56 2016 -0500 @@ -20,7 +20,11 @@ join the message cataloges to get the final catalog. """ -import os, sys, inspect +from __future__ import absolute_import, print_function + +import inspect +import os +import sys def escape(s): @@ -95,7 +99,7 @@ if mod.__doc__: src = open(path).read() lineno = 1 + offset(src, mod.__doc__, path, 7) - print poentry(path, lineno, mod.__doc__) + print(poentry(path, lineno, mod.__doc__)) functions = list(getattr(mod, 'i18nfunctions', [])) functions = [(f, True) for f in functions] @@ -115,12 +119,12 @@ if rstrip: doc = doc.rstrip() lineno += offset(src, doc, name, 1) - print poentry(path, lineno, doc) + print(poentry(path, lineno, doc)) def rawtext(path): src = open(path).read() - print poentry(path, 1, src) + print(poentry(path, 1, src)) if __name__ == "__main__":
--- a/i18n/posplit Fri Jul 01 15:12:33 2016 -0500 +++ b/i18n/posplit Fri Jul 01 16:02:56 2016 -0500 @@ -5,9 +5,11 @@ # license: MIT/X11/Expat # +from __future__ import absolute_import, print_function + +import polib import re import sys -import polib def addentry(po, entry, cache): e = cache.get(entry.msgid) @@ -67,8 +69,8 @@ continue else: # lines following directly, unexpected - print 'Warning: text follows line with directive' \ - ' %s' % directive + print('Warning: text follows line with directive' \ + ' %s' % directive) comment = 'do not translate: .. %s::' % directive if not newentry.comment: newentry.comment = comment
--- a/mercurial/__init__.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/__init__.py Fri Jul 01 16:02:56 2016 -0500 @@ -12,36 +12,13 @@ import sys import zipimport +from . import ( + policy +) + __all__ = [] -# Rules for how modules can be loaded. Values are: -# -# c - require C extensions -# allow - allow pure Python implementation when C loading fails -# py - only load pure Python modules -# -# By default, require the C extensions for performance reasons. -modulepolicy = 'c' -try: - from . import __modulepolicy__ - modulepolicy = __modulepolicy__.modulepolicy -except ImportError: - pass - -# PyPy doesn't load C extensions. -# -# The canonical way to do this is to test platform.python_implementation(). -# But we don't import platform and don't bloat for it here. -if '__pypy__' in sys.builtin_module_names: - modulepolicy = 'py' - -# Our C extensions aren't yet compatible with Python 3. So use pure Python -# on Python 3 for now. -if sys.version_info[0] >= 3: - modulepolicy = 'py' - -# Environment variable can always force settings. -modulepolicy = os.environ.get('HGMODULEPOLICY', modulepolicy) +modulepolicy = policy.policy # Modules that have both Python and C implementations. See also the # set of .py files under mercurial/pure/.
--- a/mercurial/ancestor.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/ancestor.py Fri Jul 01 16:02:56 2016 -0500 @@ -291,7 +291,7 @@ def __nonzero__(self): """False if the set is empty, True otherwise.""" try: - iter(self).next() + next(iter(self)) return True except StopIteration: return False
--- a/mercurial/bdiff.c Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/bdiff.c Fri Jul 01 16:02:56 2016 -0500 @@ -16,6 +16,7 @@ #include <limits.h> #include "util.h" +#include "bitmanipulation.h" struct line { int hash, n, e;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/bitmanipulation.h Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,53 @@ +#ifndef _HG_BITMANIPULATION_H_ +#define _HG_BITMANIPULATION_H_ + +#include "compat.h" + +static inline uint32_t getbe32(const char *c) +{ + const unsigned char *d = (const unsigned char *)c; + + return ((d[0] << 24) | + (d[1] << 16) | + (d[2] << 8) | + (d[3])); +} + +static inline int16_t getbeint16(const char *c) +{ + const unsigned char *d = (const unsigned char *)c; + + return ((d[0] << 8) | + (d[1])); +} + +static inline uint16_t getbeuint16(const char *c) +{ + const unsigned char *d = (const unsigned char *)c; + + return ((d[0] << 8) | + (d[1])); +} + +static inline void putbe32(uint32_t x, char *c) +{ + c[0] = (x >> 24) & 0xff; + c[1] = (x >> 16) & 0xff; + c[2] = (x >> 8) & 0xff; + c[3] = (x) & 0xff; +} + +static inline double getbefloat64(const char *c) +{ + const unsigned char *d = (const unsigned char *)c; + double ret; + int i; + uint64_t t = 0; + for (i = 0; i < 8; i++) { + t = (t<<8) + d[i]; + } + memcpy(&ret, &t, sizeof(t)); + return ret; +} + +#endif
--- a/mercurial/bookmarks.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/bookmarks.py Fri Jul 01 16:02:56 2016 -0500 @@ -17,6 +17,7 @@ ) from . import ( encoding, + error, lock as lockmod, obsolete, util, @@ -109,39 +110,6 @@ location='plain') tr.hookargs['bookmark_moved'] = '1' - def write(self): - '''Write bookmarks - - Write the given bookmark => hash dictionary to the .hg/bookmarks file - in a format equal to those of localtags. - - We also store a backup of the previous state in undo.bookmarks that - can be copied back on rollback. - ''' - msg = 'bm.write() is deprecated, use bm.recordchange(transaction)' - self._repo.ui.deprecwarn(msg, '3.7') - # TODO: writing the active bookmark should probably also use a - # transaction. - self._writeactive() - if self._clean: - return - repo = self._repo - if (repo.ui.configbool('devel', 'all-warnings') - or repo.ui.configbool('devel', 'check-locks')): - l = repo._wlockref and repo._wlockref() - if l is None or not l.held: - repo.ui.develwarn('bookmarks write with no wlock') - - tr = repo.currenttransaction() - if tr: - self.recordchange(tr) - # invalidatevolatilesets() is omitted because this doesn't - # write changes out actually - return - - self._writerepo(repo) - repo.invalidatevolatilesets() - def _writerepo(self, repo): """Factored out for extensibility""" rbm = repo._bookmarks @@ -150,7 +118,8 @@ rbm._writeactive() with repo.wlock(): - file_ = repo.vfs('bookmarks', 'w', atomictemp=True) + file_ = repo.vfs('bookmarks', 'w', atomictemp=True, + checkambig=True) try: self._write(file_) except: # re-raises @@ -164,7 +133,8 @@ return with self._repo.wlock(): if self._active is not None: - f = self._repo.vfs('bookmarks.current', 'w', atomictemp=True) + f = self._repo.vfs('bookmarks.current', 'w', atomictemp=True, + checkambig=True) try: f.write(encoding.fromlocal(self._active)) finally: @@ -185,7 +155,10 @@ def expandname(self, bname): if bname == '.': - return self.active + if self.active: + return self.active + else: + raise error.Abort(_("no active bookmark")) return bname def _readactive(repo, marks):
--- a/mercurial/branchmap.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/branchmap.py Fri Jul 01 16:02:56 2016 -0500 @@ -363,7 +363,7 @@ bndata = repo.vfs.read(_rbcnames) self._rbcsnameslen = len(bndata) # for verification before writing self._names = [encoding.tolocal(bn) for bn in bndata.split('\0')] - except (IOError, OSError) as inst: + except (IOError, OSError): if readonly: # don't try to use cache - fall back to the slow path self.branchinfo = self._branchinfo
--- a/mercurial/bundlerepo.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/bundlerepo.py Fri Jul 01 16:02:56 2016 -0500 @@ -291,7 +291,7 @@ ".cg%sun" % version) if cgstream is None: - raise error.Abort('No changegroups found') + raise error.Abort(_('No changegroups found')) cgstream.seek(0) self.bundle = changegroup.getunbundler(version, cgstream, 'UN')
--- a/mercurial/changegroup.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/changegroup.py Fri Jul 01 16:02:56 2016 -0500 @@ -530,6 +530,17 @@ def fileheader(self, fname): return chunkheader(len(fname)) + fname + # Extracted both for clarity and for overriding in extensions. + def _sortgroup(self, revlog, nodelist, lookup): + """Sort nodes for change group and turn them into revnums.""" + # for generaldelta revlogs, we linearize the revs; this will both be + # much quicker and generate a much smaller bundle + if (revlog._generaldelta and self._reorder is None) or self._reorder: + dag = dagutil.revlogdag(revlog) + return dag.linearize(set(revlog.rev(n) for n in nodelist)) + else: + return sorted([revlog.rev(n) for n in nodelist]) + def group(self, nodelist, revlog, lookup, units=None): """Calculate a delta group, yielding a sequence of changegroup chunks (strings). @@ -549,14 +560,7 @@ yield self.close() return - # for generaldelta revlogs, we linearize the revs; this will both be - # much quicker and generate a much smaller bundle - if (revlog._generaldelta and self._reorder is None) or self._reorder: - dag = dagutil.revlogdag(revlog) - revs = set(revlog.rev(n) for n in nodelist) - revs = dag.linearize(revs) - else: - revs = sorted([revlog.rev(n) for n in nodelist]) + revs = self._sortgroup(revlog, nodelist, lookup) # add the parent of the first rev p = revlog.parentrevs(revs[0])[0] @@ -724,10 +728,11 @@ dir = min(tmfnodes) nodes = tmfnodes[dir] prunednodes = self.prune(dirlog(dir), nodes, commonrevs) - for x in self._packmanifests(dir, prunednodes, - makelookupmflinknode(dir)): - size += len(x) - yield x + if not dir or prunednodes: + for x in self._packmanifests(dir, prunednodes, + makelookupmflinknode(dir)): + size += len(x) + yield x del tmfnodes[dir] self._verbosenote(_('%8.i (manifests)\n') % size) yield self._manifestsdone()
--- a/mercurial/cmdutil.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/cmdutil.py Fri Jul 01 16:02:56 2016 -0500 @@ -83,7 +83,7 @@ else: recordfn = crecordmod.chunkselector - return crecordmod.filterpatch(ui, originalhunks, recordfn, operation) + return crecordmod.filterpatch(ui, originalhunks, recordfn) else: return patch.filterpatch(ui, originalhunks, operation) @@ -91,9 +91,9 @@ def recordfilter(ui, originalhunks, operation=None): """ Prompts the user to filter the originalhunks and return a list of selected hunks. - *operation* is used for ui purposes to indicate the user - what kind of filtering they are doing: reverting, committing, shelving, etc. - *operation* has to be a translated string. + *operation* is used for to build ui messages to indicate the user what + kind of filtering they are doing: reverting, committing, shelving, etc. + (see patch.filterpatch). """ usecurses = crecordmod.checkcurses(ui) testfile = ui.config('experimental', 'crecordtest', None) @@ -532,7 +532,7 @@ msg = _('cannot specify --changelog and --manifest at the same time') elif cl and dir: msg = _('cannot specify --changelog and --dir at the same time') - elif cl or mf: + elif cl or mf or dir: if file_: msg = _('cannot specify filename with --changelog or --manifest') elif not repo: @@ -549,7 +549,7 @@ if 'treemanifest' not in repo.requirements: raise error.Abort(_("--dir can only be used on repos with " "treemanifest enabled")) - dirlog = repo.dirlog(file_) + dirlog = repo.dirlog(dir) if len(dirlog): r = dirlog elif mf: @@ -1405,24 +1405,24 @@ self.ui.write(",\n {") if self.ui.quiet: - self.ui.write('\n "rev": %s' % jrev) - self.ui.write(',\n "node": %s' % jnode) + self.ui.write(('\n "rev": %s') % jrev) + self.ui.write((',\n "node": %s') % jnode) self.ui.write('\n }') return - self.ui.write('\n "rev": %s' % jrev) - self.ui.write(',\n "node": %s' % jnode) - self.ui.write(',\n "branch": "%s"' % j(ctx.branch())) - self.ui.write(',\n "phase": "%s"' % ctx.phasestr()) - self.ui.write(',\n "user": "%s"' % j(ctx.user())) - self.ui.write(',\n "date": [%d, %d]' % ctx.date()) - self.ui.write(',\n "desc": "%s"' % j(ctx.description())) - - self.ui.write(',\n "bookmarks": [%s]' % + self.ui.write(('\n "rev": %s') % jrev) + self.ui.write((',\n "node": %s') % jnode) + self.ui.write((',\n "branch": "%s"') % j(ctx.branch())) + self.ui.write((',\n "phase": "%s"') % ctx.phasestr()) + self.ui.write((',\n "user": "%s"') % j(ctx.user())) + self.ui.write((',\n "date": [%d, %d]') % ctx.date()) + self.ui.write((',\n "desc": "%s"') % j(ctx.description())) + + self.ui.write((',\n "bookmarks": [%s]') % ", ".join('"%s"' % j(b) for b in ctx.bookmarks())) - self.ui.write(',\n "tags": [%s]' % + self.ui.write((',\n "tags": [%s]') % ", ".join('"%s"' % j(t) for t in ctx.tags())) - self.ui.write(',\n "parents": [%s]' % + self.ui.write((',\n "parents": [%s]') % ", ".join('"%s"' % c.hex() for c in ctx.parents())) if self.ui.debugflag: @@ -1430,26 +1430,26 @@ jmanifestnode = 'null' else: jmanifestnode = '"%s"' % hex(ctx.manifestnode()) - self.ui.write(',\n "manifest": %s' % jmanifestnode) - - self.ui.write(',\n "extra": {%s}' % + self.ui.write((',\n "manifest": %s') % jmanifestnode) + + self.ui.write((',\n "extra": {%s}') % ", ".join('"%s": "%s"' % (j(k), j(v)) for k, v in ctx.extra().items())) files = ctx.p1().status(ctx) - self.ui.write(',\n "modified": [%s]' % + self.ui.write((',\n "modified": [%s]') % ", ".join('"%s"' % j(f) for f in files[0])) - self.ui.write(',\n "added": [%s]' % + self.ui.write((',\n "added": [%s]') % ", ".join('"%s"' % j(f) for f in files[1])) - self.ui.write(',\n "removed": [%s]' % + self.ui.write((',\n "removed": [%s]') % ", ".join('"%s"' % j(f) for f in files[2])) elif self.ui.verbose: - self.ui.write(',\n "files": [%s]' % + self.ui.write((',\n "files": [%s]') % ", ".join('"%s"' % j(f) for f in ctx.files())) if copies: - self.ui.write(',\n "copies": {%s}' % + self.ui.write((',\n "copies": {%s}') % ", ".join('"%s": "%s"' % (j(k), j(v)) for k, v in copies)) @@ -1463,12 +1463,13 @@ self.ui.pushbuffer() diffordiffstat(self.ui, self.repo, diffopts, prev, node, match=matchfn, stat=True) - self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer())) + self.ui.write((',\n "diffstat": "%s"') + % j(self.ui.popbuffer())) if diff: self.ui.pushbuffer() diffordiffstat(self.ui, self.repo, diffopts, prev, node, match=matchfn, stat=False) - self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer())) + self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer())) self.ui.write("\n }") @@ -1998,7 +1999,7 @@ followfirst = 0 # --follow with FILE behavior depends on revs... it = iter(revs) - startrev = it.next() + startrev = next(it) followdescendants = startrev < next(it, startrev) # branch and only_branch are really aliases and must be handled at @@ -2147,7 +2148,8 @@ if opts.get('rev'): # User-specified revs might be unsorted, but don't sort before # _makelogrevset because it might depend on the order of revs - revs.sort(reverse=True) + if not (revs.isdescending() or revs.istopo()): + revs.sort(reverse=True) if expr: # Revset matchers often operate faster on revisions in changelog # order, because most filters deal with the changelog. @@ -3071,7 +3073,7 @@ # tell newly modified apart. dsmodified &= modified - dsmodified |= modified & dsadded # dirstate added may needs backup + dsmodified |= modified & dsadded # dirstate added may need backup modified -= dsmodified # We need to wait for some post-processing to update this set @@ -3301,10 +3303,12 @@ else: diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts) originalchunks = patch.parsepatch(diff) + operation = 'discard' if node == parent else 'revert' try: - chunks, opts = recordfilter(repo.ui, originalchunks) + chunks, opts = recordfilter(repo.ui, originalchunks, + operation=operation) if reversehunks: chunks = patch.reversehunks(chunks) @@ -3518,7 +3522,7 @@ def __init__(self, repo, name): self._repo = repo self._suffix = '.backup.%s.%d' % (name, id(self)) - repo.dirstate._savebackup(repo.currenttransaction(), self._suffix) + repo.dirstate.savebackup(repo.currenttransaction(), self._suffix) self._active = True self._closed = False @@ -3536,13 +3540,13 @@ % self._suffix) raise error.Abort(msg) - self._repo.dirstate._clearbackup(self._repo.currenttransaction(), + self._repo.dirstate.clearbackup(self._repo.currenttransaction(), self._suffix) self._active = False self._closed = True def _abort(self): - self._repo.dirstate._restorebackup(self._repo.currenttransaction(), + self._repo.dirstate.restorebackup(self._repo.currenttransaction(), self._suffix) self._active = False
--- a/mercurial/commands.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/commands.py Fri Jul 01 16:02:56 2016 -0500 @@ -59,6 +59,7 @@ obsolete, patch, phases, + policy, pvec, repair, revlog, @@ -215,7 +216,7 @@ debugrevlogopts = [ ('c', 'changelog', False, _('open changelog')), ('m', 'manifest', False, _('open manifest')), - ('', 'dir', False, _('open directory manifest')), + ('', 'dir', '', _('open directory manifest')), ] # Commands start here, listed alphabetically @@ -2089,51 +2090,56 @@ gen = exchange.readbundle(ui, f, bundlepath) if isinstance(gen, bundle2.unbundle20): return _debugbundle2(ui, gen, all=all, **opts) - if all: - ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n")) - - def showchunks(named): - ui.write("\n%s\n" % named) - chain = None - while True: - chunkdata = gen.deltachunk(chain) - if not chunkdata: - break - node = chunkdata['node'] - p1 = chunkdata['p1'] - p2 = chunkdata['p2'] - cs = chunkdata['cs'] - deltabase = chunkdata['deltabase'] - delta = chunkdata['delta'] - ui.write("%s %s %s %s %s %s\n" % - (hex(node), hex(p1), hex(p2), - hex(cs), hex(deltabase), len(delta))) - chain = node - - chunkdata = gen.changelogheader() - showchunks("changelog") - chunkdata = gen.manifestheader() - showchunks("manifest") - while True: - chunkdata = gen.filelogheader() - if not chunkdata: - break - fname = chunkdata['filename'] - showchunks(fname) - else: - if isinstance(gen, bundle2.unbundle20): - raise error.Abort(_('use debugbundle2 for this file')) - chunkdata = gen.changelogheader() + _debugchangegroup(ui, gen, all=all, **opts) + +def _debugchangegroup(ui, gen, all=None, indent=0, **opts): + indent_string = ' ' * indent + if all: + ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n") + % indent_string) + + def showchunks(named): + ui.write("\n%s%s\n" % (indent_string, named)) chain = None while True: chunkdata = gen.deltachunk(chain) if not chunkdata: break node = chunkdata['node'] - ui.write("%s\n" % hex(node)) + p1 = chunkdata['p1'] + p2 = chunkdata['p2'] + cs = chunkdata['cs'] + deltabase = chunkdata['deltabase'] + delta = chunkdata['delta'] + ui.write("%s%s %s %s %s %s %s\n" % + (indent_string, hex(node), hex(p1), hex(p2), + hex(cs), hex(deltabase), len(delta))) chain = node -def _debugbundle2(ui, gen, **opts): + chunkdata = gen.changelogheader() + showchunks("changelog") + chunkdata = gen.manifestheader() + showchunks("manifest") + while True: + chunkdata = gen.filelogheader() + if not chunkdata: + break + fname = chunkdata['filename'] + showchunks(fname) + else: + if isinstance(gen, bundle2.unbundle20): + raise error.Abort(_('use debugbundle2 for this file')) + chunkdata = gen.changelogheader() + chain = None + while True: + chunkdata = gen.deltachunk(chain) + if not chunkdata: + break + node = chunkdata['node'] + ui.write("%s%s\n" % (indent_string, hex(node))) + chain = node + +def _debugbundle2(ui, gen, all=None, **opts): """lists the contents of a bundle2""" if not isinstance(gen, bundle2.unbundle20): raise error.Abort(_('not a bundle2 file')) @@ -2143,15 +2149,7 @@ if part.type == 'changegroup': version = part.params.get('version', '01') cg = changegroup.getunbundler(version, part, 'UN') - chunkdata = cg.changelogheader() - chain = None - while True: - chunkdata = cg.deltachunk(chain) - if not chunkdata: - break - node = chunkdata['node'] - ui.write(" %s\n" % hex(node)) - chain = node + _debugchangegroup(ui, cg, all=all, indent=4, **opts) @command('debugcreatestreamclonebundle', [], 'FILE') def debugcreatestreamclonebundle(ui, repo, fname): @@ -2301,7 +2299,9 @@ @command('debugdata', debugrevlogopts, _('-c|-m|FILE REV')) def debugdata(ui, repo, file_, rev=None, **opts): """dump the contents of a data file revision""" - if opts.get('changelog') or opts.get('manifest'): + if opts.get('changelog') or opts.get('manifest') or opts.get('dir'): + if rev is not None: + raise error.CommandError('debugdata', _('invalid arguments')) file_, rev = None, file_ elif rev is None: raise error.CommandError('debugdata', _('invalid arguments')) @@ -2524,15 +2524,16 @@ break if ignored: if ignored == nf: - ui.write("%s is ignored\n" % f) + ui.write(_("%s is ignored\n") % f) else: - ui.write("%s is ignored because of containing folder %s\n" + ui.write(_("%s is ignored because of " + "containing folder %s\n") % (f, ignored)) ignorefile, lineno, line = ignoredata - ui.write("(ignore rule in %s, line %d: '%s')\n" + ui.write(_("(ignore rule in %s, line %d: '%s')\n") % (ignorefile, lineno, line)) else: - ui.write("%s is not ignored\n" % f) + ui.write(_("%s is not ignored\n") % f) @command('debugindex', debugrevlogopts + [('f', 'format', 0, _('revlog format'), _('FORMAT'))], @@ -2563,12 +2564,12 @@ break if format == 0: - ui.write(" rev offset length " + basehdr + " linkrev" - " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen))) + ui.write((" rev offset length " + basehdr + " linkrev" + " %s %s p2\n") % ("nodeid".ljust(idlen), "p1".ljust(idlen))) elif format == 1: - ui.write(" rev flag offset length" + ui.write((" rev flag offset length" " size " + basehdr + " link p1 p2" - " %s\n" % "nodeid".rjust(idlen)) + " %s\n") % "nodeid".rjust(idlen)) for i in r: node = r.node(i) @@ -2743,7 +2744,16 @@ fm.write('pythonlib', _("checking Python lib (%s)...\n"), os.path.dirname(os.__file__)) + # hg version + hgver = util.version() + fm.write('hgver', _("checking Mercurial version (%s)\n"), + hgver.split('+')[0]) + fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"), + '+'.join(hgver.split('+')[1:])) + # compiled modules + fm.write('hgmodulepolicy', _("checking module policy (%s)\n"), + policy.policy) fm.write('hgmodules', _("checking installed modules (%s)...\n"), os.path.dirname(__file__)) @@ -3022,13 +3032,13 @@ else: locker = 'user %s, process %s, host %s' \ % (user, pid, host) - ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age)) + ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age)) return 1 except OSError as e: if e.errno != errno.ENOENT: raise - ui.write("%-6s free\n" % (name + ":")) + ui.write(("%-6s free\n") % (name + ":")) return 0 held += report(repo.svfs, "lock", repo.lock) @@ -3321,8 +3331,8 @@ if opts.get("dump"): numrevs = len(r) - ui.write("# rev p1rev p2rev start end deltastart base p1 p2" - " rawsize totalsize compression heads chainlen\n") + ui.write(("# rev p1rev p2rev start end deltastart base p1 p2" + " rawsize totalsize compression heads chainlen\n")) ts = 0 heads = set() @@ -3511,18 +3521,19 @@ ui.note(revset.prettyformat(tree), "\n") newtree = revset.expandaliases(ui, tree) if newtree != tree: - ui.note("* expanded:\n", revset.prettyformat(newtree), "\n") + ui.note(("* expanded:\n"), revset.prettyformat(newtree), "\n") tree = newtree newtree = revset.foldconcat(tree) if newtree != tree: - ui.note("* concatenated:\n", revset.prettyformat(newtree), "\n") + ui.note(("* concatenated:\n"), revset.prettyformat(newtree), "\n") if opts["optimize"]: - weight, optimizedtree = revset.optimize(newtree, True) - ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n") + optimizedtree = revset.optimize(newtree) + ui.note(("* optimized:\n"), + revset.prettyformat(optimizedtree), "\n") func = revset.match(ui, expr, repo) revs = func(repo) if ui.verbose: - ui.note("* set:\n", revset.prettyformatset(revs), "\n") + ui.note(("* set:\n"), revset.prettyformatset(revs), "\n") for c in revs: ui.write("%s\n" % c) @@ -3677,7 +3688,7 @@ ui.note(templater.prettyformat(tree), '\n') newtree = templater.expandaliases(tree, aliases) if newtree != tree: - ui.note("* expanded:\n", templater.prettyformat(newtree), '\n') + ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n') mapfile = None if revs is None: @@ -4406,7 +4417,7 @@ if not opts.get('files_with_matches'): ui.write(sep, label='grep.sep') if not opts.get('text') and binary(): - ui.write(" Binary file matches") + ui.write(_(" Binary file matches")) else: for s, label in l: ui.write(s, label=label) @@ -4570,7 +4581,10 @@ Returns 0 if successful. """ - textwidth = min(ui.termwidth(), 80) - 2 + textwidth = ui.configint('ui', 'textwidth', 78) + termwidth = ui.termwidth() - 2 + if textwidth <= 0 or termwidth < textwidth: + textwidth = termwidth keep = opts.get('system') or [] if len(keep) == 0: @@ -5773,6 +5787,9 @@ If SOURCE is omitted, the 'default' path will be used. See :hg:`help urls` for more information. + Specifying bookmark as ``.`` is equivalent to specifying the active + bookmark's name. + Returns 0 on success, 1 if an update had unresolved files. """ source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch')) @@ -5794,6 +5811,7 @@ remotebookmarks = other.listkeys('bookmarks') pullopargs['remotebookmarks'] = remotebookmarks for b in opts['bookmark']: + b = repo._bookmarks.expandname(b) if b not in remotebookmarks: raise error.Abort(_('remote bookmark %s not found!') % b) revs.append(remotebookmarks[b]) @@ -5926,6 +5944,15 @@ if not revs: raise error.Abort(_("specified revisions evaluate to an empty set"), hint=_("use different revision arguments")) + elif path.pushrev: + # It doesn't make any sense to specify ancestor revisions. So limit + # to DAG heads to make discovery simpler. + expr = revset.formatspec('heads(%r)', path.pushrev) + revs = scmutil.revrange(repo, [expr]) + revs = [repo[rev].node() for rev in revs] + if not revs: + raise error.Abort(_('default push revset for path evaluates to an ' + 'empty set')) repo._subtoppath = dest try: @@ -6300,7 +6327,10 @@ related method. Modified files are saved with a .orig suffix before reverting. - To disable these backups, use --no-backup. + To disable these backups, use --no-backup. It is possible to store + the backup files in a custom directory relative to the root of the + repository by setting the ``ui.origbackuppath`` configuration + option. See :hg:`help dates` for a list of formats valid for -d/--date. @@ -6380,6 +6410,11 @@ commit transaction if it isn't checked out. Use --force to override this protection. + The rollback command can be entirely disabled by setting the + ``ui.rollback`` configuration setting to false. If you're here + because you want to use rollback and it's disabled, you can + re-enable the command by setting ``ui.rollback`` to true. + This command is not intended for use on public repositories. Once changes are visible for pull by other users, rolling a transaction back locally is ineffective (someone else may already have pulled @@ -6389,6 +6424,9 @@ Returns 0 on success, 1 if no rollback data is available. """ + if not ui.configbool('ui', 'rollback', True): + raise error.Abort(_('rollback is disabled because it is unsafe'), + hint=('see `hg help -v rollback` for information')) return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
--- a/mercurial/commandserver.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/commandserver.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,7 +7,6 @@ from __future__ import absolute_import -import SocketServer import errno import os import struct @@ -21,6 +20,8 @@ util, ) +socketserver = util.socketserver + logfile = None def log(*args): @@ -229,12 +230,8 @@ self.repo.ui = self.repo.dirstate._ui = repoui self.repo.invalidateall() - # reset last-print time of progress bar per command - # (progbar is singleton, we don't have to do for all uis) - if copiedui._progbar: - copiedui._progbar.resetstate() - for ui in uis: + ui.resetstate() # any kind of interaction must use server channels, but chg may # replace channels by fully functional tty files. so nontty is # enforced only if cin is a channel. @@ -334,7 +331,7 @@ finally: _restoreio(ui, fin, fout) -class _requesthandler(SocketServer.StreamRequestHandler): +class _requesthandler(socketserver.StreamRequestHandler): def handle(self): ui = self.server.ui repo = self.server.repo @@ -370,13 +367,13 @@ self.ui = ui self.repo = repo self.address = opts['address'] - if not util.safehasattr(SocketServer, 'UnixStreamServer'): + if not util.safehasattr(socketserver, 'UnixStreamServer'): raise error.Abort(_('unsupported platform')) if not self.address: raise error.Abort(_('no socket path specified with --address')) def init(self): - class cls(SocketServer.ForkingMixIn, SocketServer.UnixStreamServer): + class cls(socketserver.ForkingMixIn, socketserver.UnixStreamServer): ui = self.ui repo = self.repo self.server = cls(self.address, _requesthandler)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/compat.h Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,38 @@ +#ifndef _HG_COMPAT_H_ +#define _HG_COMPAT_H_ + +#ifdef _WIN32 +#ifdef _MSC_VER +/* msvc 6.0 has problems */ +#define inline __inline +typedef signed char int8_t; +typedef short int16_t; +typedef long int32_t; +typedef __int64 int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +typedef unsigned __int64 uint64_t; +#else +#include <stdint.h> +#endif +#else +/* not windows */ +#include <sys/types.h> +#if defined __BEOS__ && !defined __HAIKU__ +#include <ByteOrder.h> +#else +#include <arpa/inet.h> +#endif +#include <inttypes.h> +#endif + +#if defined __hpux || defined __SUNPRO_C || defined _AIX +#define inline +#endif + +#ifdef __linux +#define inline __inline +#endif + +#endif
--- a/mercurial/context.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/context.py Fri Jul 01 16:02:56 2016 -0500 @@ -930,16 +930,20 @@ this returns fixed value(False is used) as linenumber, if "linenumber" parameter is "False".''' + def lines(text): + if text.endswith("\n"): + return text.count("\n") + return text.count("\n") + 1 + if linenumber is None: def decorate(text, rev): - return ([rev] * len(text.splitlines()), text) + return ([rev] * lines(text), text) elif linenumber: def decorate(text, rev): - size = len(text.splitlines()) - return ([(rev, i) for i in xrange(1, size + 1)], text) + return ([(rev, i) for i in xrange(1, lines(text) + 1)], text) else: def decorate(text, rev): - return ([(rev, False)] * len(text.splitlines()), text) + return ([(rev, False)] * lines(text), text) def pair(parent, child): blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts,
--- a/mercurial/copies.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/copies.py Fri Jul 01 16:02:56 2016 -0500 @@ -484,16 +484,16 @@ f1r, f2r = f1.linkrev(), f2.linkrev() if f1r is None: - f1 = g1.next() + f1 = next(g1) if f2r is None: - f2 = g2.next() + f2 = next(g2) while True: f1r, f2r = f1.linkrev(), f2.linkrev() if f1r > f2r: - f1 = g1.next() + f1 = next(g1) elif f2r > f1r: - f2 = g2.next() + f2 = next(g2) elif f1 == f2: return f1 # a match elif f1r == f2r or f1r < limit or f2r < limit:
--- a/mercurial/crecord.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/crecord.py Fri Jul 01 16:02:56 2016 -0500 @@ -91,6 +91,7 @@ def allchildren(self): "Return a list of all of the direct children of this node" raise NotImplementedError("method must be implemented by subclass") + def nextsibling(self): """ Return the closest next item of the same type where there are no items @@ -110,18 +111,12 @@ def parentitem(self): raise NotImplementedError("method must be implemented by subclass") - - def nextitem(self, constrainlevel=True, skipfolded=True): + def nextitem(self, skipfolded=True): """ - If constrainLevel == True, return the closest next item - of the same type where there are no items of different types between - the current item and this closest item. + Try to return the next item closest to this item, regardless of item's + type (header, hunk, or hunkline). - If constrainLevel == False, then try to return the next item - closest to this item, regardless of item's type (header, hunk, or - HunkLine). - - If skipFolded == True, and the current item is folded, then the child + If skipfolded == True, and the current item is folded, then the child items that are hidden due to folding will be skipped when determining the next item. @@ -131,9 +126,7 @@ itemfolded = self.folded except AttributeError: itemfolded = False - if constrainlevel: - return self.nextsibling() - elif skipfolded and itemfolded: + if skipfolded and itemfolded: nextitem = self.nextsibling() if nextitem is None: try: @@ -164,43 +157,31 @@ except AttributeError: # parent and/or grandparent was None return None - def previtem(self, constrainlevel=True, skipfolded=True): + def previtem(self): """ - If constrainLevel == True, return the closest previous item - of the same type where there are no items of different types between - the current item and this closest item. - - If constrainLevel == False, then try to return the previous item - closest to this item, regardless of item's type (header, hunk, or - HunkLine). - - If skipFolded == True, and the current item is folded, then the items - that are hidden due to folding will be skipped when determining the - next item. + Try to return the previous item closest to this item, regardless of + item's type (header, hunk, or hunkline). If it is not possible to get the previous item, return None. """ - if constrainlevel: - return self.prevsibling() - else: - # try previous sibling's last child's last child, - # else try previous sibling's last child, else try previous sibling - prevsibling = self.prevsibling() - if prevsibling is not None: - prevsiblinglastchild = prevsibling.lastchild() - if ((prevsiblinglastchild is not None) and - not prevsibling.folded): - prevsiblinglclc = prevsiblinglastchild.lastchild() - if ((prevsiblinglclc is not None) and - not prevsiblinglastchild.folded): - return prevsiblinglclc - else: - return prevsiblinglastchild + # try previous sibling's last child's last child, + # else try previous sibling's last child, else try previous sibling + prevsibling = self.prevsibling() + if prevsibling is not None: + prevsiblinglastchild = prevsibling.lastchild() + if ((prevsiblinglastchild is not None) and + not prevsibling.folded): + prevsiblinglclc = prevsiblinglastchild.lastchild() + if ((prevsiblinglclc is not None) and + not prevsiblinglastchild.folded): + return prevsiblinglclc else: - return prevsibling + return prevsiblinglastchild + else: + return prevsibling - # try parent (or None) - return self.parentitem() + # try parent (or None) + return self.parentitem() class patch(patchnode, list): # todo: rename patchroot """ @@ -236,7 +217,6 @@ self.neverunfolded = True self.hunks = [uihunk(h, self) for h in self.hunks] - def prettystr(self): x = stringio() self.pretty(x) @@ -392,6 +372,7 @@ def allchildren(self): "return a list of all of the direct children of this node" return self.changedlines + def countchanges(self): """changedlines -> (n+,n-)""" add = len([l for l in self.changedlines if l.applied @@ -455,14 +436,12 @@ def __getattr__(self, name): return getattr(self._hunk, name) + def __repr__(self): return '<hunk %r@%d>' % (self.filename(), self.fromline) -def filterpatch(ui, chunks, chunkselector, operation=None): +def filterpatch(ui, chunks, chunkselector): """interactively filter patch chunks into applied-only chunks""" - - if operation is None: - operation = _('confirm') chunks = list(chunks) # convert chunks list into structure suitable for displaying/modifying # with curses. create a list of headers only. @@ -603,13 +582,10 @@ the last hunkline of the hunk prior to the selected hunk. or, if the first hunkline of a hunk is currently selected, then select the hunk itself. - - if the currently selected item is already at the top of the screen, - scroll the screen down to show the new-selected item. """ currentitem = self.currentselecteditem - nextitem = currentitem.previtem(constrainlevel=False) + nextitem = currentitem.previtem() if nextitem is None: # if no parent item (i.e. currentitem is the first header), then @@ -623,13 +599,10 @@ select (if possible) the previous item on the same level as the currently selected item. otherwise, select (if possible) the parent-item of the currently selected item. - - if the currently selected item is already at the top of the screen, - scroll the screen down to show the new-selected item. """ currentitem = self.currentselecteditem - nextitem = currentitem.previtem() - # if there's no previous item on this level, try choosing the parent + nextitem = currentitem.prevsibling() + # if there's no previous sibling, try choosing the parent if nextitem is None: nextitem = currentitem.parentitem() if nextitem is None: @@ -646,14 +619,11 @@ the first hunkline of the selected hunk. or, if the last hunkline of a hunk is currently selected, then select the next hunk, if one exists, or if not, the next header if one exists. - - if the currently selected item is already at the bottom of the screen, - scroll the screen up to show the new-selected item. """ #self.startprintline += 1 #debug currentitem = self.currentselecteditem - nextitem = currentitem.nextitem(constrainlevel=False) + nextitem = currentitem.nextitem() # if there's no next item, keep the selection as-is if nextitem is None: nextitem = currentitem @@ -662,24 +632,21 @@ def downarrowshiftevent(self): """ - if the cursor is already at the bottom chunk, scroll the screen up and - move the cursor-position to the subsequent chunk. otherwise, only move - the cursor position down one chunk. + select (if possible) the next item on the same level as the currently + selected item. otherwise, select (if possible) the next item on the + same level as the parent item of the currently selected item. """ - # todo: update docstring - currentitem = self.currentselecteditem - nextitem = currentitem.nextitem() - # if there's no previous item on this level, try choosing the parent's - # nextitem. + nextitem = currentitem.nextsibling() + # if there's no next sibling, try choosing the parent's nextsibling if nextitem is None: try: - nextitem = currentitem.parentitem().nextitem() + nextitem = currentitem.parentitem().nextsibling() except AttributeError: - # parentitem returned None, so nextitem() can't be called + # parentitem returned None, so nextsibling() can't be called nextitem = None if nextitem is None: - # if no next item on parent-level, then no change... + # if parent has no next sibling, then no change... nextitem = currentitem self.currentselecteditem = nextitem @@ -766,7 +733,6 @@ # negative values scroll in pgup direction self.scrolllines(selstart - padstartbuffered) - def scrolllines(self, numlines): "scroll the screen up (down) by numlines when numlines >0 (<0)." self.firstlineofpadtoprint += numlines @@ -894,7 +860,6 @@ if isinstance(item, (uiheader, uihunk)): item.folded = not item.folded - def alignstring(self, instr, window): """ add whitespace to the end of a string in order to make it fill @@ -1133,7 +1098,6 @@ lineprefix = " "*self.hunkindentnumchars + checkbox frtoline = " " + hunk.getfromtoline().strip("\n") - outstr += self.printstring(self.chunkpad, lineprefix, towin=towin, align=False) # add uncolored checkbox/indent outstr += self.printstring(self.chunkpad, frtoline, pair=colorpair, @@ -1377,7 +1341,7 @@ F : fold / unfold parent item and all of its ancestors m : edit / resume editing the commit message e : edit the currently selected hunk - a : toggle amend mode (hg rev >= 2.2), only with commit -i + a : toggle amend mode, only with commit -i c : confirm selected changes r : review/edit and confirm selected changes q : quit without confirming (no changes will be made)
--- a/mercurial/demandimport.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/demandimport.py Fri Jul 01 16:02:56 2016 -0500 @@ -188,15 +188,23 @@ if globalname and isinstance(symbol, _demandmod): symbol._addref(globalname) + def chainmodules(rootmod, modname): + # recurse down the module chain, and return the leaf module + mod = rootmod + for comp in modname.split('.')[1:]: + if getattr(mod, comp, nothing) is nothing: + setattr(mod, comp, + _demandmod(comp, mod.__dict__, mod.__dict__)) + mod = getattr(mod, comp) + return mod + if level >= 0: - # The "from a import b,c,d" or "from .a import b,c,d" - # syntax gives errors with some modules for unknown - # reasons. Work around the problem. if name: - return _hgextimport(_origimport, name, globals, locals, - fromlist, level) - - if _pypy: + # "from a import b" or "from .a import b" style + rootmod = _hgextimport(_origimport, name, globals, locals, + level=level) + mod = chainmodules(rootmod, name) + elif _pypy: # PyPy's __import__ throws an exception if invoked # with an empty name and no fromlist. Recreate the # desired behaviour by hand. @@ -220,12 +228,7 @@ # But, we still need to support lazy loading of standard library and 3rd # party modules. So handle level == -1. mod = _hgextimport(_origimport, name, globals, locals) - # recurse down the module chain - for comp in name.split('.')[1:]: - if getattr(mod, comp, nothing) is nothing: - setattr(mod, comp, - _demandmod(comp, mod.__dict__, mod.__dict__)) - mod = getattr(mod, comp) + mod = chainmodules(mod, name) for x in fromlist: processfromitem(mod, x)
--- a/mercurial/destutil.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/destutil.py Fri Jul 01 16:02:56 2016 -0500 @@ -95,6 +95,10 @@ wc = repo[None] movemark = node = None currentbranch = wc.branch() + + if clean: + currentbranch = repo['.'].branch() + if currentbranch in repo.branchmap(): heads = repo.branchheads(currentbranch) if heads:
--- a/mercurial/dirstate.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/dirstate.py Fri Jul 01 16:02:56 2016 -0500 @@ -74,6 +74,8 @@ raise return (vfs(filename), False) +_token = object() + class dirstate(object): def __init__(self, opener, ui, root, validate): @@ -365,7 +367,7 @@ def setbranch(self, branch): self._branch = encoding.fromlocal(branch) - f = self._opener('branch', 'w', atomictemp=True) + f = self._opener('branch', 'w', atomictemp=True, checkambig=True) try: f.write(self._branch + '\n') f.close() @@ -580,6 +582,8 @@ del self._map[f] if f in self._nonnormalset: self._nonnormalset.remove(f) + if f in self._copymap: + del self._copymap[f] def _discoverpath(self, path, normed, ignoremissing, exists, storemap): if exists is None: @@ -688,16 +692,15 @@ self._pl = (parent, nullid) self._dirty = True - def write(self, tr=False): + def write(self, tr=_token): if not self._dirty: return filename = self._filename - if tr is False: # not explicitly specified - if (self._ui.configbool('devel', 'all-warnings') - or self._ui.configbool('devel', 'check-dirstate-write')): - self._ui.develwarn('use dirstate.write with ' - 'repo.currenttransaction()') + if tr is _token: # not explicitly specified + self._ui.deprecwarn('use dirstate.write with ' + 'repo.currenttransaction()', + '3.9') if self._opener.lexists(self._pendingfilename): # if pending file already exists, in-memory changes @@ -727,7 +730,7 @@ self._writedirstate, location='plain') return - st = self._opener(filename, "w", atomictemp=True) + st = self._opener(filename, "w", atomictemp=True, checkambig=True) self._writedirstate(st) def _writedirstate(self, st): @@ -1206,14 +1209,16 @@ else: return self._filename - def _savebackup(self, tr, suffix): + def savebackup(self, tr, suffix='', prefix=''): '''Save current dirstate into backup file with suffix''' + assert len(suffix) > 0 or len(prefix) > 0 filename = self._actualfilename(tr) # use '_writedirstate' instead of 'write' to write changes certainly, # because the latter omits writing out if transaction is running. # output file will be used to create backup of dirstate at this point. - self._writedirstate(self._opener(filename, "w", atomictemp=True)) + self._writedirstate(self._opener(filename, "w", atomictemp=True, + checkambig=True)) if tr: # ensure that subsequent tr.writepending returns True for @@ -1227,17 +1232,22 @@ # end of this transaction tr.registertmp(filename, location='plain') - self._opener.write(filename + suffix, self._opener.tryread(filename)) + self._opener.write(prefix + self._filename + suffix, + self._opener.tryread(filename)) - def _restorebackup(self, tr, suffix): + def restorebackup(self, tr, suffix='', prefix=''): '''Restore dirstate by backup file with suffix''' + assert len(suffix) > 0 or len(prefix) > 0 # this "invalidate()" prevents "wlock.release()" from writing # changes of dirstate out after restoring from backup file self.invalidate() filename = self._actualfilename(tr) - self._opener.rename(filename + suffix, filename) + # using self._filename to avoid having "pending" in the backup filename + self._opener.rename(prefix + self._filename + suffix, filename, + checkambig=True) - def _clearbackup(self, tr, suffix): + def clearbackup(self, tr, suffix='', prefix=''): '''Clear backup file with suffix''' - filename = self._actualfilename(tr) - self._opener.unlink(filename + suffix) + assert len(suffix) > 0 or len(prefix) > 0 + # using self._filename to avoid having "pending" in the backup filename + self._opener.unlink(prefix + self._filename + suffix)
--- a/mercurial/dispatch.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/dispatch.py Fri Jul 01 16:02:56 2016 -0500 @@ -384,7 +384,7 @@ self.cmdname = '' self.definition = definition self.fn = None - self.args = [] + self.givenargs = [] self.opts = [] self.help = '' self.badalias = None @@ -432,7 +432,7 @@ % (self.name, inst)) return self.cmdname = cmd = args.pop(0) - args = map(util.expandpath, args) + self.givenargs = args for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"): if _earlygetopt([invalidarg], args): @@ -448,7 +448,6 @@ else: self.fn, self.opts = tableentry - self.args = aliasargs(self.fn, args) if self.help.startswith("hg " + cmd): # drop prefix in old-style help lines so hg shows the alias self.help = self.help[4 + len(cmd):] @@ -462,6 +461,11 @@ self.badalias = (_("alias '%s' resolves to ambiguous command '%s'") % (self.name, cmd)) + @property + def args(self): + args = map(util.expandpath, self.givenargs) + return aliasargs(self.fn, args) + def __getattr__(self, name): adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False} if name not in adefaults: @@ -629,10 +633,16 @@ # run pre-hook, and abort if it fails hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs), pats=cmdpats, opts=cmdoptions) - ret = _runcommand(ui, options, cmd, d) - # run post-hook, passing command result - hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs), - result=ret, pats=cmdpats, opts=cmdoptions) + try: + ret = _runcommand(ui, options, cmd, d) + # run post-hook, passing command result + hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs), + result=ret, pats=cmdpats, opts=cmdoptions) + except Exception: + # run failure hook and re-raise + hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs), + pats=cmdpats, opts=cmdoptions) + raise return ret def _getlocal(ui, rpath, wd=None): @@ -660,12 +670,8 @@ return path, lui -def _checkshellalias(lui, ui, args, precheck=True): - """Return the function to run the shell alias, if it is required - - 'precheck' is whether this function is invoked before adding - aliases or not. - """ +def _checkshellalias(lui, ui, args): + """Return the function to run the shell alias, if it is required""" options = {} try: @@ -676,16 +682,11 @@ if not args: return - if precheck: - strict = True - cmdtable = commands.table.copy() - addaliases(lui, cmdtable) - else: - strict = False - cmdtable = commands.table + cmdtable = commands.table cmd = args[0] try: + strict = ui.configbool("ui", "strict") aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict) except (error.AmbiguousCommand, error.UnknownCommand): return @@ -735,12 +736,6 @@ rpath = _earlygetopt(["-R", "--repository", "--repo"], args) path, lui = _getlocal(ui, rpath) - # Now that we're operating in the right directory/repository with - # the right config settings, check for shell aliases - shellaliasfn = _checkshellalias(lui, ui, args) - if shellaliasfn: - return shellaliasfn() - # Configure extensions in phases: uisetup, extsetup, cmdtable, and # reposetup. Programs like TortoiseHg will call _dispatch several # times so we keep track of configured extensions in _loaded. @@ -762,13 +757,11 @@ addaliases(lui, commands.table) - if not lui.configbool("ui", "strict"): - # All aliases and commands are completely defined, now. - # Check abbreviation/ambiguity of shell alias again, because shell - # alias may cause failure of "_parse" (see issue4355) - shellaliasfn = _checkshellalias(lui, ui, args, precheck=False) - if shellaliasfn: - return shellaliasfn() + # All aliases and commands are completely defined, now. + # Check abbreviation/ambiguity of shell alias. + shellaliasfn = _checkshellalias(lui, ui, args) + if shellaliasfn: + return shellaliasfn() # check for fallback encoding fallback = lui.config('ui', 'fallbackencoding') @@ -825,7 +818,7 @@ if cmdoptions.get('insecure', False): for ui_ in uis: - ui_.setconfig('web', 'cacerts', '!', '--insecure') + ui_.insecureconnections = True if options['version']: return commands.version_(ui)
--- a/mercurial/exchange.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/exchange.py Fri Jul 01 16:02:56 2016 -0500 @@ -8,6 +8,7 @@ from __future__ import absolute_import import errno +import hashlib from .i18n import _ from .node import ( @@ -857,14 +858,14 @@ try: reply = pushop.remote.unbundle(stream, ['force'], 'push') except error.BundleValueError as exc: - raise error.Abort('missing support for %s' % exc) + raise error.Abort(_('missing support for %s') % exc) try: trgetter = None if pushback: trgetter = pushop.trmanager.transaction op = bundle2.processbundle(pushop.repo, reply, trgetter) except error.BundleValueError as exc: - raise error.Abort('missing support for %s' % exc) + raise error.Abort(_('missing support for %s') % exc) except bundle2.AbortFromPart as exc: pushop.ui.status(_('remote: %s\n') % exc) raise error.Abort(_('push failed on remote'), hint=exc.hint) @@ -1055,7 +1056,8 @@ # revision we try to pull (None is "all") self.heads = heads # bookmark pulled explicitly - self.explicitbookmarks = bookmarks + self.explicitbookmarks = [repo._bookmarks.expandname(bookmark) + for bookmark in bookmarks] # do we force pull? self.force = force # whether a streaming clone was requested @@ -1323,7 +1325,7 @@ try: op = bundle2.processbundle(pullop.repo, bundle, pullop.gettransaction) except error.BundleValueError as exc: - raise error.Abort('missing support for %s' % exc) + raise error.Abort(_('missing support for %s') % exc) if pullop.fetch: results = [cg['return'] for cg in op.records['changegroup']] @@ -1646,7 +1648,7 @@ Used by peer for unbundling. """ heads = repo.heads() - heads_hash = util.sha1(''.join(sorted(heads))).digest() + heads_hash = hashlib.sha1(''.join(sorted(heads))).digest() if not (their_heads == ['force'] or their_heads == heads or their_heads == ['hashed', heads_hash]): # someone else committed/pushed/unbundled while we
--- a/mercurial/extensions.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/extensions.py Fri Jul 01 16:02:56 2016 -0500 @@ -25,7 +25,7 @@ _aftercallbacks = {} _order = [] _builtin = set(['hbisect', 'bookmarks', 'parentrevspec', 'progress', 'interhg', - 'inotify']) + 'inotify', 'hgcia']) def extensions(ui=None): if ui:
--- a/mercurial/formatter.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/formatter.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,7 +7,6 @@ from __future__ import absolute_import -import cPickle import os from .i18n import _ @@ -20,8 +19,11 @@ encoding, error, templater, + util, ) +pickle = util.pickle + class baseformatter(object): def __init__(self, ui, topic, opts): self._ui = ui @@ -107,7 +109,7 @@ self._data.append(self._item) def end(self): baseformatter.end(self) - self._ui.write(cPickle.dumps(self._data)) + self._ui.write(pickle.dumps(self._data)) def _jsonifyobj(v): if isinstance(v, tuple):
--- a/mercurial/graphmod.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/graphmod.py Fri Jul 01 16:02:56 2016 -0500 @@ -19,8 +19,6 @@ from __future__ import absolute_import -import heapq - from .node import nullrev from . import ( revset, @@ -32,207 +30,11 @@ GRANDPARENT = 'G' MISSINGPARENT = 'M' # Style of line to draw. None signals a line that ends and is removed at this -# point. +# point. A number prefix means only the last N characters of the current block +# will use that style, the rest will use the PARENT style. Add a - sign +# (so making N negative) and all but the first N characters use that style. EDGES = {PARENT: '|', GRANDPARENT: ':', MISSINGPARENT: None} -def groupbranchiter(revs, parentsfunc, firstbranch=()): - """Yield revisions from heads to roots one (topo) branch at a time. - - This function aims to be used by a graph generator that wishes to minimize - the number of parallel branches and their interleaving. - - Example iteration order (numbers show the "true" order in a changelog): - - o 4 - | - o 1 - | - | o 3 - | | - | o 2 - |/ - o 0 - - Note that the ancestors of merges are understood by the current - algorithm to be on the same branch. This means no reordering will - occur behind a merge. - """ - - ### Quick summary of the algorithm - # - # This function is based around a "retention" principle. We keep revisions - # in memory until we are ready to emit a whole branch that immediately - # "merges" into an existing one. This reduces the number of parallel - # branches with interleaved revisions. - # - # During iteration revs are split into two groups: - # A) revision already emitted - # B) revision in "retention". They are stored as different subgroups. - # - # for each REV, we do the following logic: - # - # 1) if REV is a parent of (A), we will emit it. If there is a - # retention group ((B) above) that is blocked on REV being - # available, we emit all the revisions out of that retention - # group first. - # - # 2) else, we'll search for a subgroup in (B) awaiting for REV to be - # available, if such subgroup exist, we add REV to it and the subgroup is - # now awaiting for REV.parents() to be available. - # - # 3) finally if no such group existed in (B), we create a new subgroup. - # - # - # To bootstrap the algorithm, we emit the tipmost revision (which - # puts it in group (A) from above). - - revs.sort(reverse=True) - - # Set of parents of revision that have been emitted. They can be considered - # unblocked as the graph generator is already aware of them so there is no - # need to delay the revisions that reference them. - # - # If someone wants to prioritize a branch over the others, pre-filling this - # set will force all other branches to wait until this branch is ready to be - # emitted. - unblocked = set(firstbranch) - - # list of groups waiting to be displayed, each group is defined by: - # - # (revs: lists of revs waiting to be displayed, - # blocked: set of that cannot be displayed before those in 'revs') - # - # The second value ('blocked') correspond to parents of any revision in the - # group ('revs') that is not itself contained in the group. The main idea - # of this algorithm is to delay as much as possible the emission of any - # revision. This means waiting for the moment we are about to display - # these parents to display the revs in a group. - # - # This first implementation is smart until it encounters a merge: it will - # emit revs as soon as any parent is about to be emitted and can grow an - # arbitrary number of revs in 'blocked'. In practice this mean we properly - # retains new branches but gives up on any special ordering for ancestors - # of merges. The implementation can be improved to handle this better. - # - # The first subgroup is special. It corresponds to all the revision that - # were already emitted. The 'revs' lists is expected to be empty and the - # 'blocked' set contains the parents revisions of already emitted revision. - # - # You could pre-seed the <parents> set of groups[0] to a specific - # changesets to select what the first emitted branch should be. - groups = [([], unblocked)] - pendingheap = [] - pendingset = set() - - heapq.heapify(pendingheap) - heappop = heapq.heappop - heappush = heapq.heappush - for currentrev in revs: - # Heap works with smallest element, we want highest so we invert - if currentrev not in pendingset: - heappush(pendingheap, -currentrev) - pendingset.add(currentrev) - # iterates on pending rev until after the current rev have been - # processed. - rev = None - while rev != currentrev: - rev = -heappop(pendingheap) - pendingset.remove(rev) - - # Seek for a subgroup blocked, waiting for the current revision. - matching = [i for i, g in enumerate(groups) if rev in g[1]] - - if matching: - # The main idea is to gather together all sets that are blocked - # on the same revision. - # - # Groups are merged when a common blocking ancestor is - # observed. For example, given two groups: - # - # revs [5, 4] waiting for 1 - # revs [3, 2] waiting for 1 - # - # These two groups will be merged when we process - # 1. In theory, we could have merged the groups when - # we added 2 to the group it is now in (we could have - # noticed the groups were both blocked on 1 then), but - # the way it works now makes the algorithm simpler. - # - # We also always keep the oldest subgroup first. We can - # probably improve the behavior by having the longest set - # first. That way, graph algorithms could minimise the length - # of parallel lines their drawing. This is currently not done. - targetidx = matching.pop(0) - trevs, tparents = groups[targetidx] - for i in matching: - gr = groups[i] - trevs.extend(gr[0]) - tparents |= gr[1] - # delete all merged subgroups (except the one we kept) - # (starting from the last subgroup for performance and - # sanity reasons) - for i in reversed(matching): - del groups[i] - else: - # This is a new head. We create a new subgroup for it. - targetidx = len(groups) - groups.append(([], set([rev]))) - - gr = groups[targetidx] - - # We now add the current nodes to this subgroups. This is done - # after the subgroup merging because all elements from a subgroup - # that relied on this rev must precede it. - # - # we also update the <parents> set to include the parents of the - # new nodes. - if rev == currentrev: # only display stuff in rev - gr[0].append(rev) - gr[1].remove(rev) - parents = [p for p in parentsfunc(rev) if p > nullrev] - gr[1].update(parents) - for p in parents: - if p not in pendingset: - pendingset.add(p) - heappush(pendingheap, -p) - - # Look for a subgroup to display - # - # When unblocked is empty (if clause), we were not waiting for any - # revisions during the first iteration (if no priority was given) or - # if we emitted a whole disconnected set of the graph (reached a - # root). In that case we arbitrarily take the oldest known - # subgroup. The heuristic could probably be better. - # - # Otherwise (elif clause) if the subgroup is blocked on - # a revision we just emitted, we can safely emit it as - # well. - if not unblocked: - if len(groups) > 1: # display other subset - targetidx = 1 - gr = groups[1] - elif not gr[1] & unblocked: - gr = None - - if gr is not None: - # update the set of awaited revisions with the one from the - # subgroup - unblocked |= gr[1] - # output all revisions in the subgroup - for r in gr[0]: - yield r - # delete the subgroup that you just output - # unless it is groups[0] in which case you just empty it. - if targetidx: - del groups[targetidx] - else: - gr[0][:] = [] - # Check if we have some subgroup waiting for revisions we are not going to - # iterate over - for g in groups: - for r in g[0]: - yield r - def dagwalker(repo, revs): """cset DAG generator yielding (id, CHANGESET, ctx, [parentinfo]) tuples @@ -250,16 +52,6 @@ gpcache = {} - if repo.ui.configbool('experimental', 'graph-group-branches', False): - firstbranch = () - firstbranchrevset = repo.ui.config( - 'experimental', 'graph-group-branches.firstbranch', '') - if firstbranchrevset: - firstbranch = repo.revs(firstbranchrevset) - parentrevs = repo.changelog.parentrevs - revs = groupbranchiter(revs, parentrevs, firstbranch) - revs = revset.baseset(revs) - for rev in revs: ctx = repo[rev] # partition into parents in the rev set and missing parents, then @@ -653,6 +445,22 @@ while len(text) < len(lines): text.append("") + if any(len(char) > 1 for char in edgemap.values()): + # limit drawing an edge to the first or last N lines of the current + # section the rest of the edge is drawn like a parent line. + parent = state['styles'][PARENT][-1] + def _drawgp(char, i): + # should a grandparent character be drawn for this line? + if len(char) < 2: + return True + num = int(char[:-1]) + # either skip first num lines or take last num lines, based on sign + return -num <= i if num < 0 else (len(lines) - i) <= num + for i, line in enumerate(lines): + line[:] = [c[-1] if _drawgp(c, i) else parent for c in line] + edgemap.update( + (e, (c if len(c) < 2 else parent)) for e, c in edgemap.items()) + # print lines indentation_level = max(ncols, ncols + coldiff) for (line, logstr) in zip(lines, text):
--- a/mercurial/help/config.txt Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/help/config.txt Fri Jul 01 16:02:56 2016 -0500 @@ -811,6 +811,15 @@ dictionary of options (with unspecified options set to their defaults). ``$HG_PATS`` is a list of arguments. Hook failure is ignored. +``fail-<command>`` + Run after a failed invocation of an associated command. The contents + of the command line are passed as ``$HG_ARGS``. Parsed command line + arguments are passed as ``$HG_PATS`` and ``$HG_OPTS``. These contain + string representations of the python data internally passed to + <command>. ``$HG_OPTS`` is a dictionary of options (with unspecified + options set to their defaults). ``$HG_PATS`` is a list of arguments. + Hook failure is ignored. + ``pre-<command>`` Run before executing the associated command. The contents of the command line are passed as ``$HG_ARGS``. Parsed command line arguments @@ -967,6 +976,8 @@ ``hostfingerprints`` -------------------- +(Deprecated. Use ``[hostsecurity]``'s ``fingerprints`` options instead.) + Fingerprints of the certificates of known HTTPS servers. A HTTPS connection to a server with a fingerprint configured here will @@ -986,6 +997,67 @@ hg.intevation.de = fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33 hg.intevation.org = fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33 +``hostsecurity`` +---------------- + +Used to specify per-host security settings. + +Options in this section have the form ``hostname``:``setting``. This allows +multiple settings to be defined on a per-host basis. + +The following per-host settings can be defined. + +``fingerprints`` + A list of hashes of the DER encoded peer/remote certificate. Values have + the form ``algorithm``:``fingerprint``. e.g. + ``sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2``. + + The following algorithms/prefixes are supported: ``sha1``, ``sha256``, + ``sha512``. + + Use of ``sha256`` or ``sha512`` is preferred. + + If a fingerprint is specified, the CA chain is not validated for this + host and Mercurial will require the remote certificate to match one + of the fingerprints specified. This means if the server updates its + certificate, Mercurial will abort until a new fingerprint is defined. + This can provide stronger security than traditional CA-based validation + at the expense of convenience. + + This option takes precedence over ``verifycertsfile``. + +``verifycertsfile`` + Path to file a containing a list of PEM encoded certificates used to + verify the server certificate. Environment variables and ``~user`` + constructs are expanded in the filename. + + The server certificate or the certificate's certificate authority (CA) + must match a certificate from this file or certificate verification + will fail and connections to the server will be refused. + + If defined, only certificates provided by this file will be used: + ``web.cacerts`` and any system/default certificates will not be + used. + + This option has no effect if the per-host ``fingerprints`` option + is set. + + The format of the file is as follows: + + -----BEGIN CERTIFICATE----- + ... (certificate in base64 PEM encoding) ... + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + ... (certificate in base64 PEM encoding) ... + -----END CERTIFICATE----- + +For example:: + + [hostsecurity] + hg.example.com:fingerprints = sha256:c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2 + hg2.example.com:fingerprints = sha1:914f1aff87249c09b6859b88b1906d30756491ca, sha1:fc:e2:8d:d9:51:cd:cb:c1:4d:18:6b:b7:44:8d:49:72:57:e6:cd:33 + foo.example.com:verifycertsfile = /etc/ssl/trusted-ca-certs.pem + ``http_proxy`` -------------- @@ -1020,8 +1092,8 @@ file in the changeset being merged or updated to, and has different contents. Options are ``abort``, ``warn`` and ``ignore``. With ``abort``, abort on such files. With ``warn``, warn on such files and back them up as - .orig. With ``ignore``, don't print a warning and back them up as - .orig. (default: ``abort``) + ``.orig``. With ``ignore``, don't print a warning and back them up as + ``.orig``. (default: ``abort``) ``checkunknown`` Controls behavior when an unknown file that isn't ignored has the same name @@ -1210,6 +1282,18 @@ The URL to use for push operations. If not defined, the location defined by the path's main entry is used. +``pushrev`` + A revset defining which revisions to push by default. + + When :hg:`push` is executed without a ``-r`` argument, the revset + defined by this sub-option is evaluated to determine what to push. + + For example, a value of ``.`` will push the working directory's + revision by default. + + Revsets specifying bookmarks will not result in the bookmark being + pushed. + The following special named paths exist: ``default`` @@ -1442,16 +1526,6 @@ Optional. Method to enable TLS when connecting to mail server: starttls, smtps or none. (default: none) -``verifycert`` - Optional. Verification for the certificate of mail server, when - ``tls`` is starttls or smtps. "strict", "loose" or False. For - "strict" or "loose", the certificate is verified as same as the - verification for HTTPS connections (see ``[hostfingerprints]`` and - ``[web] cacerts`` also). For "strict", sending email is also - aborted, if there is no configuration for mail server in - ``[hostfingerprints]`` and ``[web] cacerts``. --insecure for - :hg:`email` overwrites this as "loose". (default: strict) - ``username`` Optional. User name for authenticating with the SMTP server. (default: None) @@ -1738,6 +1812,13 @@ large organisation with its own Mercurial deployment process and crash reports should be addressed to your internal support. +``textwidth`` + Maximum width of help text. A longer line generated by ``hg help`` or + ``hg subcommand --help`` will be broken after white space to get this + width or the terminal width, whichever comes first. + A non-positive value will disable this and the terminal width will be + used. (default: 78) + ``timeout`` The timeout used when a lock is held (in seconds), a negative value means no timeout. (default: 600)
--- a/mercurial/help/templates.txt Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/help/templates.txt Fri Jul 01 16:02:56 2016 -0500 @@ -81,6 +81,10 @@ $ hg log -r 0 --template "files: {join(files, ', ')}\n" +- Separate non-empty arguments by a " ":: + + $ hg log -r 0 --template "{separate(' ', node, bookmarks, tags}\n" + - Modify each line of a commit description:: $ hg log --template "{splitlines(desc) % '**** {line}\n'}"
--- a/mercurial/hg.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/hg.py Fri Jul 01 16:02:56 2016 -0500 @@ -9,6 +9,7 @@ from __future__ import absolute_import import errno +import hashlib import os import shutil @@ -43,6 +44,9 @@ release = lock.release +# shared features +sharedbookmarks = 'bookmarks' + def _local(path): path = util.expandpath(util.urllocalpath(path)) return (os.path.isfile(path) and bundlerepo or localrepo) @@ -257,7 +261,7 @@ if bookmarks: fp = destrepo.vfs('shared', 'w') - fp.write('bookmarks\n') + fp.write(sharedbookmarks + '\n') fp.close() def _postshareupdate(repo, update, checkout=None): @@ -480,9 +484,11 @@ ui.status(_('(not using pooled storage: ' 'unable to resolve identity of remote)\n')) elif sharenamemode == 'remote': - sharepath = os.path.join(sharepool, util.sha1(source).hexdigest()) + sharepath = os.path.join( + sharepool, hashlib.sha1(source).hexdigest()) else: - raise error.Abort('unknown share naming mode: %s' % sharenamemode) + raise error.Abort(_('unknown share naming mode: %s') % + sharenamemode) if sharepath: return clonewithshare(ui, peeropts, sharepath, source, srcpeer,
--- a/mercurial/hgweb/server.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/hgweb/server.py Fri Jul 01 16:02:56 2016 -0500 @@ -9,7 +9,6 @@ from __future__ import absolute_import import BaseHTTPServer -import SocketServer import errno import os import socket @@ -23,6 +22,7 @@ util, ) +socketserver = util.socketserver urlerr = util.urlerr urlreq = util.urlreq @@ -147,9 +147,9 @@ env['wsgi.input'] = self.rfile env['wsgi.errors'] = _error_logger(self) env['wsgi.multithread'] = isinstance(self.server, - SocketServer.ThreadingMixIn) + socketserver.ThreadingMixIn) env['wsgi.multiprocess'] = isinstance(self.server, - SocketServer.ForkingMixIn) + socketserver.ForkingMixIn) env['wsgi.run_once'] = 0 self.saved_status = None @@ -240,10 +240,10 @@ try: import threading threading.activeCount() # silence pyflakes and bypass demandimport - _mixin = SocketServer.ThreadingMixIn + _mixin = socketserver.ThreadingMixIn except ImportError: if util.safehasattr(os, "fork"): - _mixin = SocketServer.ForkingMixIn + _mixin = socketserver.ForkingMixIn else: class _mixin(object): pass
--- a/mercurial/hgweb/webcommands.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/hgweb/webcommands.py Fri Jul 01 16:02:56 2016 -0500 @@ -139,7 +139,7 @@ yield {"line": t, "lineid": "l%d" % (lineno + 1), "linenumber": "% 6d" % (lineno + 1), - "parity": parity.next()} + "parity": next(parity)} return tmpl("filerevision", file=f, @@ -278,7 +278,7 @@ files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles) yield tmpl('searchentry', - parity=parity.next(), + parity=next(parity), changelogtag=showtags, files=files, **webutil.commonentry(web.repo, ctx)) @@ -375,7 +375,7 @@ break entry = webutil.changelistentry(web, web.repo[rev], tmpl) - entry['parity'] = parity.next() + entry['parity'] = next(parity) yield entry if shortlog: @@ -527,7 +527,7 @@ fctx = ctx.filectx(full) yield {"file": full, - "parity": parity.next(), + "parity": next(parity), "basename": f, "date": fctx.date(), "size": fctx.size(), @@ -545,7 +545,7 @@ h = v path = "%s%s" % (abspath, d) - yield {"parity": parity.next(), + yield {"parity": next(parity), "path": path, "emptydirs": "/".join(emptydirs), "basename": d} @@ -554,7 +554,7 @@ symrev=symrev, path=abspath, up=webutil.up(abspath), - upparity=parity.next(), + upparity=next(parity), fentries=filelist, dentries=dirlist, archives=web.archivelist(hex(node)), @@ -582,7 +582,7 @@ if latestonly: t = t[:1] for k, n in t: - yield {"parity": parity.next(), + yield {"parity": next(parity), "tag": k, "date": web.repo[n].date(), "node": hex(n)} @@ -615,7 +615,7 @@ if latestonly: t = i[:1] for k, n in t: - yield {"parity": parity.next(), + yield {"parity": next(parity), "bookmark": k, "date": web.repo[n].date(), "node": hex(n)} @@ -677,7 +677,7 @@ break yield tmpl("tagentry", - parity=parity.next(), + parity=next(parity), tag=k, node=hex(n), date=web.repo[n].date()) @@ -688,7 +688,7 @@ sortkey = lambda b: (web.repo[b[1]].rev(), b[0]) marks = sorted(marks, key=sortkey, reverse=True) for k, n in marks[:10]: # limit to 10 bookmarks - yield {'parity': parity.next(), + yield {'parity': next(parity), 'bookmark': k, 'date': web.repo[n].date(), 'node': hex(n)} @@ -704,11 +704,11 @@ l.append(tmpl( 'shortlogentry', - parity=parity.next(), + parity=next(parity), **webutil.commonentry(web.repo, ctx))) - l.reverse() - yield l + for entry in reversed(l): + yield entry tip = web.repo['tip'] count = len(web.repo) @@ -864,7 +864,6 @@ section='annotate', whitespace=True) def annotate(**map): - last = None if util.binary(fctx.data()): mt = (mimetypes.guess_type(fctx.path())[0] or 'application/octet-stream') @@ -873,19 +872,19 @@ else: lines = enumerate(fctx.annotate(follow=True, linenumber=True, diffopts=diffopts)) + previousrev = None for lineno, ((f, targetline), l) in lines: - fnode = f.filenode() - - if last != fnode: - last = fnode - - yield {"parity": parity.next(), + rev = f.rev() + blockhead = rev != previousrev or None + previousrev = rev + yield {"parity": next(parity), "node": f.hex(), - "rev": f.rev(), + "rev": rev, "author": f.user(), "desc": f.description(), "extra": f.extra(), "file": f.path(), + "blockhead": blockhead, "targetline": targetline, "line": l, "lineno": lineno + 1, @@ -963,7 +962,7 @@ iterfctx = fctx.filectx(i) l.append(dict( - parity=parity.next(), + parity=next(parity), filerev=i, file=f, rename=webutil.renamelink(iterfctx),
--- a/mercurial/hgweb/webutil.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/hgweb/webutil.py Fri Jul 01 16:02:56 2016 -0500 @@ -75,7 +75,7 @@ def _first(self): """return the minimum non-filtered changeset or None""" try: - return iter(self._revlog).next() + return next(iter(self._revlog)) except StopIteration: return None @@ -247,7 +247,7 @@ else: status = 'open' yield { - 'parity': parity.next(), + 'parity': next(parity), 'branch': ctx.branch(), 'status': status, 'node': ctx.hex(), @@ -369,7 +369,7 @@ template = f in ctx and 'filenodelink' or 'filenolink' files.append(tmpl(template, node=ctx.hex(), file=f, blockno=blockno + 1, - parity=parity.next())) + parity=next(parity))) basectx = basechangectx(web.repo, req) if basectx is None: @@ -450,15 +450,15 @@ block = [] for chunk in patch.diff(repo, node1, node2, m, opts=diffopts): if chunk.startswith('diff') and block: - blockno = blockcount.next() - yield tmpl('diffblock', parity=parity.next(), blockno=blockno, + blockno = next(blockcount) + yield tmpl('diffblock', parity=next(parity), blockno=blockno, lines=prettyprintlines(''.join(block), blockno)) block = [] if chunk.startswith('diff') and style != 'raw': chunk = ''.join(chunk.splitlines(True)[1:]) block.append(chunk) - blockno = blockcount.next() - yield tmpl('diffblock', parity=parity.next(), blockno=blockno, + blockno = next(blockcount) + yield tmpl('diffblock', parity=next(parity), blockno=blockno, lines=prettyprintlines(''.join(block), blockno)) def compare(tmpl, context, leftlines, rightlines): @@ -521,14 +521,14 @@ def diffsummary(statgen): '''Return a short summary of the diff.''' - stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next() + stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen) return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % ( len(stats), addtotal, removetotal) def diffstat(tmpl, ctx, statgen, parity): '''Return a diffstat template for each file in the diff.''' - stats, maxname, maxtotal, addtotal, removetotal, binary = statgen.next() + stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen) files = ctx.files() def pct(i): @@ -543,7 +543,7 @@ fileno += 1 yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno, total=total, addpct=pct(adds), removepct=pct(removes), - parity=parity.next()) + parity=next(parity)) class sessionvars(object): def __init__(self, vars, start='?'):
--- a/mercurial/httpclient/__init__.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/httpclient/__init__.py Fri Jul 01 16:02:56 2016 -0500 @@ -40,26 +40,38 @@ # Many functions in this file have too many arguments. # pylint: disable=R0913 - -import cStringIO +import email +import email.message import errno -import httplib +import inspect import logging -import rfc822 import select import socket +import ssl +import sys + +try: + import cStringIO as io + io.StringIO +except ImportError: + import io + +try: + import httplib + httplib.HTTPException +except ImportError: + import http.client as httplib from . import ( _readers, - socketutil, - ) +) logger = logging.getLogger(__name__) __all__ = ['HTTPConnection', 'HTTPResponse'] -HTTP_VER_1_0 = 'HTTP/1.0' -HTTP_VER_1_1 = 'HTTP/1.1' +HTTP_VER_1_0 = b'HTTP/1.0' +HTTP_VER_1_1 = b'HTTP/1.1' OUTGOING_BUFFER_SIZE = 1 << 15 INCOMING_BUFFER_SIZE = 1 << 20 @@ -73,7 +85,7 @@ CONNECTION_CLOSE = 'close' -EOL = '\r\n' +EOL = b'\r\n' _END_HEADERS = EOL * 2 # Based on some searching around, 1 second seems like a reasonable @@ -81,6 +93,57 @@ TIMEOUT_ASSUME_CONTINUE = 1 TIMEOUT_DEFAULT = None +if sys.version_info > (3, 0): + _unicode = str +else: + _unicode = unicode + +def _ensurebytes(data): + if not isinstance(data, (_unicode, bytes)): + data = str(data) + if not isinstance(data, bytes): + try: + return data.encode('latin-1') + except UnicodeEncodeError as err: + raise UnicodeEncodeError( + err.encoding, + err.object, + err.start, + err.end, + '%r is not valid Latin-1 Use .encode("utf-8") ' + 'if sending as utf-8 is desired.' % ( + data[err.start:err.end],)) + return data + +class _CompatMessage(email.message.Message): + """Workaround for rfc822.Message and email.message.Message API diffs.""" + + @classmethod + def from_string(cls, s): + if sys.version_info > (3, 0): + # Python 3 can't decode headers from bytes, so we have to + # trust RFC 2616 and decode the headers as iso-8859-1 + # bytes. + s = s.decode('iso-8859-1') + headers = email.message_from_string(s, _class=_CompatMessage) + # Fix multi-line headers to match httplib's behavior from + # Python 2.x, since email.message.Message handles them in + # slightly different ways. + if sys.version_info < (3, 0): + new = [] + for h, v in headers._headers: + if '\r\n' in v: + v = '\n'.join([' ' + x.lstrip() for x in v.split('\r\n')])[1:] + new.append((h, v)) + headers._headers = new + return headers + + def getheaders(self, key): + return self.get_all(key) + + def getheader(self, key, default=None): + return self.get(key, failobj=default) + class HTTPResponse(object): """Response from an HTTP server. @@ -91,11 +154,11 @@ def __init__(self, sock, timeout, method): self.sock = sock self.method = method - self.raw_response = '' + self.raw_response = b'' self._headers_len = 0 self.headers = None self.will_close = False - self.status_line = '' + self.status_line = b'' self.status = None self.continued = False self.http_version = None @@ -131,6 +194,10 @@ return self.headers.getheader(header, default=default) def getheaders(self): + if sys.version_info < (3, 0): + return [(k.lower(), v) for k, v in self.headers.items()] + # Starting in Python 3, headers aren't lowercased before being + # returned here. return self.headers.items() def readline(self): @@ -141,14 +208,14 @@ """ blocks = [] while True: - self._reader.readto('\n', blocks) + self._reader.readto(b'\n', blocks) - if blocks and blocks[-1][-1] == '\n' or self.complete(): + if blocks and blocks[-1][-1:] == b'\n' or self.complete(): break self._select() - return ''.join(blocks) + return b''.join(blocks) def read(self, length=None): """Read data from the response body.""" @@ -175,8 +242,8 @@ raise HTTPTimeoutException('timeout reading data') try: data = self.sock.recv(INCOMING_BUFFER_SIZE) - except socket.sslerror as e: - if e.args[0] != socket.SSL_ERROR_WANT_READ: + except ssl.SSLError as e: + if e.args[0] != ssl.SSL_ERROR_WANT_READ: raise logger.debug('SSL_ERROR_WANT_READ in _select, should retry later') return True @@ -203,7 +270,7 @@ self.raw_response += data # This is a bogus server with bad line endings if self._eol not in self.raw_response: - for bad_eol in ('\n', '\r'): + for bad_eol in (b'\n', b'\r'): if (bad_eol in self.raw_response # verify that bad_eol is not the end of the incoming data # as this could be a response line that just got @@ -220,8 +287,8 @@ # handle 100-continue response hdrs, body = self.raw_response.split(self._end_headers, 1) - unused_http_ver, status = hdrs.split(' ', 1) - if status.startswith('100'): + unused_http_ver, status = hdrs.split(b' ', 1) + if status.startswith(b'100'): self.raw_response = body self.continued = True logger.debug('continue seen, setting body to %r', body) @@ -235,14 +302,14 @@ self.status_line, hdrs = hdrs.split(self._eol, 1) else: self.status_line = hdrs - hdrs = '' + hdrs = b'' # TODO HTTP < 1.0 support (self.http_version, self.status, - self.reason) = self.status_line.split(' ', 2) + self.reason) = self.status_line.split(b' ', 2) self.status = int(self.status) if self._eol != EOL: - hdrs = hdrs.replace(self._eol, '\r\n') - headers = rfc822.Message(cStringIO.StringIO(hdrs)) + hdrs = hdrs.replace(self._eol, b'\r\n') + headers = _CompatMessage.from_string(hdrs) content_len = None if HDR_CONTENT_LENGTH in headers: content_len = int(headers[HDR_CONTENT_LENGTH]) @@ -259,8 +326,8 @@ # HEAD responses are forbidden from returning a body, and # it's implausible for a CONNECT response to use # close-is-end logic for an OK response. - if (self.method == 'HEAD' or - (self.method == 'CONNECT' and content_len is None)): + if (self.method == b'HEAD' or + (self.method == b'CONNECT' and content_len is None)): content_len = 0 if content_len is not None: logger.debug('using a content-length reader with length %d', @@ -294,8 +361,48 @@ >>> _foldheaders({'Accept-Encoding': 'wat'}) {'accept-encoding': ('Accept-Encoding', 'wat')} """ - return dict((k.lower(), (k, v)) for k, v in headers.iteritems()) + return dict((k.lower(), (k, v)) for k, v in headers.items()) + +try: + inspect.signature + def _handlesarg(func, arg): + """ Try to determine if func accepts arg + + If it takes arg, return True + If it happens to take **args, then it could do anything: + * It could throw a different TypeError, just for fun + * It could throw an ArgumentError or anything else + * It could choose not to throw an Exception at all + ... return 'unknown' + Otherwise, return False + """ + params = inspect.signature(func).parameters + if arg in params: + return True + for p in params: + if params[p].kind == inspect._ParameterKind.VAR_KEYWORD: + return 'unknown' + return False +except AttributeError: + def _handlesarg(func, arg): + """ Try to determine if func accepts arg + + If it takes arg, return True + If it happens to take **args, then it could do anything: + * It could throw a different TypeError, just for fun + * It could throw an ArgumentError or anything else + * It could choose not to throw an Exception at all + ... return 'unknown' + + Otherwise, return False + """ + spec = inspect.getargspec(func) + if arg in spec.args: + return True + if spec.keywords: + return 'unknown' + return False class HTTPConnection(object): """Connection to a single http server. @@ -340,15 +447,38 @@ Any extra keyword arguments to this function will be provided to the ssl_wrap_socket method. If no ssl """ - if port is None and host.count(':') == 1 or ']:' in host: - host, port = host.rsplit(':', 1) + host = _ensurebytes(host) + if port is None and host.count(b':') == 1 or b']:' in host: + host, port = host.rsplit(b':', 1) port = int(port) - if '[' in host: + if b'[' in host: host = host[1:-1] if ssl_wrap_socket is not None: - self._ssl_wrap_socket = ssl_wrap_socket + _wrap_socket = ssl_wrap_socket else: - self._ssl_wrap_socket = socketutil.wrap_socket + _wrap_socket = ssl.wrap_socket + call_wrap_socket = None + handlesubar = _handlesarg(_wrap_socket, 'server_hostname') + if handlesubar is True: + # supports server_hostname + call_wrap_socket = _wrap_socket + handlesnobar = _handlesarg(_wrap_socket, 'serverhostname') + if handlesnobar is True and handlesubar is not True: + # supports serverhostname + def call_wrap_socket(sock, server_hostname=None, **ssl_opts): + return _wrap_socket(sock, serverhostname=server_hostname, + **ssl_opts) + if handlesubar is False and handlesnobar is False: + # does not support either + def call_wrap_socket(sock, server_hostname=None, **ssl_opts): + return _wrap_socket(sock, **ssl_opts) + if call_wrap_socket is None: + # we assume it takes **args + def call_wrap_socket(sock, **ssl_opts): + if 'server_hostname' in ssl_opts: + ssl_opts['serverhostname'] = ssl_opts['server_hostname'] + return _wrap_socket(sock, **ssl_opts) + self._ssl_wrap_socket = call_wrap_socket if use_ssl is None and port is None: use_ssl = False port = 80 @@ -357,8 +487,6 @@ elif port is None: port = (use_ssl and 443 or 80) self.port = port - if use_ssl and not socketutil.have_ssl: - raise Exception('ssl requested but unavailable on this Python') self.ssl = use_ssl self.ssl_opts = ssl_opts self._ssl_validator = ssl_validator @@ -388,15 +516,15 @@ if self._proxy_host is not None: logger.info('Connecting to http proxy %s:%s', self._proxy_host, self._proxy_port) - sock = socketutil.create_connection((self._proxy_host, - self._proxy_port)) + sock = socket.create_connection((self._proxy_host, + self._proxy_port)) if self.ssl: - data = self._buildheaders('CONNECT', '%s:%d' % (self.host, - self.port), + data = self._buildheaders(b'CONNECT', b'%s:%d' % (self.host, + self.port), proxy_headers, HTTP_VER_1_0) sock.send(data) sock.setblocking(0) - r = self.response_class(sock, self.timeout, 'CONNECT') + r = self.response_class(sock, self.timeout, b'CONNECT') timeout_exc = HTTPTimeoutException( 'Timed out waiting for CONNECT response from proxy') while not r.complete(): @@ -421,7 +549,7 @@ logger.info('CONNECT (for SSL) to %s:%s via proxy succeeded.', self.host, self.port) else: - sock = socketutil.create_connection((self.host, self.port)) + sock = socket.create_connection((self.host, self.port)) if self.ssl: # This is the default, but in the case of proxied SSL # requests the proxy logic above will have cleared @@ -429,7 +557,8 @@ sock.setblocking(1) logger.debug('wrapping socket for ssl with options %r', self.ssl_opts) - sock = self._ssl_wrap_socket(sock, **self.ssl_opts) + sock = self._ssl_wrap_socket(sock, server_hostname=self.host, + **self.ssl_opts) if self._ssl_validator: self._ssl_validator(sock) sock.setblocking(0) @@ -441,25 +570,26 @@ hdrhost = self.host else: # include nonstandard port in header - if ':' in self.host: # must be IPv6 - hdrhost = '[%s]:%d' % (self.host, self.port) + if b':' in self.host: # must be IPv6 + hdrhost = b'[%s]:%d' % (self.host, self.port) else: - hdrhost = '%s:%d' % (self.host, self.port) + hdrhost = b'%s:%d' % (self.host, self.port) if self._proxy_host and not self.ssl: # When talking to a regular http proxy we must send the # full URI, but in all other cases we must not (although # technically RFC 2616 says servers must accept our # request if we screw up, experimentally few do that # correctly.) - assert path[0] == '/', 'path must start with a /' - path = 'http://%s%s' % (hdrhost, path) - outgoing = ['%s %s %s%s' % (method, path, http_ver, EOL)] - headers['host'] = ('Host', hdrhost) + assert path[0:1] == b'/', 'path must start with a /' + path = b'http://%s%s' % (hdrhost, path) + outgoing = [b'%s %s %s%s' % (method, path, http_ver, EOL)] + headers[b'host'] = (b'Host', hdrhost) headers[HDR_ACCEPT_ENCODING] = (HDR_ACCEPT_ENCODING, 'identity') - for hdr, val in headers.itervalues(): - outgoing.append('%s: %s%s' % (hdr, val, EOL)) + for hdr, val in sorted((_ensurebytes(h), _ensurebytes(v)) + for h, v in headers.values()): + outgoing.append(b'%s: %s%s' % (hdr, val, EOL)) outgoing.append(EOL) - return ''.join(outgoing) + return b''.join(outgoing) def close(self): """Close the connection to the server. @@ -512,6 +642,8 @@ available. Use the `getresponse()` method to retrieve the response. """ + method = _ensurebytes(method) + path = _ensurebytes(path) if self.busy(): raise httplib.CannotSendRequest( 'Can not send another request before ' @@ -520,11 +652,26 @@ logger.info('sending %s request for %s to %s on port %s', method, path, self.host, self.port) + hdrs = _foldheaders(headers) - if hdrs.get('expect', ('', ''))[1].lower() == '100-continue': + # Figure out headers that have to be computed from the request + # body. + chunked = False + if body and HDR_CONTENT_LENGTH not in hdrs: + if getattr(body, '__len__', False): + hdrs[HDR_CONTENT_LENGTH] = (HDR_CONTENT_LENGTH, + b'%d' % len(body)) + elif getattr(body, 'read', False): + hdrs[HDR_XFER_ENCODING] = (HDR_XFER_ENCODING, + XFER_ENCODING_CHUNKED) + chunked = True + else: + raise BadRequestData('body has no __len__() nor read()') + # Figure out expect-continue header + if hdrs.get('expect', ('', ''))[1].lower() == b'100-continue': expect_continue = True elif expect_continue: - hdrs['expect'] = ('Expect', '100-Continue') + hdrs['expect'] = (b'Expect', b'100-Continue') # httplib compatibility: if the user specified a # proxy-authorization header, that's actually intended for a # proxy CONNECT action, not the real request, but only if @@ -534,25 +681,15 @@ pa = hdrs.pop('proxy-authorization', None) if pa is not None: pheaders['proxy-authorization'] = pa - - chunked = False - if body and HDR_CONTENT_LENGTH not in hdrs: - if getattr(body, '__len__', False): - hdrs[HDR_CONTENT_LENGTH] = (HDR_CONTENT_LENGTH, len(body)) - elif getattr(body, 'read', False): - hdrs[HDR_XFER_ENCODING] = (HDR_XFER_ENCODING, - XFER_ENCODING_CHUNKED) - chunked = True - else: - raise BadRequestData('body has no __len__() nor read()') + # Build header data + outgoing_headers = self._buildheaders( + method, path, hdrs, self.http_version) # If we're reusing the underlying socket, there are some # conditions where we'll want to retry, so make a note of the # state of self.sock fresh_socket = self.sock is None self._connect(pheaders) - outgoing_headers = self._buildheaders( - method, path, hdrs, self.http_version) response = None first = True @@ -592,8 +729,8 @@ try: try: data = r[0].recv(INCOMING_BUFFER_SIZE) - except socket.sslerror as e: - if e.args[0] != socket.SSL_ERROR_WANT_READ: + except ssl.SSLError as e: + if e.args[0] != ssl.SSL_ERROR_WANT_READ: raise logger.debug('SSL_ERROR_WANT_READ while sending ' 'data, retrying...') @@ -662,16 +799,20 @@ continue if len(data) < OUTGOING_BUFFER_SIZE: if chunked: - body = '0' + EOL + EOL + body = b'0' + EOL + EOL else: body = None if chunked: - out = hex(len(data))[2:] + EOL + data + EOL + # This encode is okay because we know + # hex() is building us only 0-9 and a-f + # digits. + asciilen = hex(len(data))[2:].encode('ascii') + out = asciilen + EOL + data + EOL else: out = data amt = w[0].send(out) except socket.error as e: - if e[0] == socket.SSL_ERROR_WANT_WRITE and self.ssl: + if e[0] == ssl.SSL_ERROR_WANT_WRITE and self.ssl: # This means that SSL hasn't flushed its buffer into # the socket yet. # TODO: find a way to block on ssl flushing its buffer @@ -690,6 +831,7 @@ body = out[amt:] else: outgoing_headers = out[amt:] + # End of request-sending loop. # close if the server response said to or responded before eating # the whole request
--- a/mercurial/httpclient/_readers.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/httpclient/_readers.py Fri Jul 01 16:02:56 2016 -0500 @@ -33,7 +33,12 @@ """ from __future__ import absolute_import -import httplib +try: + import httplib + httplib.HTTPException +except ImportError: + import http.client as httplib + import logging logger = logging.getLogger(__name__) @@ -93,7 +98,7 @@ need -= len(b) if need == 0: break - result = ''.join(blocks) + result = b''.join(blocks) assert len(result) == amt or (self._finished and len(result) < amt) return result
--- a/mercurial/httpclient/socketutil.py Fri Jul 01 15:12:33 2016 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,140 +0,0 @@ -# Copyright 2010, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""Abstraction to simplify socket use for Python < 2.6 - -This will attempt to use the ssl module and the new -socket.create_connection method, but fall back to the old -methods if those are unavailable. -""" -from __future__ import absolute_import - -import logging -import socket - -logger = logging.getLogger(__name__) - -try: - import ssl - # make demandimporters load the module - ssl.wrap_socket # pylint: disable=W0104 - have_ssl = True -except ImportError: - import httplib - import urllib2 - have_ssl = getattr(urllib2, 'HTTPSHandler', False) - ssl = False - - -try: - create_connection = socket.create_connection -except AttributeError: - def create_connection(address): - """Backport of socket.create_connection from Python 2.6.""" - host, port = address - msg = "getaddrinfo returns an empty list" - sock = None - for res in socket.getaddrinfo(host, port, 0, - socket.SOCK_STREAM): - af, socktype, proto, unused_canonname, sa = res - try: - sock = socket.socket(af, socktype, proto) - logger.info("connect: (%s, %s)", host, port) - sock.connect(sa) - except socket.error as msg: - logger.info('connect fail: %s %s', host, port) - if sock: - sock.close() - sock = None - continue - break - if not sock: - raise socket.error(msg) - return sock - -if ssl: - wrap_socket = ssl.wrap_socket - CERT_NONE = ssl.CERT_NONE - CERT_OPTIONAL = ssl.CERT_OPTIONAL - CERT_REQUIRED = ssl.CERT_REQUIRED -else: - class FakeSocket(httplib.FakeSocket): - """Socket wrapper that supports SSL.""" - - # Silence lint about this goofy backport class - # pylint: disable=W0232,E1101,R0903,R0913,C0111 - - # backport the behavior from Python 2.6, which is to busy wait - # on the socket instead of anything nice. Sigh. - # See http://bugs.python.org/issue3890 for more info. - def recv(self, buflen=1024, flags=0): - """ssl-aware wrapper around socket.recv - """ - if flags != 0: - raise ValueError( - "non-zero flags not allowed in calls to recv() on %s" % - self.__class__) - while True: - try: - return self._ssl.read(buflen) - except socket.sslerror as x: - if x.args[0] == socket.SSL_ERROR_WANT_READ: - continue - else: - raise x - - _PROTOCOL_SSLv23 = 2 - - CERT_NONE = 0 - CERT_OPTIONAL = 1 - CERT_REQUIRED = 2 - - # Disable unused-argument because we're making a dumb wrapper - # that's like an upstream method. - # - # pylint: disable=W0613,R0913 - def wrap_socket(sock, keyfile=None, certfile=None, - server_side=False, cert_reqs=CERT_NONE, - ssl_version=_PROTOCOL_SSLv23, ca_certs=None, - do_handshake_on_connect=True, - suppress_ragged_eofs=True): - """Backport of ssl.wrap_socket from Python 2.6.""" - if cert_reqs != CERT_NONE and ca_certs: - raise CertificateValidationUnsupported( - 'SSL certificate validation requires the ssl module' - '(included in Python 2.6 and later.)') - sslob = socket.ssl(sock) - # borrow httplib's workaround for no ssl.wrap_socket - sock = FakeSocket(sock, sslob) - return sock - # pylint: enable=W0613,R0913 - - -class CertificateValidationUnsupported(Exception): - """Exception raised when cert validation is requested but unavailable.""" -# no-check-code
--- a/mercurial/httpconnection.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/httpconnection.py Fri Jul 01 16:02:56 2016 -0500 @@ -280,10 +280,9 @@ kwargs['keyfile'] = keyfile kwargs['certfile'] = certfile - kwargs.update(sslutil.sslkwargs(self.ui, host)) - con = HTTPConnection(host, port, use_ssl=True, ssl_wrap_socket=sslutil.wrapsocket, - ssl_validator=sslutil.validator(self.ui, host), + ssl_validator=sslutil.validatesocket, + ui=self.ui, **kwargs) return con
--- a/mercurial/httppeer.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/httppeer.py Fri Jul 01 16:02:56 2016 -0500 @@ -9,7 +9,6 @@ from __future__ import absolute_import import errno -import httplib import os import socket import tempfile @@ -27,6 +26,7 @@ wireproto, ) +httplib = util.httplib urlerr = util.urlerr urlreq = util.urlreq @@ -302,7 +302,7 @@ except error.RepoError as httpexception: try: r = statichttprepo.instance(ui, "static-" + path, create) - ui.note('(falling back to static-http)\n') + ui.note(_('(falling back to static-http)\n')) return r except error.RepoError: raise httpexception # use the original http RepoError instead
--- a/mercurial/i18n.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/i18n.py Fri Jul 01 16:02:56 2016 -0500 @@ -78,7 +78,7 @@ paragraphs = [p.decode("ascii") for p in message.split('\n\n')] # Be careful not to translate the empty string -- it holds the # meta data of the .po file. - u = u'\n\n'.join([p and _ugettext(p) or '' for p in paragraphs]) + u = u'\n\n'.join([p and _ugettext(p) or u'' for p in paragraphs]) try: # encoding.tolocal cannot be used since it will first try to # decode the Unicode string. Calling u.decode(enc) really
--- a/mercurial/keepalive.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/keepalive.py Fri Jul 01 16:02:56 2016 -0500 @@ -110,15 +110,16 @@ from __future__ import absolute_import, print_function import errno -import httplib +import hashlib import socket import sys -import thread +import threading from . import ( util, ) +httplib = util.httplib urlerr = util.urlerr urlreq = util.urlreq @@ -134,7 +135,7 @@ * keep track of all existing """ def __init__(self): - self._lock = thread.allocate_lock() + self._lock = threading.Lock() self._hostmap = {} # map hosts to a list of connections self._connmap = {} # map connections to host self._readymap = {} # map connection to ready state @@ -624,8 +625,7 @@ keepalive_handler.close_all() def continuity(url): - from . import util - md5 = util.md5 + md5 = hashlib.md5 format = '%25s: %s' # first fetch the file with the normal http handler
--- a/mercurial/localrepo.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/localrepo.py Fri Jul 01 16:02:56 2016 -0500 @@ -8,6 +8,7 @@ from __future__ import absolute_import import errno +import hashlib import inspect import os import random @@ -57,16 +58,16 @@ ) release = lockmod.release -propertycache = util.propertycache urlerr = util.urlerr urlreq = util.urlreq -filecache = scmutil.filecache -class repofilecache(filecache): +class repofilecache(scmutil.filecache): """All filecache usage on repo are done for logic that should be unfiltered """ def __get__(self, repo, type=None): + if repo is None: + return self return super(repofilecache, self).__get__(repo.unfiltered(), type) def __set__(self, repo, value): return super(repofilecache, self).__set__(repo.unfiltered(), value) @@ -78,7 +79,7 @@ def join(self, obj, fname): return obj.sjoin(fname) -class unfilteredpropertycache(propertycache): +class unfilteredpropertycache(util.propertycache): """propertycache that apply to unfiltered repo only""" def __get__(self, repo, type=None): @@ -87,7 +88,7 @@ return super(unfilteredpropertycache, self).__get__(unfi) return getattr(unfi, self.name) -class filteredpropertycache(propertycache): +class filteredpropertycache(util.propertycache): """propertycache that must take filtering in account""" def cachevalue(self, obj, value): @@ -553,7 +554,10 @@ The revset is specified as a string ``expr`` that may contain %-formatting to escape certain types. See ``revset.formatspec``. - Return a revset.abstractsmartset, which is a list-like interface + Revset aliases from the configuration are not expanded. To expand + user aliases, consider calling ``scmutil.revrange()``. + + Returns a revset.abstractsmartset, which is a list-like interface that contains integer revisions. ''' expr = revset.formatspec(expr, *args) @@ -565,6 +569,9 @@ This is a convenience wrapper around ``revs()`` that iterates the result and is a generator of changectx instances. + + Revset aliases from the configuration are not expanded. To expand + user aliases, consider calling ``scmutil.revrange()``. ''' for r in self.revs(expr, *args): yield self[r] @@ -881,12 +888,6 @@ f = f[1:] return filelog.filelog(self.svfs, f) - def parents(self, changeid=None): - '''get list of changectxs for parents of changeid''' - msg = 'repo.parents() is deprecated, use repo[%r].parents()' % changeid - self.ui.deprecwarn(msg, '3.7') - return self[changeid].parents() - def changectx(self, changeid): return self[changeid] @@ -1008,7 +1009,8 @@ or self.ui.configbool('devel', 'check-locks')): l = self._lockref and self._lockref() if l is None or not l.held: - self.ui.develwarn('transaction with no lock') + raise RuntimeError('programming error: transaction requires ' + 'locking') tr = self.currenttransaction() if tr is not None: return tr.nest() @@ -1019,11 +1021,8 @@ _("abandoned transaction found"), hint=_("run 'hg recover' to clean up transaction")) - # make journal.dirstate contain in-memory changes at this point - self.dirstate.write(None) - idbase = "%.40f#%f" % (random.random(), time.time()) - txnid = 'TXN:' + util.sha1(idbase).hexdigest() + txnid = 'TXN:' + hashlib.sha1(idbase).hexdigest() self.hook('pretxnopen', throw=True, txnname=desc, txnid=txnid) self._writejournal(desc) @@ -1049,13 +1048,9 @@ # transaction running repo.dirstate.write(None) else: - # prevent in-memory changes from being written out at - # the end of outer wlock scope or so - repo.dirstate.invalidate() - # discard all changes (including ones already written # out) in this transaction - repo.vfs.rename('journal.dirstate', 'dirstate') + repo.dirstate.restorebackup(None, prefix='journal.') repo.invalidate(clearfilecache=True) @@ -1110,8 +1105,7 @@ return [(vfs, undoname(x)) for vfs, x in self._journalfiles()] def _writejournal(self, desc): - self.vfs.write("journal.dirstate", - self.vfs.tryread("dirstate")) + self.dirstate.savebackup(None, prefix='journal.') self.vfs.write("journal.branch", encoding.fromlocal(self.dirstate.branch())) self.vfs.write("journal.desc", @@ -1186,9 +1180,9 @@ vfsmap = {'plain': self.vfs, '': self.svfs} transaction.rollback(self.svfs, vfsmap, 'undo', ui.warn) if self.vfs.exists('undo.bookmarks'): - self.vfs.rename('undo.bookmarks', 'bookmarks') + self.vfs.rename('undo.bookmarks', 'bookmarks', checkambig=True) if self.svfs.exists('undo.phaseroots'): - self.svfs.rename('undo.phaseroots', 'phaseroots') + self.svfs.rename('undo.phaseroots', 'phaseroots', checkambig=True) self.invalidate() parentgone = (parents[0] not in self.changelog.nodemap or @@ -1197,7 +1191,7 @@ # prevent dirstateguard from overwriting already restored one dsguard.close() - self.vfs.rename('undo.dirstate', 'dirstate') + self.dirstate.restorebackup(None, prefix='undo.') try: branch = self.vfs.read('undo.branch') self.dirstate.setbranch(encoding.tolocal(branch)) @@ -1206,7 +1200,6 @@ 'current branch is still \'%s\'\n') % self.dirstate.branch()) - self.dirstate.invalidate() parents = tuple([p.rev() for p in self[None].parents()]) if len(parents) > 1: ui.status(_('working directory now based on '
--- a/mercurial/mail.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/mail.py Fri Jul 01 16:02:56 2016 -0500 @@ -41,16 +41,16 @@ kw['continuation_ws'] = ' ' _oldheaderinit(self, *args, **kw) -email.Header.Header.__dict__['__init__'] = _unifiedheaderinit +setattr(email.header.Header, '__init__', _unifiedheaderinit) class STARTTLS(smtplib.SMTP): '''Derived class to verify the peer certificate for STARTTLS. This class allows to pass any keyword arguments to SSL socket creation. ''' - def __init__(self, sslkwargs, host=None, **kwargs): + def __init__(self, ui, host=None, **kwargs): smtplib.SMTP.__init__(self, **kwargs) - self._sslkwargs = sslkwargs + self._ui = ui self._host = host def starttls(self, keyfile=None, certfile=None): @@ -60,8 +60,8 @@ (resp, reply) = self.docmd("STARTTLS") if resp == 220: self.sock = sslutil.wrapsocket(self.sock, keyfile, certfile, - serverhostname=self._host, - **self._sslkwargs) + ui=self._ui, + serverhostname=self._host) self.file = smtplib.SSLFakeFile(self.sock) self.helo_resp = None self.ehlo_resp = None @@ -74,14 +74,14 @@ This class allows to pass any keyword arguments to SSL socket creation. ''' - def __init__(self, sslkwargs, keyfile=None, certfile=None, host=None, + def __init__(self, ui, keyfile=None, certfile=None, host=None, **kwargs): self.keyfile = keyfile self.certfile = certfile smtplib.SMTP.__init__(self, **kwargs) self._host = host self.default_port = smtplib.SMTP_SSL_PORT - self._sslkwargs = sslkwargs + self._ui = ui def _get_socket(self, host, port, timeout): if self.debuglevel > 0: @@ -89,8 +89,8 @@ new_socket = socket.create_connection((host, port), timeout) new_socket = sslutil.wrapsocket(new_socket, self.keyfile, self.certfile, - serverhostname=self._host, - **self._sslkwargs) + ui=self._ui, + serverhostname=self._host) self.file = smtplib.SSLFakeFile(new_socket) return new_socket @@ -106,22 +106,11 @@ mailhost = ui.config('smtp', 'host') if not mailhost: raise error.Abort(_('smtp.host not configured - cannot send mail')) - verifycert = ui.config('smtp', 'verifycert', 'strict') - if verifycert not in ['strict', 'loose']: - if util.parsebool(verifycert) is not False: - raise error.Abort(_('invalid smtp.verifycert configuration: %s') - % (verifycert)) - verifycert = False - if (starttls or smtps) and verifycert: - sslkwargs = sslutil.sslkwargs(ui, mailhost) - else: - # 'ui' is required by sslutil.wrapsocket() and set by sslkwargs() - sslkwargs = {'ui': ui} if smtps: ui.note(_('(using smtps)\n')) - s = SMTPS(sslkwargs, local_hostname=local_hostname, host=mailhost) + s = SMTPS(ui, local_hostname=local_hostname, host=mailhost) elif starttls: - s = STARTTLS(sslkwargs, local_hostname=local_hostname, host=mailhost) + s = STARTTLS(ui, local_hostname=local_hostname, host=mailhost) else: s = smtplib.SMTP(local_hostname=local_hostname) if smtps: @@ -137,9 +126,9 @@ s.ehlo() s.starttls() s.ehlo() - if (starttls or smtps) and verifycert: + if starttls or smtps: ui.note(_('(verifying remote certificate)\n')) - sslutil.validator(ui, mailhost)(s.sock, verifycert == 'strict') + sslutil.validatesocket(s.sock) username = ui.config('smtp', 'username') password = ui.config('smtp', 'password') if username and not password:
--- a/mercurial/manifest.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/manifest.py Fri Jul 01 16:02:56 2016 -0500 @@ -211,8 +211,10 @@ def filesnotin(self, m2): '''Set of files in this manifest that are not in the other''' - files = set(self) - files.difference_update(m2) + diff = self.diff(m2) + files = set(filepath + for filepath, hashflags in diff.iteritems() + if hashflags[1][0] is None) return files @propertycache @@ -966,7 +968,7 @@ return self.readdelta(node) if self._usemanifestv2: raise error.Abort( - "readshallowdelta() not implemented for manifestv2") + _("readshallowdelta() not implemented for manifestv2")) r = self.rev(node) d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r)) return manifestdict(d)
--- a/mercurial/match.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/match.py Fri Jul 01 16:02:56 2016 -0500 @@ -38,7 +38,7 @@ for kind, pat, source in kindpats: if kind == 'set': if not ctx: - raise error.Abort("fileset expression with no context") + raise error.Abort(_("fileset expression with no context")) s = ctx.getfileset(pat) fset.update(s)
--- a/mercurial/mdiff.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/mdiff.py Fri Jul 01 16:02:56 2016 -0500 @@ -58,10 +58,8 @@ 'upgrade': False, } - __slots__ = defaults.keys() - def __init__(self, **opts): - for k in self.__slots__: + for k in self.defaults.keys(): v = opts.get(k) if v is None: v = self.defaults[k]
--- a/mercurial/merge.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/merge.py Fri Jul 01 16:02:56 2016 -0500 @@ -8,6 +8,7 @@ from __future__ import absolute_import import errno +import hashlib import os import shutil import struct @@ -373,7 +374,7 @@ """Write current state on disk in a version 1 file""" f = self._repo.vfs(self.statepathv1, 'w') irecords = iter(records) - lrecords = irecords.next() + lrecords = next(irecords) assert lrecords[0] == 'L' f.write(hex(self._local) + '\n') for rtype, data in irecords: @@ -408,7 +409,7 @@ if fcl.isabsent(): hash = nullhex else: - hash = util.sha1(fcl.path()).hexdigest() + hash = hashlib.sha1(fcl.path()).hexdigest() self._repo.vfs.write('merge/' + hash, fcl.data()) self._state[fd] = ['u', hash, fcl.path(), fca.path(), hex(fca.filenode()), @@ -989,19 +990,19 @@ if len(bids) == 1: # all bids are the same kind of method m, l = bids.items()[0] if all(a == l[0] for a in l[1:]): # len(bids) is > 1 - repo.ui.note(" %s: consensus for %s\n" % (f, m)) + repo.ui.note(_(" %s: consensus for %s\n") % (f, m)) actions[f] = l[0] continue # If keep is an option, just do it. if 'k' in bids: - repo.ui.note(" %s: picking 'keep' action\n" % f) + repo.ui.note(_(" %s: picking 'keep' action\n") % f) actions[f] = bids['k'][0] continue # If there are gets and they all agree [how could they not?], do it. if 'g' in bids: ga0 = bids['g'][0] if all(a == ga0 for a in bids['g'][1:]): - repo.ui.note(" %s: picking 'get' action\n" % f) + repo.ui.note(_(" %s: picking 'get' action\n") % f) actions[f] = ga0 continue # TODO: Consider other simple actions such as mode changes @@ -1442,9 +1443,7 @@ pas = [repo[ancestor]] if node is None: - if (repo.ui.configbool('devel', 'all-warnings') - or repo.ui.configbool('devel', 'oldapi')): - repo.ui.develwarn('update with no target') + repo.ui.deprecwarn('update with no target', '3.9') rev, _mark, _act = destutil.destupdate(repo) node = repo[rev].node()
--- a/mercurial/mpatch.c Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/mpatch.c Fri Jul 01 16:02:56 2016 -0500 @@ -26,6 +26,7 @@ #include <string.h> #include "util.h" +#include "bitmanipulation.h" static char mpatch_doc[] = "Efficient binary patching."; static PyObject *mpatch_Error;
--- a/mercurial/obsolete.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/obsolete.py Fri Jul 01 16:02:56 2016 -0500 @@ -600,8 +600,8 @@ Take care of filtering duplicate. Return the number of new marker.""" if self._readonly: - raise error.Abort('creating obsolete markers is not enabled on ' - 'this repo') + raise error.Abort(_('creating obsolete markers is not enabled on ' + 'this repo')) known = set(self._all) new = [] for m in markers: @@ -1171,7 +1171,7 @@ ignoreflags=bumpedfix): prev = torev(pnode) # unfiltered! but so is phasecache if (prev is not None) and (phase(repo, prev) <= public): - # we have a public precursors + # we have a public precursor bumped.add(rev) break # Next draft! return bumped @@ -1234,7 +1234,7 @@ localmetadata.update(rel[2]) if not prec.mutable(): - raise error.Abort("cannot obsolete public changeset: %s" + raise error.Abort(_("cannot obsolete public changeset: %s") % prec, hint='see "hg help phases" for details') nprec = prec.node() @@ -1243,7 +1243,8 @@ if not nsucs: npare = tuple(p.node() for p in prec.parents()) if nprec in nsucs: - raise error.Abort("changeset %s cannot obsolete itself" % prec) + raise error.Abort(_("changeset %s cannot obsolete itself") + % prec) # Creating the marker causes the hidden cache to become invalid, # which causes recomputation when we ask for prec.parents() above.
--- a/mercurial/parser.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/parser.py Fri Jul 01 16:02:56 2016 -0500 @@ -325,13 +325,13 @@ >>> builddecl('foo') ('foo', None, None) >>> builddecl('$foo') - ('$foo', None, "'$' not for alias arguments") + ('$foo', None, "invalid symbol '$foo'") >>> builddecl('foo::bar') ('foo::bar', None, 'invalid format') >>> builddecl('foo()') ('foo', [], None) >>> builddecl('$foo()') - ('$foo()', None, "'$' not for alias arguments") + ('$foo()', None, "invalid function '$foo'") >>> builddecl('foo($1, $2)') ('foo', ['$1', '$2'], None) >>> builddecl('foo(bar_bar, baz.baz)') @@ -358,7 +358,7 @@ # "name = ...." style name = tree[1] if name.startswith('$'): - return (decl, None, _("'$' not for alias arguments")) + return (decl, None, _("invalid symbol '%s'") % name) return (name, None, None) func = cls._trygetfunc(tree) @@ -366,7 +366,7 @@ # "name(arg, ....) = ...." style name, args = func if name.startswith('$'): - return (decl, None, _("'$' not for alias arguments")) + return (decl, None, _("invalid function '%s'") % name) if any(t[0] != cls._symbolnode for t in args): return (decl, None, _("invalid argument list")) if len(args) != len(set(args)): @@ -389,7 +389,7 @@ if sym in args: op = '_aliasarg' elif sym.startswith('$'): - raise error.ParseError(_("'$' not for alias arguments")) + raise error.ParseError(_("invalid symbol '%s'") % sym) return (op, sym) @classmethod @@ -423,7 +423,7 @@ ... builddefn('$1 or $bar', args) ... except error.ParseError as inst: ... print parseerrordetail(inst) - '$' not for alias arguments + invalid symbol '$bar' >>> args = ['$1', '$10', 'foo'] >>> pprint(builddefn('$10 or baz', args)) (or @@ -447,15 +447,13 @@ repl = efmt = None name, args, err = cls._builddecl(decl) if err: - efmt = _('failed to parse the declaration of %(section)s ' - '"%(name)s": %(error)s') + efmt = _('bad declaration of %(section)s "%(name)s": %(error)s') else: try: repl = cls._builddefn(defn, args) except error.ParseError as inst: err = parseerrordetail(inst) - efmt = _('failed to parse the definition of %(section)s ' - '"%(name)s": %(error)s') + efmt = _('bad definition of %(section)s "%(name)s": %(error)s') if err: err = efmt % {'section': cls._section, 'name': name, 'error': err} return alias(name, args, err, repl)
--- a/mercurial/parsers.c Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/parsers.c Fri Jul 01 16:02:56 2016 -0500 @@ -13,6 +13,7 @@ #include <string.h> #include "util.h" +#include "bitmanipulation.h" static char *versionerrortext = "Python minor version mismatch";
--- a/mercurial/patch.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/patch.py Fri Jul 01 16:02:56 2016 -0500 @@ -12,6 +12,7 @@ import copy import email import errno +import hashlib import os import posixpath import re @@ -978,7 +979,19 @@ def filterpatch(ui, headers, operation=None): """Interactively filter patch chunks into applied-only chunks""" if operation is None: - operation = _('record') + operation = 'record' + messages = { + 'multiple': { + 'discard': _("discard change %d/%d to '%s'?"), + 'record': _("record change %d/%d to '%s'?"), + 'revert': _("revert change %d/%d to '%s'?"), + }[operation], + 'single': { + 'discard': _("discard this change to '%s'?"), + 'record': _("record this change to '%s'?"), + 'revert': _("revert this change to '%s'?"), + }[operation], + } def prompt(skipfile, skipall, query, chunk): """prompt query, and process base inputs @@ -1109,11 +1122,10 @@ if skipfile is None and skipall is None: chunk.pretty(ui) if total == 1: - msg = _("record this change to '%s'?") % chunk.filename() + msg = messages['single'] % chunk.filename() else: idx = pos - len(h.hunks) + i - msg = _("record change %d/%d to '%s'?") % (idx, total, - chunk.filename()) + msg = messages['multiple'] % (idx, total, chunk.filename()) r, skipfile, skipall, newpatches = prompt(skipfile, skipall, msg, chunk) if r: @@ -2172,7 +2184,7 @@ return mdiff.diffopts(**buildopts) def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None, - losedatafn=None, prefix='', relroot=''): + losedatafn=None, prefix='', relroot='', copy=None): '''yields diff of changes to files between two nodes, or node and working directory. @@ -2191,7 +2203,10 @@ display (used for subrepos). relroot, if not empty, must be normalized with a trailing /. Any match - patterns that fall outside it will be ignored.''' + patterns that fall outside it will be ignored. + + copy, if not empty, should contain mappings {dst@y: src@x} of copy + information.''' if opts is None: opts = mdiff.defaultopts @@ -2238,9 +2253,10 @@ hexfunc = short revs = [hexfunc(node) for node in [ctx1.node(), ctx2.node()] if node] - copy = {} - if opts.git or opts.upgrade: - copy = copies.pathcopies(ctx1, ctx2, match=match) + if copy is None: + copy = {} + if opts.git or opts.upgrade: + copy = copies.pathcopies(ctx1, ctx2, match=match) if relroot is not None: if not relfiltered: @@ -2401,7 +2417,7 @@ if not text: text = "" l = len(text) - s = util.sha1('blob %d\0' % l) + s = hashlib.sha1('blob %d\0' % l) s.update(text) return s.hexdigest()
--- a/mercurial/pathencode.c Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/pathencode.c Fri Jul 01 16:02:56 2016 -0500 @@ -653,24 +653,24 @@ PyObject *shaobj, *hashobj; if (shafunc == NULL) { - PyObject *util, *name = PyString_FromString("mercurial.util"); + PyObject *hashlib, *name = PyString_FromString("hashlib"); if (name == NULL) return -1; - util = PyImport_Import(name); + hashlib = PyImport_Import(name); Py_DECREF(name); - if (util == NULL) { - PyErr_SetString(PyExc_ImportError, "mercurial.util"); + if (hashlib == NULL) { + PyErr_SetString(PyExc_ImportError, "hashlib"); return -1; } - shafunc = PyObject_GetAttrString(util, "sha1"); - Py_DECREF(util); + shafunc = PyObject_GetAttrString(hashlib, "sha1"); + Py_DECREF(hashlib); if (shafunc == NULL) { PyErr_SetString(PyExc_AttributeError, - "module 'mercurial.util' has no " + "module 'hashlib' has no " "attribute 'sha1'"); return -1; }
--- a/mercurial/peer.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/peer.py Fri Jul 01 16:02:56 2016 -0500 @@ -98,12 +98,12 @@ ''' def plain(*args, **opts): batchable = f(*args, **opts) - encargsorres, encresref = batchable.next() + encargsorres, encresref = next(batchable) if not encresref: return encargsorres # a local result in this case self = args[0] encresref.set(self._submitone(f.func_name, encargsorres)) - return batchable.next() + return next(batchable) setattr(plain, 'batchable', f) return plain
--- a/mercurial/phases.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/phases.py Fri Jul 01 16:02:56 2016 -0500 @@ -251,7 +251,7 @@ def write(self): if not self.dirty: return - f = self.opener('phaseroots', 'w', atomictemp=True) + f = self.opener('phaseroots', 'w', atomictemp=True, checkambig=True) try: self._write(f) finally:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/policy.py Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,40 @@ +# policy.py - module policy logic for Mercurial. +# +# Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from __future__ import absolute_import + +import os +import sys + +# Rules for how modules can be loaded. Values are: +# +# c - require C extensions +# allow - allow pure Python implementation when C loading fails +# py - only load pure Python modules +# +# By default, require the C extensions for performance reasons. +policy = 'c' +try: + from . import __modulepolicy__ + policy = __modulepolicy__.modulepolicy +except ImportError: + pass + +# PyPy doesn't load C extensions. +# +# The canonical way to do this is to test platform.python_implementation(). +# But we don't import platform and don't bloat for it here. +if '__pypy__' in sys.builtin_module_names: + policy = 'py' + +# Our C extensions aren't yet compatible with Python 3. So use pure Python +# on Python 3 for now. +if sys.version_info[0] >= 3: + policy = 'py' + +# Environment variable can always force settings. +policy = os.environ.get('HGMODULEPOLICY', policy)
--- a/mercurial/pure/parsers.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/pure/parsers.py Fri Jul 01 16:02:56 2016 -0500 @@ -25,49 +25,111 @@ # x is a tuple return x -def parse_index2(data, inline): - def gettype(q): - return int(q & 0xFFFF) +indexformatng = ">Qiiiiii20s12x" +indexfirst = struct.calcsize('Q') +sizeint = struct.calcsize('i') +indexsize = struct.calcsize(indexformatng) + +def gettype(q): + return int(q & 0xFFFF) - def offset_type(offset, type): - return long(long(offset) << 16 | type) +def offset_type(offset, type): + return long(long(offset) << 16 | type) + +class BaseIndexObject(object): + def __len__(self): + return self._lgt + len(self._extra) + 1 + + def insert(self, i, tup): + assert i == -1 + self._extra.append(tup) - indexformatng = ">Qiiiiii20s12x" + def _fix_index(self, i): + if not isinstance(i, int): + raise TypeError("expecting int indexes") + if i < 0: + i = len(self) + i + if i < 0 or i >= len(self): + raise IndexError + return i - s = struct.calcsize(indexformatng) - index = [] - cache = None - off = 0 + def __getitem__(self, i): + i = self._fix_index(i) + if i == len(self) - 1: + return (0, 0, 0, -1, -1, -1, -1, nullid) + if i >= self._lgt: + return self._extra[i - self._lgt] + index = self._calculate_index(i) + r = struct.unpack(indexformatng, self._data[index:index + indexsize]) + if i == 0: + e = list(r) + type = gettype(e[0]) + e[0] = offset_type(0, type) + return tuple(e) + return r + +class IndexObject(BaseIndexObject): + def __init__(self, data): + assert len(data) % indexsize == 0 + self._data = data + self._lgt = len(data) // indexsize + self._extra = [] + + def _calculate_index(self, i): + return i * indexsize - l = len(data) - s - append = index.append - if inline: - cache = (0, data) - while off <= l: - e = _unpack(indexformatng, data[off:off + s]) - append(e) - if e[1] < 0: - break - off += e[1] + s - else: - while off <= l: - e = _unpack(indexformatng, data[off:off + s]) - append(e) - off += s + def __delitem__(self, i): + if not isinstance(i, slice) or not i.stop == -1 or not i.step is None: + raise ValueError("deleting slices only supports a:-1 with step 1") + i = self._fix_index(i.start) + if i < self._lgt: + self._data = self._data[:i * indexsize] + self._lgt = i + self._extra = [] + else: + self._extra = self._extra[:i - self._lgt] + +class InlinedIndexObject(BaseIndexObject): + def __init__(self, data, inline=0): + self._data = data + self._lgt = self._inline_scan(None) + self._inline_scan(self._lgt) + self._extra = [] - if off != len(data): - raise ValueError('corrupt index file') + def _inline_scan(self, lgt): + off = 0 + if lgt is not None: + self._offsets = [0] * lgt + count = 0 + while off <= len(self._data) - indexsize: + s, = struct.unpack('>i', + self._data[off + indexfirst:off + sizeint + indexfirst]) + if lgt is not None: + self._offsets[count] = off + count += 1 + off += indexsize + s + if off != len(self._data): + raise ValueError("corrupted data") + return count - if index: - e = list(index[0]) - type = gettype(e[0]) - e[0] = offset_type(0, type) - index[0] = tuple(e) + def __delitem__(self, i): + if not isinstance(i, slice) or not i.stop == -1 or not i.step is None: + raise ValueError("deleting slices only supports a:-1 with step 1") + i = self._fix_index(i.start) + if i < self._lgt: + self._offsets = self._offsets[:i] + self._lgt = i + self._extra = [] + else: + self._extra = self._extra[:i - self._lgt] - # add the magic null revision at -1 - index.append((0, 0, 0, -1, -1, -1, -1, nullid)) + def _calculate_index(self, i): + return self._offsets[i] - return index, cache +def parse_index2(data, inline): + if not inline: + return IndexObject(data), None + return InlinedIndexObject(data, inline), (0, data) def parse_dirstate(dmap, copymap, st): parents = [st[:20], st[20: 40]]
--- a/mercurial/pycompat.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/pycompat.py Fri Jul 01 16:02:56 2016 -0500 @@ -11,6 +11,41 @@ from __future__ import absolute_import try: + import cPickle as pickle + pickle.dumps +except ImportError: + import pickle + pickle.dumps # silence pyflakes + +try: + import httplib + httplib.HTTPException +except ImportError: + import http.client as httplib + httplib.HTTPException + +try: + import SocketServer as socketserver + socketserver.ThreadingMixIn +except ImportError: + import socketserver + socketserver.ThreadingMixIn + +try: + import xmlrpclib + xmlrpclib.Transport +except ImportError: + import xmlrpc.client as xmlrpclib + xmlrpclib.Transport + +try: + import urlparse + urlparse.urlparse +except ImportError: + import urllib.parse as urlparse + urlparse.urlparse + +try: import cStringIO as io stringio = io.StringIO except ImportError: @@ -99,6 +134,7 @@ "pathname2url", "HTTPBasicAuthHandler", "HTTPDigestAuthHandler", + "HTTPPasswordMgrWithDefaultRealm", "ProxyHandler", "quote", "Request",
--- a/mercurial/repair.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/repair.py Fri Jul 01 16:02:56 2016 -0500 @@ -9,6 +9,7 @@ from __future__ import absolute_import import errno +import hashlib from .i18n import _ from .node import short @@ -35,7 +36,7 @@ # Include a hash of all the nodes in the filename for uniqueness allcommits = repo.set('%ln::%ln', bases, heads) allhashes = sorted(c.hex() for c in allcommits) - totalhash = util.sha1(''.join(allhashes)).hexdigest() + totalhash = hashlib.sha1(''.join(allhashes)).hexdigest() name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix) comp = None
--- a/mercurial/repoview.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/repoview.py Fri Jul 01 16:02:56 2016 -0500 @@ -9,6 +9,7 @@ from __future__ import absolute_import import copy +import hashlib import heapq import struct @@ -18,7 +19,6 @@ obsolete, phases, tags as tagsmod, - util, ) def hideablerevs(repo): @@ -102,7 +102,7 @@ it to the cache. Upon reading we can easily validate by checking the hash against the stored one and discard the cache in case the hashes don't match. """ - h = util.sha1() + h = hashlib.sha1() h.update(''.join(repo.heads())) h.update(str(hash(frozenset(hideable)))) return h.digest()
--- a/mercurial/revlog.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/revlog.py Fri Jul 01 16:02:56 2016 -0500 @@ -15,6 +15,7 @@ import collections import errno +import hashlib import os import struct import zlib @@ -40,7 +41,6 @@ _unpack = struct.unpack _compress = zlib.compress _decompress = zlib.decompress -_sha = util.sha1 # revlog header flags REVLOGV0 = 0 @@ -74,7 +74,7 @@ def offset_type(offset, type): return long(long(offset) << 16 | type) -_nullhash = _sha(nullid) +_nullhash = hashlib.sha1(nullid) def hash(text, p1, p2): """generate a hash from the given text and its parent hashes @@ -92,7 +92,7 @@ # none of the parent nodes are nullid l = [p1, p2] l.sort() - s = _sha(l[0]) + s = hashlib.sha1(l[0]) s.update(l[1]) s.update(text) return s.digest() @@ -941,8 +941,11 @@ return None except RevlogError: # parsers.c radix tree lookup gave multiple matches + # fast path: for unfiltered changelog, radix tree is accurate + if not getattr(self, 'filteredrevs', None): + raise LookupError(id, self.indexfile, + _('ambiguous identifier')) # fall through to slow path that filters hidden revisions - pass except (AttributeError, ValueError): # we are pure python, or key was too short to search radix tree pass
--- a/mercurial/revset.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/revset.py Fri Jul 01 16:02:56 2016 -0500 @@ -302,6 +302,11 @@ # helpers +def getsymbol(x): + if x and x[0] == 'symbol': + return x[1] + raise error.ParseError(_('not a symbol')) + def getstring(x, err): if x and (x[0] == 'string' or x[0] == 'symbol'): return x[1] @@ -330,13 +335,12 @@ s = methods[x[0]](repo, subset, *x[1:]) if util.safehasattr(s, 'isascending'): return s - if (repo.ui.configbool('devel', 'all-warnings') - or repo.ui.configbool('devel', 'old-revset')): - # else case should not happen, because all non-func are internal, - # ignoring for now. - if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols: - repo.ui.develwarn('revset "%s" use list instead of smartset, ' - '(upgrade your code)' % x[1][1]) + # else case should not happen, because all non-func are internal, + # ignoring for now. + if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols: + repo.ui.deprecwarn('revset "%s" uses list instead of smartset' + % x[1][1], + '3.9') return baseset(s) def _getrevsource(repo, r): @@ -387,9 +391,7 @@ r = fullreposet(repo) xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y), includepath=True) - # XXX We should combine with subset first: 'subset & baseset(...)'. This is - # necessary to ensure we preserve the order in subset. - return xs & subset + return subset & xs def andset(repo, subset, x, y): return getset(repo, getset(repo, subset, x), y) @@ -417,13 +419,14 @@ raise error.ParseError(_("can't use a key-value pair in this context")) def func(repo, subset, a, b): - if a[0] == 'symbol' and a[1] in symbols: - return symbols[a[1]](repo, subset, b) + f = getsymbol(a) + if f in symbols: + return symbols[f](repo, subset, b) keep = lambda fn: getattr(fn, '__doc__', None) is not None syms = [s for (s, fn) in symbols.items() if keep(fn)] - raise error.UnknownIdentifier(a[1], syms) + raise error.UnknownIdentifier(f, syms) # functions @@ -695,20 +698,18 @@ return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat)) -def _children(repo, narrow, parentset): +def _children(repo, subset, parentset): if not parentset: return baseset() cs = set() pr = repo.changelog.parentrevs minrev = parentset.min() - for r in narrow: + for r in subset: if r <= minrev: continue for p in pr(r): if p in parentset: cs.add(r) - # XXX using a set to feed the baseset is wrong. Sets are not ordered. - # This does not break because of other fullreposet misbehavior. return baseset(cs) @predicate('children(set)', safe=True) @@ -1150,13 +1151,9 @@ getargs(x, 0, 0, _("head takes no arguments")) hs = set() cl = repo.changelog - for b, ls in repo.branchmap().iteritems(): + for ls in repo.branchmap().itervalues(): hs.update(cl.rev(h) for h in ls) - # XXX using a set to feed the baseset is wrong. Sets are not ordered. - # This does not break because of other fullreposet misbehavior. - # XXX We should combine with subset first: 'subset & baseset(...)'. This is - # necessary to ensure we preserve the order in subset. - return baseset(hs) & subset + return subset & baseset(hs) @predicate('heads(set)', safe=True) def heads(repo, subset, x): @@ -1837,7 +1834,54 @@ return True return subset & s.filter(filter, condrepr='<roots>') -@predicate('sort(set[, [-]key...])', safe=True) +_sortkeyfuncs = { + 'rev': lambda c: c.rev(), + 'branch': lambda c: c.branch(), + 'desc': lambda c: c.description(), + 'user': lambda c: c.user(), + 'author': lambda c: c.user(), + 'date': lambda c: c.date()[0], +} + +def _getsortargs(x): + """Parse sort options into (set, [(key, reverse)], opts)""" + args = getargsdict(x, 'sort', 'set keys topo.firstbranch') + if 'set' not in args: + # i18n: "sort" is a keyword + raise error.ParseError(_('sort requires one or two arguments')) + keys = "rev" + if 'keys' in args: + # i18n: "sort" is a keyword + keys = getstring(args['keys'], _("sort spec must be a string")) + + keyflags = [] + for k in keys.split(): + fk = k + reverse = (k[0] == '-') + if reverse: + k = k[1:] + if k not in _sortkeyfuncs and k != 'topo': + raise error.ParseError(_("unknown sort key %r") % fk) + keyflags.append((k, reverse)) + + if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags): + # i18n: "topo" is a keyword + raise error.ParseError(_( + 'topo sort order cannot be combined with other sort keys')) + + opts = {} + if 'topo.firstbranch' in args: + if any(k == 'topo' for k, reverse in keyflags): + opts['topo.firstbranch'] = args['topo.firstbranch'] + else: + # i18n: "topo" and "topo.firstbranch" are keywords + raise error.ParseError(_( + 'topo.firstbranch can only be used when using the topo sort ' + 'key')) + + return args['set'], keyflags, opts + +@predicate('sort(set[, [-]key... [, ...]])', safe=True) def sort(repo, subset, x): """Sort set by keys. The default sort order is ascending, specify a key as ``-key`` to sort in descending order. @@ -1849,50 +1893,235 @@ - ``desc`` for the commit message (description), - ``user`` for user name (``author`` can be used as an alias), - ``date`` for the commit date + - ``topo`` for a reverse topographical sort + + The ``topo`` sort order cannot be combined with other sort keys. This sort + takes one optional argument, ``topo.firstbranch``, which takes a revset that + specifies what topographical branches to prioritize in the sort. + """ - # i18n: "sort" is a keyword - l = getargs(x, 1, 2, _("sort requires one or two arguments")) - keys = "rev" - if len(l) == 2: - # i18n: "sort" is a keyword - keys = getstring(l[1], _("sort spec must be a string")) - - s = l[0] - keys = keys.split() + s, keyflags, opts = _getsortargs(x) revs = getset(repo, subset, s) - if keys == ["rev"]: - revs.sort() + + if not keyflags: + return revs + if len(keyflags) == 1 and keyflags[0][0] == "rev": + revs.sort(reverse=keyflags[0][1]) return revs - elif keys == ["-rev"]: - revs.sort(reverse=True) + elif keyflags[0][0] == "topo": + firstbranch = () + if 'topo.firstbranch' in opts: + firstbranch = getset(repo, subset, opts['topo.firstbranch']) + revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch), + istopo=True) + if keyflags[0][1]: + revs.reverse() return revs + # sort() is guaranteed to be stable ctxs = [repo[r] for r in revs] - for k in reversed(keys): - if k == 'rev': - ctxs.sort(key=lambda c: c.rev()) - elif k == '-rev': - ctxs.sort(key=lambda c: c.rev(), reverse=True) - elif k == 'branch': - ctxs.sort(key=lambda c: c.branch()) - elif k == '-branch': - ctxs.sort(key=lambda c: c.branch(), reverse=True) - elif k == 'desc': - ctxs.sort(key=lambda c: c.description()) - elif k == '-desc': - ctxs.sort(key=lambda c: c.description(), reverse=True) - elif k in 'user author': - ctxs.sort(key=lambda c: c.user()) - elif k in '-user -author': - ctxs.sort(key=lambda c: c.user(), reverse=True) - elif k == 'date': - ctxs.sort(key=lambda c: c.date()[0]) - elif k == '-date': - ctxs.sort(key=lambda c: c.date()[0], reverse=True) - else: - raise error.ParseError(_("unknown sort key %r") % k) + for k, reverse in reversed(keyflags): + ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse) return baseset([c.rev() for c in ctxs]) +def _toposort(revs, parentsfunc, firstbranch=()): + """Yield revisions from heads to roots one (topo) branch at a time. + + This function aims to be used by a graph generator that wishes to minimize + the number of parallel branches and their interleaving. + + Example iteration order (numbers show the "true" order in a changelog): + + o 4 + | + o 1 + | + | o 3 + | | + | o 2 + |/ + o 0 + + Note that the ancestors of merges are understood by the current + algorithm to be on the same branch. This means no reordering will + occur behind a merge. + """ + + ### Quick summary of the algorithm + # + # This function is based around a "retention" principle. We keep revisions + # in memory until we are ready to emit a whole branch that immediately + # "merges" into an existing one. This reduces the number of parallel + # branches with interleaved revisions. + # + # During iteration revs are split into two groups: + # A) revision already emitted + # B) revision in "retention". They are stored as different subgroups. + # + # for each REV, we do the following logic: + # + # 1) if REV is a parent of (A), we will emit it. If there is a + # retention group ((B) above) that is blocked on REV being + # available, we emit all the revisions out of that retention + # group first. + # + # 2) else, we'll search for a subgroup in (B) awaiting for REV to be + # available, if such subgroup exist, we add REV to it and the subgroup is + # now awaiting for REV.parents() to be available. + # + # 3) finally if no such group existed in (B), we create a new subgroup. + # + # + # To bootstrap the algorithm, we emit the tipmost revision (which + # puts it in group (A) from above). + + revs.sort(reverse=True) + + # Set of parents of revision that have been emitted. They can be considered + # unblocked as the graph generator is already aware of them so there is no + # need to delay the revisions that reference them. + # + # If someone wants to prioritize a branch over the others, pre-filling this + # set will force all other branches to wait until this branch is ready to be + # emitted. + unblocked = set(firstbranch) + + # list of groups waiting to be displayed, each group is defined by: + # + # (revs: lists of revs waiting to be displayed, + # blocked: set of that cannot be displayed before those in 'revs') + # + # The second value ('blocked') correspond to parents of any revision in the + # group ('revs') that is not itself contained in the group. The main idea + # of this algorithm is to delay as much as possible the emission of any + # revision. This means waiting for the moment we are about to display + # these parents to display the revs in a group. + # + # This first implementation is smart until it encounters a merge: it will + # emit revs as soon as any parent is about to be emitted and can grow an + # arbitrary number of revs in 'blocked'. In practice this mean we properly + # retains new branches but gives up on any special ordering for ancestors + # of merges. The implementation can be improved to handle this better. + # + # The first subgroup is special. It corresponds to all the revision that + # were already emitted. The 'revs' lists is expected to be empty and the + # 'blocked' set contains the parents revisions of already emitted revision. + # + # You could pre-seed the <parents> set of groups[0] to a specific + # changesets to select what the first emitted branch should be. + groups = [([], unblocked)] + pendingheap = [] + pendingset = set() + + heapq.heapify(pendingheap) + heappop = heapq.heappop + heappush = heapq.heappush + for currentrev in revs: + # Heap works with smallest element, we want highest so we invert + if currentrev not in pendingset: + heappush(pendingheap, -currentrev) + pendingset.add(currentrev) + # iterates on pending rev until after the current rev have been + # processed. + rev = None + while rev != currentrev: + rev = -heappop(pendingheap) + pendingset.remove(rev) + + # Seek for a subgroup blocked, waiting for the current revision. + matching = [i for i, g in enumerate(groups) if rev in g[1]] + + if matching: + # The main idea is to gather together all sets that are blocked + # on the same revision. + # + # Groups are merged when a common blocking ancestor is + # observed. For example, given two groups: + # + # revs [5, 4] waiting for 1 + # revs [3, 2] waiting for 1 + # + # These two groups will be merged when we process + # 1. In theory, we could have merged the groups when + # we added 2 to the group it is now in (we could have + # noticed the groups were both blocked on 1 then), but + # the way it works now makes the algorithm simpler. + # + # We also always keep the oldest subgroup first. We can + # probably improve the behavior by having the longest set + # first. That way, graph algorithms could minimise the length + # of parallel lines their drawing. This is currently not done. + targetidx = matching.pop(0) + trevs, tparents = groups[targetidx] + for i in matching: + gr = groups[i] + trevs.extend(gr[0]) + tparents |= gr[1] + # delete all merged subgroups (except the one we kept) + # (starting from the last subgroup for performance and + # sanity reasons) + for i in reversed(matching): + del groups[i] + else: + # This is a new head. We create a new subgroup for it. + targetidx = len(groups) + groups.append(([], set([rev]))) + + gr = groups[targetidx] + + # We now add the current nodes to this subgroups. This is done + # after the subgroup merging because all elements from a subgroup + # that relied on this rev must precede it. + # + # we also update the <parents> set to include the parents of the + # new nodes. + if rev == currentrev: # only display stuff in rev + gr[0].append(rev) + gr[1].remove(rev) + parents = [p for p in parentsfunc(rev) if p > node.nullrev] + gr[1].update(parents) + for p in parents: + if p not in pendingset: + pendingset.add(p) + heappush(pendingheap, -p) + + # Look for a subgroup to display + # + # When unblocked is empty (if clause), we were not waiting for any + # revisions during the first iteration (if no priority was given) or + # if we emitted a whole disconnected set of the graph (reached a + # root). In that case we arbitrarily take the oldest known + # subgroup. The heuristic could probably be better. + # + # Otherwise (elif clause) if the subgroup is blocked on + # a revision we just emitted, we can safely emit it as + # well. + if not unblocked: + if len(groups) > 1: # display other subset + targetidx = 1 + gr = groups[1] + elif not gr[1] & unblocked: + gr = None + + if gr is not None: + # update the set of awaited revisions with the one from the + # subgroup + unblocked |= gr[1] + # output all revisions in the subgroup + for r in gr[0]: + yield r + # delete the subgroup that you just output + # unless it is groups[0] in which case you just empty it. + if targetidx: + del groups[targetidx] + else: + gr[0][:] = [] + # Check if we have some subgroup waiting for revisions we are not going to + # iterate over + for g in groups: + for r in g[0]: + yield r + @predicate('subrepo([pattern])') def subrepo(repo, subset, x): """Changesets that add, modify or remove the given subrepo. If no subrepo @@ -2073,7 +2302,22 @@ "parentpost": p1, } -def optimize(x, small): +def _matchonly(revs, bases): + """ + >>> f = lambda *args: _matchonly(*map(parse, args)) + >>> f('ancestors(A)', 'not ancestors(B)') + ('list', ('symbol', 'A'), ('symbol', 'B')) + """ + if (revs is not None + and revs[0] == 'func' + and getsymbol(revs[1]) == 'ancestors' + and bases is not None + and bases[0] == 'not' + and bases[1][0] == 'func' + and getsymbol(bases[1][1]) == 'ancestors'): + return ('list', revs[2], bases[1][2]) + +def _optimize(x, small): if x is None: return 0, x @@ -2083,47 +2327,36 @@ op = x[0] if op == 'minus': - return optimize(('and', x[1], ('not', x[2])), small) + return _optimize(('and', x[1], ('not', x[2])), small) elif op == 'only': - return optimize(('func', ('symbol', 'only'), - ('list', x[1], x[2])), small) + t = ('func', ('symbol', 'only'), ('list', x[1], x[2])) + return _optimize(t, small) elif op == 'onlypost': - return optimize(('func', ('symbol', 'only'), x[1]), small) + return _optimize(('func', ('symbol', 'only'), x[1]), small) elif op == 'dagrangepre': - return optimize(('func', ('symbol', 'ancestors'), x[1]), small) + return _optimize(('func', ('symbol', 'ancestors'), x[1]), small) elif op == 'dagrangepost': - return optimize(('func', ('symbol', 'descendants'), x[1]), small) + return _optimize(('func', ('symbol', 'descendants'), x[1]), small) elif op == 'rangeall': - return optimize(('range', ('string', '0'), ('string', 'tip')), small) + return _optimize(('range', ('string', '0'), ('string', 'tip')), small) elif op == 'rangepre': - return optimize(('range', ('string', '0'), x[1]), small) + return _optimize(('range', ('string', '0'), x[1]), small) elif op == 'rangepost': - return optimize(('range', x[1], ('string', 'tip')), small) + return _optimize(('range', x[1], ('string', 'tip')), small) elif op == 'negate': - return optimize(('string', - '-' + getstring(x[1], _("can't negate that"))), small) + s = getstring(x[1], _("can't negate that")) + return _optimize(('string', '-' + s), small) elif op in 'string symbol negate': return smallbonus, x # single revisions are small elif op == 'and': - wa, ta = optimize(x[1], True) - wb, tb = optimize(x[2], True) + wa, ta = _optimize(x[1], True) + wb, tb = _optimize(x[2], True) + w = min(wa, wb) # (::x and not ::y)/(not ::y and ::x) have a fast path - def isonly(revs, bases): - return ( - revs is not None - and revs[0] == 'func' - and getstring(revs[1], _('not a symbol')) == 'ancestors' - and bases is not None - and bases[0] == 'not' - and bases[1][0] == 'func' - and getstring(bases[1][1], _('not a symbol')) == 'ancestors') - - w = min(wa, wb) - if isonly(ta, tb): - return w, ('func', ('symbol', 'only'), ('list', ta[2], tb[1][2])) - if isonly(tb, ta): - return w, ('func', ('symbol', 'only'), ('list', tb[2], ta[1][2])) + tm = _matchonly(ta, tb) or _matchonly(tb, ta) + if tm: + return w, ('func', ('symbol', 'only'), tm) if tb is not None and tb[0] == 'not': return wa, ('difference', ta, tb[1]) @@ -2143,12 +2376,12 @@ else: s = '\0'.join(t[1] for w, t in ss) y = ('func', ('symbol', '_list'), ('string', s)) - w, t = optimize(y, False) + w, t = _optimize(y, False) ws.append(w) ts.append(t) del ss[:] for y in x[1:]: - w, t = optimize(y, False) + w, t = _optimize(y, False) if t is not None and (t[0] == 'string' or t[0] == 'symbol'): ss.append((w, t)) continue @@ -2166,34 +2399,34 @@ # Optimize not public() to _notpublic() because we have a fast version if x[1] == ('func', ('symbol', 'public'), None): newsym = ('func', ('symbol', '_notpublic'), None) - o = optimize(newsym, not small) + o = _optimize(newsym, not small) return o[0], o[1] else: - o = optimize(x[1], not small) + o = _optimize(x[1], not small) return o[0], (op, o[1]) elif op == 'parentpost': - o = optimize(x[1], small) + o = _optimize(x[1], small) return o[0], (op, o[1]) elif op == 'group': - return optimize(x[1], small) + return _optimize(x[1], small) elif op in 'dagrange range parent ancestorspec': if op == 'parent': # x^:y means (x^) : y, not x ^ (:y) post = ('parentpost', x[1]) if x[2][0] == 'dagrangepre': - return optimize(('dagrange', post, x[2][1]), small) + return _optimize(('dagrange', post, x[2][1]), small) elif x[2][0] == 'rangepre': - return optimize(('range', post, x[2][1]), small) - - wa, ta = optimize(x[1], small) - wb, tb = optimize(x[2], small) + return _optimize(('range', post, x[2][1]), small) + + wa, ta = _optimize(x[1], small) + wb, tb = _optimize(x[2], small) return wa + wb, (op, ta, tb) elif op == 'list': - ws, ts = zip(*(optimize(y, small) for y in x[1:])) + ws, ts = zip(*(_optimize(y, small) for y in x[1:])) return sum(ws), (op,) + ts elif op == 'func': - f = getstring(x[1], _("not a symbol")) - wa, ta = optimize(x[2], small) + f = getsymbol(x[1]) + wa, ta = _optimize(x[2], small) if f in ("author branch closed date desc file grep keyword " "outgoing user"): w = 10 # slow @@ -2212,33 +2445,32 @@ return w + wa, (op, x[1], ta) return 1, x +def optimize(tree): + _weight, newtree = _optimize(tree, small=True) + return newtree + # the set of valid characters for the initial letter of symbols in # alias declarations and definitions _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)] if c.isalnum() or c in '._@$' or ord(c) > 127) -def _tokenizealias(program, lookup=None): - """Parse alias declaration/definition into a stream of tokens - - This allows symbol names to use also ``$`` as an initial letter - (for backward compatibility), and callers of this function should - examine whether ``$`` is used also for unexpected symbols or not. - """ - return tokenize(program, lookup=lookup, - syminitletters=_aliassyminitletters) - -def _parsealias(spec): - """Parse alias declaration/definition ``spec`` - - >>> _parsealias('foo($1)') +def _parsewith(spec, lookup=None, syminitletters=None): + """Generate a parse tree of given spec with given tokenizing options + + >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters) ('func', ('symbol', 'foo'), ('symbol', '$1')) - >>> _parsealias('foo bar') + >>> _parsewith('$1') + Traceback (most recent call last): + ... + ParseError: ("syntax error in revset '$1'", 0) + >>> _parsewith('foo bar') Traceback (most recent call last): ... ParseError: ('invalid token', 4) """ p = parser.parser(elements) - tree, pos = p.parse(_tokenizealias(spec)) + tree, pos = p.parse(tokenize(spec, lookup=lookup, + syminitletters=syminitletters)) if pos != len(spec): raise error.ParseError(_('invalid token'), pos) return parser.simplifyinfixops(tree, ('list', 'or')) @@ -2246,7 +2478,16 @@ class _aliasrules(parser.basealiasrules): """Parsing and expansion rule set of revset aliases""" _section = _('revset alias') - _parse = staticmethod(_parsealias) + + @staticmethod + def _parse(spec): + """Parse alias declaration/definition ``spec`` + + This allows symbol names to use also ``$`` as an initial letter + (for backward compatibility), and callers of this function should + examine whether ``$`` is used also for unexpected symbols or not. + """ + return _parsewith(spec, syminitletters=_aliassyminitletters) @staticmethod def _trygetfunc(tree): @@ -2286,24 +2527,15 @@ return tuple(foldconcat(t) for t in tree) def parse(spec, lookup=None): - p = parser.parser(elements) - tree, pos = p.parse(tokenize(spec, lookup=lookup)) - if pos != len(spec): - raise error.ParseError(_("invalid token"), pos) - return parser.simplifyinfixops(tree, ('list', 'or')) + return _parsewith(spec, lookup=lookup) def posttreebuilthook(tree, repo): # hook for extensions to execute code on the optimized tree pass def match(ui, spec, repo=None): - if not spec: - raise error.ParseError(_("empty query")) - lookup = None - if repo: - lookup = repo.__contains__ - tree = parse(spec, lookup) - return _makematcher(ui, tree, repo) + """Create a matcher for a single revision spec.""" + return matchany(ui, [spec], repo=repo) def matchany(ui, specs, repo=None): """Create a matcher that will include any revisions matching one of the @@ -2327,7 +2559,7 @@ if ui: tree = expandaliases(ui, tree, showwarning=ui.warn) tree = foldconcat(tree) - weight, tree = optimize(tree, True) + tree = optimize(tree) posttreebuilthook(tree, repo) def mfunc(repo, subset=None): if subset is None: @@ -2426,7 +2658,8 @@ ret += listexp(list(args[arg]), d) arg += 1 else: - raise error.Abort('unexpected revspec format character %s' % d) + raise error.Abort(_('unexpected revspec format character %s') + % d) else: ret += c pos += 1 @@ -2506,6 +2739,10 @@ """True if the set will iterate in descending order""" raise NotImplementedError() + def istopo(self): + """True if the set will iterate in topographical order""" + raise NotImplementedError() + @util.cachefunc def min(self): """return the minimum element in the set""" @@ -2591,12 +2828,13 @@ Every method in this class should be implemented by any smartset class. """ - def __init__(self, data=(), datarepr=None): + def __init__(self, data=(), datarepr=None, istopo=False): """ datarepr: a tuple of (format, obj, ...), a function or an object that provides a printable representation of the given data. """ self._ascending = None + self._istopo = istopo if not isinstance(data, list): if isinstance(data, set): self._set = data @@ -2639,12 +2877,14 @@ def sort(self, reverse=False): self._ascending = not bool(reverse) + self._istopo = False def reverse(self): if self._ascending is None: self._list.reverse() else: self._ascending = not self._ascending + self._istopo = False def __len__(self): return len(self._list) @@ -2665,6 +2905,14 @@ return True return self._ascending is not None and not self._ascending + def istopo(self): + """Is the collection is in topographical order or not. + + This is part of the mandatory API for smartset.""" + if len(self) <= 1: + return True + return self._istopo + def first(self): if self: if self._ascending is None: @@ -2741,9 +2989,16 @@ return lambda: self._iterfilter(it()) def __nonzero__(self): - fast = self.fastasc - if fast is None: - fast = self.fastdesc + fast = None + candidates = [self.fastasc if self.isascending() else None, + self.fastdesc if self.isdescending() else None, + self.fastasc, + self.fastdesc] + for candidate in candidates: + if candidate is not None: + fast = candidate + break + if fast is not None: it = fast() else: @@ -2773,6 +3028,9 @@ def isdescending(self): return self._subset.isdescending() + def istopo(self): + return self._subset.istopo() + def first(self): for x in self: return x @@ -2816,14 +3074,14 @@ # Consume both iterators in an ordered way until one is empty while True: if val1 is None: - val1 = iter1.next() + val1 = next(iter1) if val2 is None: - val2 = iter2.next() - next = choice(val1, val2) - yield next - if val1 == next: + val2 = next(iter2) + n = choice(val1, val2) + yield n + if val1 == n: val1 = None - if val2 == next: + if val2 == n: val2 = None except StopIteration: # Flush any remaining values and consume the other one @@ -3019,6 +3277,12 @@ def isdescending(self): return self._ascending is not None and not self._ascending + def istopo(self): + # not worth the trouble asserting if the two sets combined are still + # in topographical order. Use the sort() predicate to explicitly sort + # again instead. + return False + def reverse(self): if self._ascending is None: self._list.reverse() @@ -3186,6 +3450,12 @@ def isdescending(self): return not self._ascending + def istopo(self): + # not worth the trouble asserting if the two sets combined are still + # in topographical order. Use the sort() predicate to explicitly sort + # again instead. + return False + def first(self): if self._ascending: it = self.fastasc @@ -3248,6 +3518,12 @@ def reverse(self): self._ascending = not self._ascending + def istopo(self): + # not worth the trouble asserting if the two sets combined are still + # in topographical order. Use the sort() predicate to explicitly sort + # again instead. + return False + def _iterfilter(self, iterrange): s = self._hiddenrevs for r in iterrange:
--- a/mercurial/scmutil.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/scmutil.py Fri Jul 01 16:02:56 2016 -0500 @@ -10,6 +10,7 @@ import contextlib import errno import glob +import hashlib import os import re import shutil @@ -224,7 +225,7 @@ key = None revs = sorted(r for r in cl.filteredrevs if r <= maxrev) if revs: - s = util.sha1() + s = hashlib.sha1() for rev in revs: s.update('%s;' % rev) key = s.digest() @@ -377,8 +378,24 @@ def readlock(self, path): return util.readlock(self.join(path)) - def rename(self, src, dst): - return util.rename(self.join(src), self.join(dst)) + def rename(self, src, dst, checkambig=False): + """Rename from src to dst + + checkambig argument is used with util.filestat, and is useful + only if destination file is guarded by any lock + (e.g. repo.lock or repo.wlock). + """ + dstpath = self.join(dst) + oldstat = checkambig and util.filestat(dstpath) + if oldstat and oldstat.stat: + ret = util.rename(self.join(src), dstpath) + newstat = util.filestat(dstpath) + if newstat.isambig(oldstat): + # stat of renamed file is ambiguous to original one + advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff + os.utime(dstpath, (advanced, advanced)) + return ret + return util.rename(self.join(src), dstpath) def readlink(self, path): return os.readlink(self.join(path)) @@ -451,7 +468,8 @@ # have a use case. vfs = getattr(self, 'vfs', self) if getattr(vfs, '_backgroundfilecloser', None): - raise error.Abort('can only have 1 active background file closer') + raise error.Abort( + _('can only have 1 active background file closer')) with backgroundfilecloser(ui, expectedcount=expectedcount) as bfc: try: @@ -502,7 +520,7 @@ os.chmod(name, self.createmode & 0o666) def __call__(self, path, mode="r", text=False, atomictemp=False, - notindexed=False, backgroundclose=False): + notindexed=False, backgroundclose=False, checkambig=False): '''Open ``path`` file, which is relative to vfs root. Newly created directories are marked as "not to be indexed by @@ -521,6 +539,10 @@ closing a file on a background thread and reopening it. (If the file were opened multiple times, there could be unflushed data because the original file handle hasn't been flushed/closed yet.) + + ``checkambig`` argument is passed to atomictemplfile (valid + only for writing), and is useful only if target file is + guarded by any lock (e.g. repo.lock or repo.wlock). ''' if self._audit: r = util.checkosfilename(path) @@ -540,7 +562,8 @@ if basename: if atomictemp: util.makedirs(dirname, self.createmode, notindexed) - return util.atomictempfile(f, mode, self.createmode) + return util.atomictempfile(f, mode, self.createmode, + checkambig=checkambig) try: if 'w' in mode: util.unlink(f) @@ -568,8 +591,9 @@ if backgroundclose: if not self._backgroundfilecloser: - raise error.Abort('backgroundclose can only be used when a ' + raise error.Abort(_('backgroundclose can only be used when a ' 'backgroundclosing context manager is active') + ) fp = delayclosedfile(fp, self._backgroundfilecloser) @@ -640,7 +664,7 @@ def __call__(self, path, mode='r', *args, **kw): if mode not in ('r', 'rb'): - raise error.Abort('this vfs is read only') + raise error.Abort(_('this vfs is read only')) return self.vfs(path, mode, *args, **kw) def join(self, path, *insidef): @@ -751,7 +775,7 @@ def _pairspec(revspec): tree = revset.parse(revspec) - tree = revset.optimize(tree, True)[1] # fix up "x^:y" -> "(x^):y" + tree = revset.optimize(tree) # fix up "x^:y" -> "(x^):y" return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall') def revpair(repo, revs): @@ -784,10 +808,29 @@ return repo.lookup(first), repo.lookup(second) -def revrange(repo, revs): - """Yield revision as strings from a list of revision specifications.""" +def revrange(repo, specs): + """Execute 1 to many revsets and return the union. + + This is the preferred mechanism for executing revsets using user-specified + config options, such as revset aliases. + + The revsets specified by ``specs`` will be executed via a chained ``OR`` + expression. If ``specs`` is empty, an empty result is returned. + + ``specs`` can contain integers, in which case they are assumed to be + revision numbers. + + It is assumed the revsets are already formatted. If you have arguments + that need to be expanded in the revset, call ``revset.formatspec()`` + and pass the result as an element of ``specs``. + + Specifying a single revset is allowed. + + Returns a ``revset.abstractsmartset`` which is a list-like interface over + integer revisions. + """ allspecs = [] - for spec in revs: + for spec in specs: if isinstance(spec, int): spec = revset.formatspec('rev(%d)', spec) allspecs.append(spec) @@ -1183,6 +1226,9 @@ return self def __get__(self, obj, type=None): + # if accessed on the class, return the descriptor itself. + if obj is None: + return self # do we need to check if the file changed? if self.name in obj.__dict__: assert self.name in obj._filecache, self.name @@ -1358,8 +1404,8 @@ def close(self, fh): """Schedule a file for closing.""" if not self._entered: - raise error.Abort('can only call close() when context manager ' - 'active') + raise error.Abort(_('can only call close() when context manager ' + 'active')) # If a background thread encountered an exception, raise now so we fail # fast. Otherwise we may potentially go on for minutes until the error @@ -1375,4 +1421,3 @@ return self._queue.put(fh, block=True, timeout=None) -
--- a/mercurial/similar.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/similar.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,6 +7,8 @@ from __future__ import absolute_import +import hashlib + from .i18n import _ from . import ( bdiff, @@ -27,14 +29,14 @@ for i, fctx in enumerate(removed): repo.ui.progress(_('searching for exact renames'), i, total=numfiles, unit=_('files')) - h = util.sha1(fctx.data()).digest() + h = hashlib.sha1(fctx.data()).digest() hashes[h] = fctx # For each added file, see if it corresponds to a removed file. for i, fctx in enumerate(added): repo.ui.progress(_('searching for exact renames'), i + len(removed), total=numfiles, unit=_('files')) - h = util.sha1(fctx.data()).digest() + h = hashlib.sha1(fctx.data()).digest() if h in hashes: yield (hashes[h], fctx) @@ -106,4 +108,3 @@ for (a, b, score) in _findsimilarmatches(repo, sorted(addedfiles), sorted(removedfiles), threshold): yield (a.path(), b.path(), score) -
--- a/mercurial/sshpeer.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/sshpeer.py Fri Jul 01 16:02:56 2016 -0500 @@ -307,7 +307,7 @@ r = self._call(cmd, **args) if r: # XXX needs to be made better - raise error.Abort('unexpected remote reply: %s' % r) + raise error.Abort(_('unexpected remote reply: %s') % r) while True: d = fp.read(4096) if not d:
--- a/mercurial/sshserver.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/sshserver.py Fri Jul 01 16:02:56 2016 -0500 @@ -11,6 +11,7 @@ import os import sys +from .i18n import _ from . import ( error, hook, @@ -40,7 +41,7 @@ argline = self.fin.readline()[:-1] arg, l = argline.split() if arg not in keys: - raise error.Abort("unexpected parameter %r" % arg) + raise error.Abort(_("unexpected parameter %r") % arg) if arg == '*': star = {} for k in xrange(int(l)):
--- a/mercurial/sslutil.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/sslutil.py Fri Jul 01 16:02:56 2016 -0500 @@ -9,6 +9,7 @@ from __future__ import absolute_import +import hashlib import os import re import ssl @@ -76,15 +77,15 @@ def load_verify_locations(self, cafile=None, capath=None, cadata=None): if capath: - raise error.Abort('capath not supported') + raise error.Abort(_('capath not supported')) if cadata: - raise error.Abort('cadata not supported') + raise error.Abort(_('cadata not supported')) self._cacerts = cafile def set_ciphers(self, ciphers): if not self._supportsciphers: - raise error.Abort('setting ciphers not supported') + raise error.Abort(_('setting ciphers not supported')) self._ciphers = ciphers @@ -107,8 +108,118 @@ return ssl.wrap_socket(socket, **args) -def wrapsocket(sock, keyfile, certfile, ui, cert_reqs=ssl.CERT_NONE, - ca_certs=None, serverhostname=None): +def _hostsettings(ui, hostname): + """Obtain security settings for a hostname. + + Returns a dict of settings relevant to that hostname. + """ + s = { + # Whether we should attempt to load default/available CA certs + # if an explicit ``cafile`` is not defined. + 'allowloaddefaultcerts': True, + # List of 2-tuple of (hash algorithm, hash). + 'certfingerprints': [], + # Path to file containing concatenated CA certs. Used by + # SSLContext.load_verify_locations(). + 'cafile': None, + # Whether certificate verification should be disabled. + 'disablecertverification': False, + # Whether the legacy [hostfingerprints] section has data for this host. + 'legacyfingerprint': False, + # ssl.CERT_* constant used by SSLContext.verify_mode. + 'verifymode': None, + } + + # Look for fingerprints in [hostsecurity] section. Value is a list + # of <alg>:<fingerprint> strings. + fingerprints = ui.configlist('hostsecurity', '%s:fingerprints' % hostname, + []) + for fingerprint in fingerprints: + if not (fingerprint.startswith(('sha1:', 'sha256:', 'sha512:'))): + raise error.Abort(_('invalid fingerprint for %s: %s') % ( + hostname, fingerprint), + hint=_('must begin with "sha1:", "sha256:", ' + 'or "sha512:"')) + + alg, fingerprint = fingerprint.split(':', 1) + fingerprint = fingerprint.replace(':', '').lower() + s['certfingerprints'].append((alg, fingerprint)) + + # Fingerprints from [hostfingerprints] are always SHA-1. + for fingerprint in ui.configlist('hostfingerprints', hostname, []): + fingerprint = fingerprint.replace(':', '').lower() + s['certfingerprints'].append(('sha1', fingerprint)) + s['legacyfingerprint'] = True + + # If a host cert fingerprint is defined, it is the only thing that + # matters. No need to validate CA certs. + if s['certfingerprints']: + s['verifymode'] = ssl.CERT_NONE + s['allowloaddefaultcerts'] = False + + # If --insecure is used, don't take CAs into consideration. + elif ui.insecureconnections: + s['disablecertverification'] = True + s['verifymode'] = ssl.CERT_NONE + s['allowloaddefaultcerts'] = False + + if ui.configbool('devel', 'disableloaddefaultcerts'): + s['allowloaddefaultcerts'] = False + + # If both fingerprints and a per-host ca file are specified, issue a warning + # because users should not be surprised about what security is or isn't + # being performed. + cafile = ui.config('hostsecurity', '%s:verifycertsfile' % hostname) + if s['certfingerprints'] and cafile: + ui.warn(_('(hostsecurity.%s:verifycertsfile ignored when host ' + 'fingerprints defined; using host fingerprints for ' + 'verification)\n') % hostname) + + # Try to hook up CA certificate validation unless something above + # makes it not necessary. + if s['verifymode'] is None: + # Look at per-host ca file first. + if cafile: + cafile = util.expandpath(cafile) + if not os.path.exists(cafile): + raise error.Abort(_('path specified by %s does not exist: %s') % + ('hostsecurity.%s:verifycertsfile' % hostname, + cafile)) + s['cafile'] = cafile + else: + # Find global certificates file in config. + cafile = ui.config('web', 'cacerts') + + if cafile: + cafile = util.expandpath(cafile) + if not os.path.exists(cafile): + raise error.Abort(_('could not find web.cacerts: %s') % + cafile) + else: + # No global CA certs. See if we can load defaults. + cafile = _defaultcacerts() + if cafile: + ui.debug('using %s to enable OS X system CA\n' % cafile) + + s['cafile'] = cafile + + # Require certificate validation if CA certs are being loaded and + # verification hasn't been disabled above. + if cafile or (_canloaddefaultcerts and s['allowloaddefaultcerts']): + s['verifymode'] = ssl.CERT_REQUIRED + else: + # At this point we don't have a fingerprint, aren't being + # explicitly insecure, and can't load CA certs. Connecting + # is insecure. We allow the connection and abort during + # validation (once we have the fingerprint to print to the + # user). + s['verifymode'] = ssl.CERT_NONE + + assert s['verifymode'] is not None + + return s + +def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None): """Add SSL/TLS to a socket. This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane @@ -121,6 +232,11 @@ server (and client) support SNI, this tells the server which certificate to use. """ + if not serverhostname: + raise error.Abort(_('serverhostname argument is required')) + + settings = _hostsettings(ui, serverhostname) + # Despite its name, PROTOCOL_SSLv23 selects the highest protocol # that both ends support, including TLS protocols. On legacy stacks, # the highest it likely goes in TLS 1.0. On modern stacks, it can @@ -146,7 +262,7 @@ sslcontext.options |= OP_NO_SSLv2 | OP_NO_SSLv3 # This still works on our fake SSLContext. - sslcontext.verify_mode = cert_reqs + sslcontext.verify_mode = settings['verifymode'] if certfile is not None: def password(): @@ -154,18 +270,50 @@ return ui.getpass(_('passphrase for %s: ') % f, '') sslcontext.load_cert_chain(certfile, keyfile, password) - if ca_certs is not None: - sslcontext.load_verify_locations(cafile=ca_certs) - else: + if settings['cafile'] is not None: + try: + sslcontext.load_verify_locations(cafile=settings['cafile']) + except ssl.SSLError as e: + raise error.Abort(_('error loading CA file %s: %s') % ( + settings['cafile'], e.args[1]), + hint=_('file is empty or malformed?')) + caloaded = True + elif settings['allowloaddefaultcerts']: # This is a no-op on old Python. sslcontext.load_default_certs() + caloaded = True + else: + caloaded = False - sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname) + try: + sslsocket = sslcontext.wrap_socket(sock, server_hostname=serverhostname) + except ssl.SSLError: + # If we're doing certificate verification and no CA certs are loaded, + # that is almost certainly the reason why verification failed. Provide + # a hint to the user. + # Only modern ssl module exposes SSLContext.get_ca_certs() so we can + # only show this warning if modern ssl is available. + if (caloaded and settings['verifymode'] == ssl.CERT_REQUIRED and + modernssl and not sslcontext.get_ca_certs()): + ui.warn(_('(an attempt was made to load CA certificates but none ' + 'were loaded; see ' + 'https://mercurial-scm.org/wiki/SecureConnections for ' + 'how to configure Mercurial to avoid this error)\n')) + raise + # check if wrap_socket failed silently because socket had been # closed # - see http://bugs.python.org/issue13721 if not sslsocket.cipher(): raise error.Abort(_('ssl connection failed')) + + sslsocket._hgstate = { + 'caloaded': caloaded, + 'hostname': serverhostname, + 'settings': settings, + 'ui': ui, + } + return sslsocket class wildcarderror(Exception): @@ -268,10 +416,6 @@ else: return _('no commonName or subjectAltName found in certificate') - -# CERT_REQUIRED means fetch the cert from the server all the time AND -# validate it against the CA store provided in web.cacerts. - def _plainapplepython(): """return true if this seems to be a pure Apple Python that * is unfrozen and presumably has the whole mercurial module in the file @@ -287,96 +431,91 @@ exe.startswith('/system/library/frameworks/python.framework/')) def _defaultcacerts(): - """return path to CA certificates; None for system's store; ! to disable""" + """return path to default CA certificates or None.""" if _plainapplepython(): dummycert = os.path.join(os.path.dirname(__file__), 'dummycert.pem') if os.path.exists(dummycert): return dummycert - if _canloaddefaultcerts: - return None - return '!' + + return None + +def validatesocket(sock): + """Validate a socket meets security requiremnets. + + The passed socket must have been created with ``wrapsocket()``. + """ + host = sock._hgstate['hostname'] + ui = sock._hgstate['ui'] + settings = sock._hgstate['settings'] + + try: + peercert = sock.getpeercert(True) + peercert2 = sock.getpeercert() + except AttributeError: + raise error.Abort(_('%s ssl connection error') % host) -def sslkwargs(ui, host): - kws = {'ui': ui} - hostfingerprint = ui.config('hostfingerprints', host) - if hostfingerprint: - return kws - cacerts = ui.config('web', 'cacerts') - if cacerts == '!': - pass - elif cacerts: - cacerts = util.expandpath(cacerts) - if not os.path.exists(cacerts): - raise error.Abort(_('could not find web.cacerts: %s') % cacerts) - else: - cacerts = _defaultcacerts() - if cacerts and cacerts != '!': - ui.debug('using %s to enable OS X system CA\n' % cacerts) - ui.setconfig('web', 'cacerts', cacerts, 'defaultcacerts') - if cacerts != '!': - kws.update({'ca_certs': cacerts, - 'cert_reqs': ssl.CERT_REQUIRED, - }) - return kws + if not peercert: + raise error.Abort(_('%s certificate error: ' + 'no certificate received') % host) -class validator(object): - def __init__(self, ui, host): - self.ui = ui - self.host = host - - def __call__(self, sock, strict=False): - host = self.host + if settings['disablecertverification']: + # We don't print the certificate fingerprint because it shouldn't + # be necessary: if the user requested certificate verification be + # disabled, they presumably already saw a message about the inability + # to verify the certificate and this message would have printed the + # fingerprint. So printing the fingerprint here adds little to no + # value. + ui.warn(_('warning: connection security to %s is disabled per current ' + 'settings; communication is susceptible to eavesdropping ' + 'and tampering\n') % host) + return - if not sock.cipher(): # work around http://bugs.python.org/issue13721 - raise error.Abort(_('%s ssl connection error') % host) - try: - peercert = sock.getpeercert(True) - peercert2 = sock.getpeercert() - except AttributeError: - raise error.Abort(_('%s ssl connection error') % host) + # If a certificate fingerprint is pinned, use it and only it to + # validate the remote cert. + peerfingerprints = { + 'sha1': hashlib.sha1(peercert).hexdigest(), + 'sha256': hashlib.sha256(peercert).hexdigest(), + 'sha512': hashlib.sha512(peercert).hexdigest(), + } - if not peercert: - raise error.Abort(_('%s certificate error: ' - 'no certificate received') % host) + def fmtfingerprint(s): + return ':'.join([s[x:x + 2] for x in range(0, len(s), 2)]) + + nicefingerprint = 'sha256:%s' % fmtfingerprint(peerfingerprints['sha256']) + + if settings['certfingerprints']: + for hash, fingerprint in settings['certfingerprints']: + if peerfingerprints[hash].lower() == fingerprint: + ui.debug('%s certificate matched fingerprint %s:%s\n' % + (host, hash, fmtfingerprint(fingerprint))) + return - # If a certificate fingerprint is pinned, use it and only it to - # validate the remote cert. - hostfingerprints = self.ui.configlist('hostfingerprints', host) - peerfingerprint = util.sha1(peercert).hexdigest() - nicefingerprint = ":".join([peerfingerprint[x:x + 2] - for x in xrange(0, len(peerfingerprint), 2)]) - if hostfingerprints: - fingerprintmatch = False - for hostfingerprint in hostfingerprints: - if peerfingerprint.lower() == \ - hostfingerprint.replace(':', '').lower(): - fingerprintmatch = True - break - if not fingerprintmatch: - raise error.Abort(_('certificate for %s has unexpected ' - 'fingerprint %s') % (host, nicefingerprint), - hint=_('check hostfingerprint configuration')) - self.ui.debug('%s certificate matched fingerprint %s\n' % - (host, nicefingerprint)) - return + # Pinned fingerprint didn't match. This is a fatal error. + if settings['legacyfingerprint']: + section = 'hostfingerprint' + nice = fmtfingerprint(peerfingerprints['sha1']) + else: + section = 'hostsecurity' + nice = '%s:%s' % (hash, fmtfingerprint(peerfingerprints[hash])) + raise error.Abort(_('certificate for %s has unexpected ' + 'fingerprint %s') % (host, nice), + hint=_('check %s configuration') % section) - # No pinned fingerprint. Establish trust by looking at the CAs. - cacerts = self.ui.config('web', 'cacerts') - if cacerts != '!': - msg = _verifycert(peercert2, host) - if msg: - raise error.Abort(_('%s certificate error: %s') % (host, msg), - hint=_('configure hostfingerprint %s or use ' - '--insecure to connect insecurely') % - nicefingerprint) - self.ui.debug('%s certificate successfully verified\n' % host) - elif strict: - raise error.Abort(_('%s certificate with fingerprint %s not ' - 'verified') % (host, nicefingerprint), - hint=_('check hostfingerprints or web.cacerts ' - 'config setting')) - else: - self.ui.warn(_('warning: %s certificate with fingerprint %s not ' - 'verified (check hostfingerprints or web.cacerts ' - 'config setting)\n') % - (host, nicefingerprint)) + # Security is enabled but no CAs are loaded. We can't establish trust + # for the cert so abort. + if not sock._hgstate['caloaded']: + raise error.Abort( + _('unable to verify security of %s (no loaded CA certificates); ' + 'refusing to connect') % host, + hint=_('see https://mercurial-scm.org/wiki/SecureConnections for ' + 'how to configure Mercurial to avoid this error or set ' + 'hostsecurity.%s:fingerprints=%s to trust this server') % + (host, nicefingerprint)) + + msg = _verifycert(peercert2, host) + if msg: + raise error.Abort(_('%s certificate error: %s') % (host, msg), + hint=_('set hostsecurity.%s:certfingerprints=%s ' + 'config setting or use --insecure to connect ' + 'insecurely') % + (host, nicefingerprint))
--- a/mercurial/store.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/store.py Fri Jul 01 16:02:56 2016 -0500 @@ -8,6 +8,7 @@ from __future__ import absolute_import import errno +import hashlib import os import stat @@ -19,8 +20,6 @@ util, ) -_sha = util.sha1 - # This avoids a collision between a file named foo and a dir named # foo.i or foo.d def _encodedir(path): @@ -57,6 +56,23 @@ .replace(".i.hg/", ".i/") .replace(".hg.hg/", ".hg/")) +def _reserved(): + ''' characters that are problematic for filesystems + + * ascii escapes (0..31) + * ascii hi (126..255) + * windows specials + + these characters will be escaped by encodefunctions + ''' + winreserved = [ord(x) for x in '\\:*?"<>|'] + for x in range(32): + yield x + for x in range(126, 256): + yield x + for x in winreserved: + yield x + def _buildencodefun(): ''' >>> enc, dec = _buildencodefun() @@ -82,11 +98,10 @@ 'the\\x07quick\\xadshot' ''' e = '_' - winreserved = [ord(x) for x in '\\:*?"<>|'] cmap = dict([(chr(x), chr(x)) for x in xrange(127)]) - for x in (range(32) + range(126, 256) + winreserved): + for x in _reserved(): cmap[chr(x)] = "~%02x" % x - for x in range(ord("A"), ord("Z") + 1) + [ord(e)]: + for x in list(range(ord("A"), ord("Z") + 1)) + [ord(e)]: cmap[chr(x)] = e + chr(x).lower() dmap = {} for k, v in cmap.iteritems(): @@ -134,9 +149,8 @@ >>> f('the\x07quick\xADshot') 'the~07quick~adshot' ''' - winreserved = [ord(x) for x in '\\:*?"<>|'] cmap = dict([(chr(x), chr(x)) for x in xrange(127)]) - for x in (range(32) + range(126, 256) + winreserved): + for x in _reserved(): cmap[chr(x)] = "~%02x" % x for x in range(ord("A"), ord("Z") + 1): cmap[chr(x)] = chr(x).lower() @@ -196,7 +210,7 @@ _maxshortdirslen = 8 * (_dirprefixlen + 1) - 4 def _hashencode(path, dotencode): - digest = _sha(path).hexdigest() + digest = hashlib.sha1(path).hexdigest() le = lowerencode(path[5:]).split('/') # skips prefix 'data/' or 'meta/' parts = _auxencode(le, dotencode) basename = parts[-1]
--- a/mercurial/subrepo.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/subrepo.py Fri Jul 01 16:02:56 2016 -0500 @@ -9,6 +9,7 @@ import copy import errno +import hashlib import os import posixpath import re @@ -50,7 +51,7 @@ def _getstorehashcachename(remotepath): '''get a unique filename for the store hash cache of a remote repository''' - return util.sha1(_expandedabspath(remotepath)).hexdigest()[0:12] + return hashlib.sha1(_expandedabspath(remotepath)).hexdigest()[0:12] class SubrepoAbort(error.Abort): """Exception class used to avoid handling a subrepo error more than once""" @@ -585,7 +586,7 @@ return 1 def revert(self, substate, *pats, **opts): - self.ui.warn('%s: reverting %s subrepos is unsupported\n' \ + self.ui.warn(_('%s: reverting %s subrepos is unsupported\n') \ % (substate[0], substate[2])) return [] @@ -659,7 +660,7 @@ yield '# %s\n' % _expandedabspath(remotepath) vfs = self._repo.vfs for relname in filelist: - filehash = util.sha1(vfs.tryread(relname)).hexdigest() + filehash = hashlib.sha1(vfs.tryread(relname)).hexdigest() yield '%s = %s\n' % (relname, filehash) @propertycache @@ -1413,7 +1414,7 @@ if command in ('cat-file', 'symbolic-ref'): return retdata, p.returncode # for all others, abort - raise error.Abort('git %s error %d in %s' % + raise error.Abort(_('git %s error %d in %s') % (command, p.returncode, self._relpath)) return retdata, p.returncode
--- a/mercurial/tags.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/tags.py Fri Jul 01 16:02:56 2016 -0500 @@ -292,7 +292,7 @@ cachehash = None if cachefile: try: - validline = cachelines.next() + validline = next(cachelines) validline = validline.split() cacherev = int(validline[0]) cachenode = bin(validline[1])
--- a/mercurial/templater.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templater.py Fri Jul 01 16:02:56 2016 -0500 @@ -724,6 +724,25 @@ return minirst.format(text, style=style, keep=['verbose']) +@templatefunc('separate(sep, args)') +def separate(context, mapping, args): + """Add a separator between non-empty arguments.""" + if not args: + # i18n: "separate" is a keyword + raise error.ParseError(_("separate expects at least one argument")) + + sep = evalstring(context, mapping, args[0]) + first = True + for arg in args[1:]: + argstr = evalstring(context, mapping, arg) + if not argstr: + continue + if first: + first = False + else: + yield sep + yield argstr + @templatefunc('shortest(node, minlength=4)') def shortest(context, mapping, args): """Obtain the shortest representation of
--- a/mercurial/templates/atom/branchentry.tmpl Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/atom/branchentry.tmpl Fri Jul 01 16:02:56 2016 -0500 @@ -4,5 +4,5 @@ <id>{urlbase}{url|urlescape}#branch-{node}</id> <updated>{date|rfc3339date}</updated> <published>{date|rfc3339date}</published> - <content type="text"><![CDATA[{branch|strip|escape|addbreaks}]]></content> + <content type="text">{branch|strip|escape}</content> </entry>
--- a/mercurial/templates/atom/changelogentry.tmpl Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/atom/changelogentry.tmpl Fri Jul 01 16:02:56 2016 -0500 @@ -9,35 +9,35 @@ <updated>{date|rfc3339date}</updated> <published>{date|rfc3339date}</published> <content type="xhtml"> - <table xmlns="http://www.w3.org/1999/xhtml"> - <tr> - <th style="text-align:left;">changeset</th> - <td>{node|short}</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>{inbranch%"{name|escape}"}{branches%"{name|escape}"}</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td>{bookmarks%"{name|escape}"}</td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td>{tags%"{name|escape}"}</td> - </tr> - <tr> - <th style="text-align:left;">user</th> - <td>{author|obfuscate}</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>{desc|strip|escape|websub|addbreaks|nonempty}</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>{files}</td> - </tr> - </table> + <table xmlns="http://www.w3.org/1999/xhtml"> + <tr> + <th style="text-align:left;">changeset</th> + <td>{node|short}</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>{inbranch%"{name|escape}"}{branches%"{name|escape}"}</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td>{bookmarks%"{name|escape}"}</td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td>{tags%"{name|escape}"}</td> + </tr> + <tr> + <th style="text-align:left;">user</th> + <td>{author|obfuscate}</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>{desc|strip|escape|websub|addbreaks|nonempty}</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>{files}</td> + </tr> + </table> </content> </entry>
--- a/mercurial/templates/atom/map Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/atom/map Fri Jul 01 16:02:56 2016 -0500 @@ -5,7 +5,6 @@ changelog = changelog.tmpl changelogentry = changelogentry.tmpl filelog = filelog.tmpl -filelogentry = filelogentry.tmpl tags = tags.tmpl tagentry = tagentry.tmpl bookmarks = bookmarks.tmpl
--- a/mercurial/templates/gitweb/map Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/gitweb/map Fri Jul 01 16:02:56 2016 -0500 @@ -95,10 +95,12 @@ fileline = ' <a href="#{lineid}"></a><span id="{lineid}">{strip(line|escape, '\r\n')}</span>' annotateline = ' - <tr id="{lineid}" style="font-family:monospace" class="parity{parity}"> + <tr id="{lineid}" style="font-family:monospace" class="parity{parity}{ifeq(node, originalnode, ' thisrev')}"> <td class="linenr" style="text-align: right;"> - <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}" - title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a> + {if(blockhead, + '<a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}" + title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>', + '')} </td> <td><pre><a class="linenr" href="#{lineid}">{linenumber}</a></pre></td> <td><pre>{line|escape}</pre></td>
--- a/mercurial/templates/json/map Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/json/map Fri Jul 01 16:02:56 2016 -0500 @@ -1,6 +1,26 @@ mimetype = 'application/json' -filerevision = '"not yet implemented"' -search = '"not yet implemented"' +filerevision = '\{ + "node": {node|json}, + "path": {file|json}, + "date": {date|json}, + "desc": {desc|utf8|json}, + "branch": {if(branch, branch%changesetbranch, "default"|json)}, + "bookmarks": [{join(bookmarks%changelistentryname, ", ")}], + "tags": [{join(tags%changelistentryname, ", ")}], + "user": {author|utf8|json}, + "parents": [{join(parent%changesetparent, ", ")}], + "phase": {phase|json}, + "lines": [{join(text%lineentry, ", ")}] + }' +lineentry = '\{ + "line": {line|json} + }' +search = '\{ + "node": {node|json}, + "query": {query|json}, + "entries": [{join(entries%searchentry, ", ")}] + }' +searchentry = '{changelistentry}' # changelog and shortlog are the same web API but with different # number of entries. changelog = changelist.tmpl @@ -9,10 +29,13 @@ "node": {node|json}, "date": {date|json}, "desc": {desc|utf8|json}, + "branch": {if(branch, branch%changesetbranch, "default"|json)}, "bookmarks": [{join(bookmarks%changelistentryname, ", ")}], "tags": [{join(tags%changelistentryname, ", ")}], "user": {author|utf8|json}, - "parents": [{join(allparents%changesetparent, ", ")}] + "phase": {phase|json}, + "parents": [{if(allparents, join(allparents%changesetparent, ", "), + join(parent%changesetparent, ", "))}] }' changelistentryname = '{name|utf8|json}' changeset = '\{ @@ -78,7 +101,22 @@ "date": {date|json}, "status": {status|json} }' -summary = '"not yet implemented"' +shortlogentry = '{changelistentry}' +summary = '\{ + "node": {node|json}, + "lastchange": {lastchange|json}, + "bookmarks": [{join(bookmarks%bookmarkentry, ", ")}], + "branches": [{join(branches%branchentry, ", ")}], + "shortlog": [{join(shortlog%shortlogentry, ", ")}], + "tags": [{join(tags%tagentry, ", ")}], + "archives": [{join(archives%archiveentry, ", ")}] + }' +archiveentry = '\{ + "node": {node|json}, + "extension": {extension|json}, + "type": {type|json}, + "url": {"{urlbase}{url}archive/{node}{extension}"|json} + }' filediff = '\{ "path": {file|json}, "node": {node|json}, @@ -156,7 +194,9 @@ "lineno": {lineno|json}, "revdate": {revdate|json} }' -filelog = '"not yet implemented"' +filelog = '\{ + "entries": [{join(entries%changelistentry, ", ")}] + }' graph = '"not yet implemented"' helptopics = '\{ "topics": [{join(topics%helptopicentry, ", ")}],
--- a/mercurial/templates/monoblue/map Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/monoblue/map Fri Jul 01 16:02:56 2016 -0500 @@ -91,10 +91,12 @@ fileline = ' <a href="#{lineid}"></a><span id="{lineid}">{strip(line|escape, '\r\n')}</span>' annotateline = ' - <tr id="{lineid}" class="parity{parity}"> + <tr id="{lineid}" class="parity{parity}{ifeq(node, originalnode, ' thisrev')}"> <td class="linenr"> - <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}" - title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a> + {if(blockhead, + '<a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}" + title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>', + '')} </td> <td class="lineno"> <a href="#{lineid}">{linenumber}</a>
--- a/mercurial/templates/paper/map Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/paper/map Fri Jul 01 16:02:56 2016 -0500 @@ -76,10 +76,12 @@ filelogentry = filelogentry.tmpl annotateline = ' - <tr id="{lineid}"> + <tr id="{lineid}"{ifeq(node, originalnode, ' class="thisrev"')}> <td class="annotate"> - <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}" - title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a> + {if(blockhead, + '<a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}" + title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>', + '')} </td> <td class="source"><a href="#{lineid}">{linenumber}</a> {line|escape}</td> </tr>'
--- a/mercurial/templates/rss/bookmarks.tmpl Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/rss/bookmarks.tmpl Fri Jul 01 16:02:56 2016 -0500 @@ -1,5 +1,5 @@ {header} - <title>{repo|escape}: bookmarks </title> + <title>{repo|escape}: bookmarks</title> <description>{repo|escape} bookmark history</description> {entries%bookmarkentry} </channel>
--- a/mercurial/templates/rss/changelogentry.tmpl Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/rss/changelogentry.tmpl Fri Jul 01 16:02:56 2016 -0500 @@ -1,40 +1,41 @@ <item> <title>{inbranch%"{if(name, '[{name|escape}] ')}"}{branches%"{if(name, '[{name|escape}] ')}"}{desc|strip|firstline|strip|escape}</title> <guid isPermaLink="true">{urlbase}{url|urlescape}rev/{node|short}</guid> - <link>{urlbase}{url|urlescape}rev/{node|short}</link> + <link>{urlbase}{url|urlescape}rev/{node|short}</link> <description> - <![CDATA[ - <table> - <tr> - <th style="text-align:left;">changeset</th> - <td>{node|short}</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>{inbranch%"{name|escape}"}{branches%"{name|escape}"}</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td>{bookmarks%"{name|escape}"}</td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td>{tags%"{name|escape}"}</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">user</th> - <td>{author|obfuscate}</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>{desc|strip|escape|websub|addbreaks|nonempty}</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>{files}</td> - </tr> - </table> - ]]></description> + <![CDATA[ + <table> + <tr> + <th style="text-align:left;">changeset</th> + <td>{node|short}</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>{inbranch%"{name|escape}"}{branches%"{name|escape}"}</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td>{bookmarks%"{name|escape}"}</td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td>{tags%"{name|escape}"}</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">user</th> + <td>{author|obfuscate}</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>{desc|strip|escape|websub|addbreaks|nonempty}</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>{files}</td> + </tr> + </table> + ]]> + </description> <author>{author|obfuscate}</author> <pubDate>{date|rfc822date}</pubDate> </item>
--- a/mercurial/templates/rss/tags.tmpl Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/rss/tags.tmpl Fri Jul 01 16:02:56 2016 -0500 @@ -1,5 +1,5 @@ {header} - <title>{repo|escape}: tags </title> + <title>{repo|escape}: tags</title> <description>{repo|escape} tag history</description> {entriesnotip%tagentry} </channel>
--- a/mercurial/templates/spartan/map Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/spartan/map Fri Jul 01 16:02:56 2016 -0500 @@ -54,10 +54,12 @@ # is an empty line in the annotated file), which in turn ensures that # all table rows have equal height. annotateline = ' - <tr class="parity{parity}"> + <tr class="parity{parity}{ifeq(node, originalnode, ' thisrev')}"> <td class="annotate"> - <a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}" - title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a> + {if(blockhead, + '<a href="{url|urlescape}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}#l{targetline}" + title="{node|short}: {desc|escape|firstline}">{author|user}@{rev}</a>', + '')} </td> <td> <a class="lineno" href="#{lineid}" id="{lineid}">{linenumber}</a>
--- a/mercurial/templates/static/style-gitweb.css Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/static/style-gitweb.css Fri Jul 01 16:02:56 2016 -0500 @@ -52,6 +52,8 @@ div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; } div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; } div.search { margin:4px 8px; position:absolute; top:56px; right:12px } +tr.thisrev a { color:#999999; text-decoration: none; } +tr.thisrev pre { color:#009900; } .linenr { color:#999999; text-decoration:none } div.rss_logo { float: right; white-space: nowrap; } div.rss_logo a {
--- a/mercurial/templates/static/style-monoblue.css Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/static/style-monoblue.css Fri Jul 01 16:02:56 2016 -0500 @@ -331,6 +331,8 @@ td.linenr { width: 60px; } +tr.thisrev a { color:#999999; text-decoration: none; } +tr.thisrev td.source { color:#009900; } div#powered-by { position: absolute;
--- a/mercurial/templates/static/style-paper.css Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/static/style-paper.css Fri Jul 01 16:02:56 2016 -0500 @@ -208,6 +208,8 @@ .bigtable .annotate { text-align: right; } .bigtable td.annotate { font-size: smaller; } .bigtable td.source { font-size: inherit; } +tr.thisrev a { color:#999999; text-decoration: none; } +tr.thisrev td.source { color:#009900; } .source, .sourcefirst { font-family: monospace;
--- a/mercurial/templates/static/style.css Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/templates/static/style.css Fri Jul 01 16:02:56 2016 -0500 @@ -10,6 +10,8 @@ .minusline { color: red; } .atline { color: purple; } .annotate { font-size: smaller; text-align: right; padding-right: 1em; } +tr.thisrev a { color:#999999; text-decoration: none; } +tr.thisrev pre { color:#009900; } .buttons a { background-color: #666; padding: 2pt;
--- a/mercurial/transaction.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/transaction.py Fri Jul 01 16:02:56 2016 -0500 @@ -31,10 +31,9 @@ 'dirstate' ]) -class GenerationGroup(object): - ALL='all' - PREFINALIZE='prefinalize' - POSTFINALIZE='postfinalize' +gengroupall='all' +gengroupprefinalize='prefinalize' +gengrouppostfinalize='postfinalize' def active(func): def _active(self, *args, **kwds): @@ -73,7 +72,7 @@ filepath = vfs.join(f) backuppath = vfs.join(b) try: - util.copyfile(backuppath, filepath) + util.copyfile(backuppath, filepath, checkambig=True) backupfiles.append(b) except IOError: report(_("failed to recover %s\n") % f) @@ -289,7 +288,7 @@ # but for bookmarks that are handled outside this mechanism. self._filegenerators[genid] = (order, filenames, genfunc, location) - def _generatefiles(self, suffix='', group=GenerationGroup.ALL): + def _generatefiles(self, suffix='', group=gengroupall): # write files registered for generation any = False for id, entry in sorted(self._filegenerators.iteritems()): @@ -297,8 +296,8 @@ order, filenames, genfunc, location = entry # for generation at closing, check if it's before or after finalize - postfinalize = group == GenerationGroup.POSTFINALIZE - if (group != GenerationGroup.ALL and + postfinalize = group == gengrouppostfinalize + if (group != gengroupall and (id in postfinalizegenerators) != (postfinalize)): continue @@ -311,7 +310,8 @@ self.registertmp(name, location=location) else: self.addbackup(name, location=location) - files.append(vfs(name, 'w', atomictemp=True)) + files.append(vfs(name, 'w', atomictemp=True, + checkambig=not suffix)) genfunc(*files) finally: for f in files: @@ -427,13 +427,13 @@ '''commit the transaction''' if self.count == 1: self.validator(self) # will raise exception if needed - self._generatefiles(group=GenerationGroup.PREFINALIZE) + self._generatefiles(group=gengroupprefinalize) categories = sorted(self._finalizecallback) for cat in categories: self._finalizecallback[cat](self) # Prevent double usage and help clear cycles. self._finalizecallback = None - self._generatefiles(group=GenerationGroup.POSTFINALIZE) + self._generatefiles(group=gengrouppostfinalize) self.count -= 1 if self.count != 0:
--- a/mercurial/ui.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/ui.py Fri Jul 01 16:02:56 2016 -0500 @@ -29,6 +29,8 @@ util, ) +urlreq = util.urlreq + samplehgrcs = { 'user': """# example user config (see "hg help config" for more info) @@ -107,6 +109,8 @@ self._trustusers = set() self._trustgroups = set() self.callhooks = True + # Insecure server connections requested. + self.insecureconnections = False if src: self.fout = src.fout @@ -120,7 +124,10 @@ self._trustgroups = src._trustgroups.copy() self.environ = src.environ self.callhooks = src.callhooks + self.insecureconnections = src.insecureconnections self.fixconfig() + + self.httppasswordmgrdb = src.httppasswordmgrdb else: self.fout = sys.stdout self.ferr = sys.stderr @@ -132,9 +139,17 @@ for f in scmutil.rcpath(): self.readconfig(f, trust=True) + self.httppasswordmgrdb = urlreq.httppasswordmgrwithdefaultrealm() + def copy(self): return self.__class__(self) + def resetstate(self): + """Clear internal state that shouldn't persist across commands""" + if self._progbar: + self._progbar.resetstate() # reset last-print time of progress bar + self.httppasswordmgrdb = urlreq.httppasswordmgrwithdefaultrealm() + def formatter(self, topic, opts): return formatter.formatter(self, topic, opts) @@ -213,6 +228,9 @@ root = root or os.getcwd() for c in self._tcfg, self._ucfg, self._ocfg: for n, p in c.items('paths'): + # Ignore sub-options. + if ':' in n: + continue if not p: continue if '%%' in p: @@ -1135,12 +1153,15 @@ ''' return msg - def develwarn(self, msg, stacklevel=1): + def develwarn(self, msg, stacklevel=1, config=None): """issue a developer warning message Use 'stacklevel' to report the offender some layers further up in the stack. """ + if not self.configbool('devel', 'all-warnings'): + if config is not None and not self.configbool('devel', config): + return msg = 'devel-warn: ' + msg stacklevel += 1 # get in develwarn if self.tracebackflag: @@ -1166,7 +1187,7 @@ return msg += ("\n(compatibility will be dropped after Mercurial-%s," " update your code.)") % version - self.develwarn(msg, stacklevel=2) + self.develwarn(msg, stacklevel=2, config='deprec-warn') class paths(dict): """Represents a collection of paths and their configs. @@ -1260,6 +1281,10 @@ return str(u) +@pathsuboption('pushrev', 'pushrev') +def pushrevpathoption(ui, path, value): + return value + class path(object): """Represents an individual path and its configuration."""
--- a/mercurial/url.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/url.py Fri Jul 01 16:02:56 2016 -0500 @@ -10,7 +10,6 @@ from __future__ import absolute_import import base64 -import httplib import os import socket @@ -22,19 +21,22 @@ sslutil, util, ) + +httplib = util.httplib stringio = util.stringio - urlerr = util.urlerr urlreq = util.urlreq -class passwordmgr(urlreq.httppasswordmgrwithdefaultrealm): - def __init__(self, ui): - urlreq.httppasswordmgrwithdefaultrealm.__init__(self) +class passwordmgr(object): + def __init__(self, ui, passwddb): self.ui = ui + self.passwddb = passwddb + + def add_password(self, realm, uri, user, passwd): + return self.passwddb.add_password(realm, uri, user, passwd) def find_user_password(self, realm, authuri): - authinfo = urlreq.httppasswordmgrwithdefaultrealm.find_user_password( - self, realm, authuri) + authinfo = self.passwddb.find_user_password(realm, authuri) user, passwd = authinfo if user and passwd: self._writedebug(user, passwd) @@ -64,7 +66,7 @@ if not passwd: passwd = self.ui.getpass() - self.add_password(realm, authuri, user, passwd) + self.passwddb.add_password(realm, authuri, user, passwd) self._writedebug(user, passwd) return (user, passwd) @@ -73,8 +75,7 @@ self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set')) def find_stored_password(self, authuri): - return urlreq.httppasswordmgrwithdefaultrealm.find_user_password( - self, None, authuri) + return self.passwddb.find_user_password(None, authuri) class proxyhandler(urlreq.proxyhandler): def __init__(self, ui): @@ -354,16 +355,17 @@ _generic_proxytunnel(self) host = self.realhostport.rsplit(':', 1)[0] self.sock = sslutil.wrapsocket( - self.sock, self.key_file, self.cert_file, serverhostname=host, - **sslutil.sslkwargs(self.ui, host)) - sslutil.validator(self.ui, host)(self.sock) + self.sock, self.key_file, self.cert_file, ui=self.ui, + serverhostname=host) + sslutil.validatesocket(self.sock) class httpshandler(keepalive.KeepAliveHandler, urlreq.httpshandler): def __init__(self, ui): keepalive.KeepAliveHandler.__init__(self) urlreq.httpshandler.__init__(self) self.ui = ui - self.pwmgr = passwordmgr(self.ui) + self.pwmgr = passwordmgr(self.ui, + self.ui.httppasswordmgrdb) def _start_transaction(self, h, req): _generic_start_transaction(self, h, req) @@ -477,7 +479,11 @@ ''' # experimental config: ui.usehttp2 if ui.configbool('ui', 'usehttp2', False): - handlers = [httpconnectionmod.http2handler(ui, passwordmgr(ui))] + handlers = [ + httpconnectionmod.http2handler( + ui, + passwordmgr(ui, ui.httppasswordmgrdb)) + ] else: handlers = [httphandler()] if has_https: @@ -485,10 +491,12 @@ handlers.append(proxyhandler(ui)) - passmgr = passwordmgr(ui) + passmgr = passwordmgr(ui, ui.httppasswordmgrdb) if authinfo is not None: - passmgr.add_password(*authinfo) - user, passwd = authinfo[2:4] + realm, uris, user, passwd = authinfo + saveduser, savedpass = passmgr.find_stored_password(uris[0]) + if user != saveduser or passwd: + passmgr.add_password(realm, uris, user, passwd) ui.debug('http auth: user %s, password %s\n' % (user, passwd and '*' * len(passwd) or 'not set'))
--- a/mercurial/util.h Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/util.h Fri Jul 01 16:02:56 2016 -0500 @@ -8,6 +8,8 @@ #ifndef _HG_UTIL_H_ #define _HG_UTIL_H_ +#include "compat.h" + #if PY_MAJOR_VERSION >= 3 #define IS_PY3K @@ -57,40 +59,6 @@ #endif /* PY_MAJOR_VERSION */ -#ifdef _WIN32 -#ifdef _MSC_VER -/* msvc 6.0 has problems */ -#define inline __inline -typedef signed char int8_t; -typedef short int16_t; -typedef long int32_t; -typedef __int64 int64_t; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned long uint32_t; -typedef unsigned __int64 uint64_t; -#else -#include <stdint.h> -#endif -#else -/* not windows */ -#include <sys/types.h> -#if defined __BEOS__ && !defined __HAIKU__ -#include <ByteOrder.h> -#else -#include <arpa/inet.h> -#endif -#include <inttypes.h> -#endif - -#if defined __hpux || defined __SUNPRO_C || defined _AIX -#define inline -#endif - -#ifdef __linux -#define inline __inline -#endif - typedef struct { PyObject_HEAD char state; @@ -102,53 +70,6 @@ extern PyTypeObject dirstateTupleType; #define dirstate_tuple_check(op) (Py_TYPE(op) == &dirstateTupleType) -static inline uint32_t getbe32(const char *c) -{ - const unsigned char *d = (const unsigned char *)c; - - return ((d[0] << 24) | - (d[1] << 16) | - (d[2] << 8) | - (d[3])); -} - -static inline int16_t getbeint16(const char *c) -{ - const unsigned char *d = (const unsigned char *)c; - - return ((d[0] << 8) | - (d[1])); -} - -static inline uint16_t getbeuint16(const char *c) -{ - const unsigned char *d = (const unsigned char *)c; - - return ((d[0] << 8) | - (d[1])); -} - -static inline void putbe32(uint32_t x, char *c) -{ - c[0] = (x >> 24) & 0xff; - c[1] = (x >> 16) & 0xff; - c[2] = (x >> 8) & 0xff; - c[3] = (x) & 0xff; -} - -static inline double getbefloat64(const char *c) -{ - const unsigned char *d = (const unsigned char *)c; - double ret; - int i; - uint64_t t = 0; - for (i = 0; i < 8; i++) { - t = (t<<8) + d[i]; - } - memcpy(&ret, &t, sizeof(t)); - return ret; -} - /* This should be kept in sync with normcasespecs in encoding.py. */ enum normcase_spec { NORMCASE_LOWER = -1,
--- a/mercurial/util.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/util.py Fri Jul 01 16:02:56 2016 -0500 @@ -47,11 +47,16 @@ for attr in ( 'empty', + 'httplib', + 'pickle', 'queue', 'urlerr', + 'urlparse', # we do import urlreq, but we do it outside the loop #'urlreq', 'stringio', + 'socketserver', + 'xmlrpclib', ): globals()[attr] = getattr(pycompat, attr) @@ -63,9 +68,6 @@ else: from . import posix as platform -md5 = hashlib.md5 -sha1 = hashlib.sha1 -sha512 = hashlib.sha512 _ = i18n._ cachestat = platform.cachestat @@ -136,9 +138,9 @@ return getattr(thing, attr, _notset) is not _notset DIGESTS = { - 'md5': md5, - 'sha1': sha1, - 'sha512': sha512, + 'md5': hashlib.md5, + 'sha1': hashlib.sha1, + 'sha512': hashlib.sha512, } # List of digest types from strongest to weakest DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5'] @@ -1010,10 +1012,21 @@ return check -def copyfile(src, dest, hardlink=False, copystat=False): +def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False): '''copy a file, preserving mode and optionally other stat info like - atime/mtime''' + atime/mtime + + checkambig argument is used with filestat, and is useful only if + destination file is guarded by any lock (e.g. repo.lock or + repo.wlock). + + copystat and checkambig should be exclusive. + ''' + assert not (copystat and checkambig) + oldstat = None if os.path.lexists(dest): + if checkambig: + oldstat = checkambig and filestat(dest) unlink(dest) # hardlinks are problematic on CIFS, quietly ignore this flag # until we find a way to work around it cleanly (issue4546) @@ -1035,6 +1048,12 @@ shutil.copystat(src, dest) else: shutil.copymode(src, dest) + if oldstat and oldstat.stat: + newstat = filestat(dest) + if newstat.isambig(oldstat): + # stat of copied file is ambiguous to original one + advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff + os.utime(dest, (advanced, advanced)) except shutil.Error as inst: raise Abort(str(inst)) @@ -1381,6 +1400,72 @@ raise return temp +class filestat(object): + """help to exactly detect change of a file + + 'stat' attribute is result of 'os.stat()' if specified 'path' + exists. Otherwise, it is None. This can avoid preparative + 'exists()' examination on client side of this class. + """ + def __init__(self, path): + try: + self.stat = os.stat(path) + except OSError as err: + if err.errno != errno.ENOENT: + raise + self.stat = None + + __hash__ = object.__hash__ + + def __eq__(self, old): + try: + # if ambiguity between stat of new and old file is + # avoided, comparision of size, ctime and mtime is enough + # to exactly detect change of a file regardless of platform + return (self.stat.st_size == old.stat.st_size and + self.stat.st_ctime == old.stat.st_ctime and + self.stat.st_mtime == old.stat.st_mtime) + except AttributeError: + return False + + def isambig(self, old): + """Examine whether new (= self) stat is ambiguous against old one + + "S[N]" below means stat of a file at N-th change: + + - S[n-1].ctime < S[n].ctime: can detect change of a file + - S[n-1].ctime == S[n].ctime + - S[n-1].ctime < S[n].mtime: means natural advancing (*1) + - S[n-1].ctime == S[n].mtime: is ambiguous (*2) + - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care) + - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care) + + Case (*2) above means that a file was changed twice or more at + same time in sec (= S[n-1].ctime), and comparison of timestamp + is ambiguous. + + Base idea to avoid such ambiguity is "advance mtime 1 sec, if + timestamp is ambiguous". + + But advancing mtime only in case (*2) doesn't work as + expected, because naturally advanced S[n].mtime in case (*1) + might be equal to manually advanced S[n-1 or earlier].mtime. + + Therefore, all "S[n-1].ctime == S[n].ctime" cases should be + treated as ambiguous regardless of mtime, to avoid overlooking + by confliction between such mtime. + + Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime != + S[n].mtime", even if size of a file isn't changed. + """ + try: + return (self.stat.st_ctime == old.stat.st_ctime) + except AttributeError: + return False + + def __ne__(self, other): + return not self == other + class atomictempfile(object): '''writable file object that atomically updates a file @@ -1389,14 +1474,20 @@ the temporary copy to the original name, making the changes visible. If the object is destroyed without being closed, all your writes are discarded. + + checkambig argument of constructor is used with filestat, and is + useful only if target file is guarded by any lock (e.g. repo.lock + or repo.wlock). ''' - def __init__(self, name, mode='w+b', createmode=None): + def __init__(self, name, mode='w+b', createmode=None, checkambig=False): self.__name = name # permanent name self._tempname = mktempcopy(name, emptyok=('w' in mode), createmode=createmode) self._fp = posixfile(self._tempname, mode) + self._checkambig = checkambig # delegated methods + self.read = self._fp.read self.write = self._fp.write self.seek = self._fp.seek self.tell = self._fp.tell @@ -1405,7 +1496,17 @@ def close(self): if not self._fp.closed: self._fp.close() - rename(self._tempname, localpath(self.__name)) + filename = localpath(self.__name) + oldstat = self._checkambig and filestat(filename) + if oldstat and oldstat.stat: + rename(self._tempname, filename) + newstat = filestat(filename) + if newstat.isambig(oldstat): + # stat of changed file is ambiguous to original one + advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff + os.utime(filename, (advanced, advanced)) + else: + rename(self._tempname, filename) def discard(self): if not self._fp.closed: @@ -1419,6 +1520,15 @@ if safehasattr(self, '_fp'): # constructor actually did something self.discard() + def __enter__(self): + return self + + def __exit__(self, exctype, excvalue, traceback): + if exctype is not None: + self.discard() + else: + self.close() + def makedirs(name, mode=None, notindexed=False): """recursive directory creation with parent mode inheritance
--- a/mercurial/wireproto.py Fri Jul 01 15:12:33 2016 -0500 +++ b/mercurial/wireproto.py Fri Jul 01 16:02:56 2016 -0500 @@ -7,6 +7,7 @@ from __future__ import absolute_import +import hashlib import itertools import os import sys @@ -97,7 +98,7 @@ batchablefn = getattr(mtd, 'batchable', None) if batchablefn is not None: batchable = batchablefn(mtd.im_self, *args, **opts) - encargsorres, encresref = batchable.next() + encargsorres, encresref = next(batchable) if encresref: req.append((name, encargsorres,)) rsp.append((batchable, encresref, resref,)) @@ -115,7 +116,7 @@ for encres, r in zip(encresults, rsp): batchable, encresref, resref = r encresref.set(encres) - resref.set(batchable.next()) + resref.set(next(batchable)) class remoteiterbatcher(peer.iterbatcher): def __init__(self, remote): @@ -138,7 +139,7 @@ for name, args, opts, resref in self.calls: mtd = getattr(self._remote, name) batchable = mtd.batchable(mtd.im_self, *args, **opts) - encargsorres, encresref = batchable.next() + encargsorres, encresref = next(batchable) assert encresref req.append((name, encargsorres)) rsp.append((batchable, encresref)) @@ -150,7 +151,7 @@ for (batchable, encresref), encres in itertools.izip( self._rsp, self._resultiter): encresref.set(encres) - yield batchable.next() + yield next(batchable) # Forward a couple of names from peer to make wireproto interactions # slightly more sensible. @@ -231,17 +232,19 @@ for k, v in argsdict.iteritems()) cmds.append('%s %s' % (op, args)) rsp = self._callstream("batch", cmds=';'.join(cmds)) - # TODO this response parsing is probably suboptimal for large - # batches with large responses. - work = rsp.read(1024) - chunk = work + chunk = rsp.read(1024) + work = [chunk] while chunk: - while ';' in work: - one, work = work.split(';', 1) + while ';' not in chunk and chunk: + chunk = rsp.read(1024) + work.append(chunk) + merged = ''.join(work) + while ';' in merged: + one, merged = merged.split(';', 1) yield unescapearg(one) chunk = rsp.read(1024) - work += chunk - yield unescapearg(work) + work = [merged, chunk] + yield unescapearg(''.join(work)) def _submitone(self, op, args): return self._call(op, **args) @@ -408,7 +411,7 @@ if heads != ['force'] and self.capable('unbundlehash'): heads = encodelist(['hashed', - util.sha1(''.join(sorted(heads))).digest()]) + hashlib.sha1(''.join(sorted(heads))).digest()]) else: heads = encodelist(heads)
--- a/setup.py Fri Jul 01 15:12:33 2016 -0500 +++ b/setup.py Fri Jul 01 16:02:56 2016 -0500 @@ -536,7 +536,9 @@ 'hgext.fsmonitor.pywatchman', 'hgext.highlight', 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd'] -common_depends = ['mercurial/util.h'] +common_depends = ['mercurial/bitmanipulation.h', + 'mercurial/compat.h', + 'mercurial/util.h'] osutil_ldflags = []
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/dummysmtpd.py Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,81 @@ +#!/usr/bin/env python + +"""dummy SMTP server for use in tests""" + +from __future__ import absolute_import + +import asyncore +import optparse +import smtpd +import ssl +import sys + +from mercurial import ( + cmdutil, +) + +def log(msg): + sys.stdout.write(msg) + sys.stdout.flush() + +class dummysmtpserver(smtpd.SMTPServer): + def __init__(self, localaddr): + smtpd.SMTPServer.__init__(self, localaddr, remoteaddr=None) + + def process_message(self, peer, mailfrom, rcpttos, data): + log('%s from=%s to=%s\n' % (peer[0], mailfrom, ', '.join(rcpttos))) + +class dummysmtpsecureserver(dummysmtpserver): + def __init__(self, localaddr, certfile): + dummysmtpserver.__init__(self, localaddr) + self._certfile = certfile + + def handle_accept(self): + pair = self.accept() + if not pair: + return + conn, addr = pair + try: + # wrap_socket() would block, but we don't care + conn = ssl.wrap_socket(conn, server_side=True, + certfile=self._certfile, + ssl_version=ssl.PROTOCOL_TLSv1) + except ssl.SSLError: + log('%s ssl error\n' % addr[0]) + conn.close() + return + smtpd.SMTPChannel(self, conn, addr) + +def run(): + try: + asyncore.loop() + except KeyboardInterrupt: + pass + +def main(): + op = optparse.OptionParser() + op.add_option('-d', '--daemon', action='store_true') + op.add_option('--daemon-postexec', action='append') + op.add_option('-p', '--port', type=int, default=8025) + op.add_option('-a', '--address', default='localhost') + op.add_option('--pid-file', metavar='FILE') + op.add_option('--tls', choices=['none', 'smtps'], default='none') + op.add_option('--certificate', metavar='FILE') + + opts, args = op.parse_args() + if opts.tls == 'smtps' and not opts.certificate: + op.error('--certificate must be specified') + + addr = (opts.address, opts.port) + def init(): + if opts.tls == 'none': + dummysmtpserver(addr) + else: + dummysmtpsecureserver(addr, opts.certificate) + log('listening at %s:%d\n' % addr) + + cmdutil.service(vars(opts), initfn=init, runfn=run, + runargs=[sys.executable, __file__] + sys.argv[1:]) + +if __name__ == '__main__': + main()
--- a/tests/dummyssh Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/dummyssh Fri Jul 01 16:02:56 2016 -0500 @@ -1,7 +1,9 @@ #!/usr/bin/env python +from __future__ import absolute_import + +import os import sys -import os os.chdir(os.getenv('TESTTMP'))
--- a/tests/f Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/f Fri Jul 01 16:02:56 2016 -0500 @@ -23,7 +23,14 @@ md5sum.py """ -import sys, os, errno, re, glob, optparse +from __future__ import absolute_import + +import glob +import hashlib +import optparse +import os +import re +import sys def visit(opts, filenames, outfile): """Process filenames in the way specified in opts, writing output to @@ -74,17 +81,11 @@ else: facts.append('older than %s' % opts.newer) if opts.md5 and content is not None: - try: - from hashlib import md5 - except ImportError: - from md5 import md5 - facts.append('md5=%s' % md5(content).hexdigest()[:opts.bytes]) + h = hashlib.md5(content) + facts.append('md5=%s' % h.hexdigest()[:opts.bytes]) if opts.sha1 and content is not None: - try: - from hashlib import sha1 - except ImportError: - from sha import sha as sha1 - facts.append('sha1=%s' % sha1(content).hexdigest()[:opts.bytes]) + h = hashlib.sha1(content) + facts.append('sha1=%s' % h.hexdigest()[:opts.bytes]) if isstdin: outfile.write(', '.join(facts) + '\n') elif facts:
--- a/tests/get-with-headers.py Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/get-with-headers.py Fri Jul 01 16:02:56 2016 -0500 @@ -5,11 +5,16 @@ from __future__ import absolute_import, print_function -import httplib import json import os import sys +from mercurial import ( + util, +) + +httplib = util.httplib + try: import msvcrt msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/helper-runtests.sh Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,7 @@ +# +# Avoid interference from actual test env: + +unset HGTEST_JOBS +unset HGTEST_TIMEOUT +unset HGTEST_PORT +unset HGTEST_SHELL
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/helpers-testrepo.sh Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,19 @@ +# The test-repo is a live hg repository which may have evolution +# markers created, e.g. when a ~/.hgrc enabled evolution. +# +# Tests are run using a custom HGRCPATH, which do not +# enable evolution markers by default. +# +# If test-repo includes evolution markers, and we do not +# enable evolution markers, hg will occasionally complain +# when it notices them, which disrupts tests resulting in +# sporadic failures. +# +# Since we aren't performing any write operations on the +# test-repo, there's no harm in telling hg that we support +# evolution markers, which is what the following lines +# for the hgrc file do: +cat >> $HGRCPATH << EOF +[experimental] +evolution=createmarkers +EOF
--- a/tests/hghave Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/hghave Fri Jul 01 16:02:56 2016 -0500 @@ -4,22 +4,23 @@ prefixed with "no-", the absence of feature is tested. """ -from __future__ import print_function +from __future__ import absolute_import, print_function +import hghave import optparse -import os, sys -import hghave +import os +import sys checks = hghave.checks def list_features(): - for name, feature in sorted(checks.iteritems()): + for name, feature in sorted(checks.items()): desc = feature[1] print(name + ':', desc) def test_features(): failed = 0 - for name, feature in checks.iteritems(): + for name, feature in checks.items(): check, _ = feature try: check() @@ -48,6 +49,7 @@ sys.path.insert(0, path) try: import hghaveaddon + assert hghaveaddon # silence pyflakes except BaseException as inst: sys.stderr.write('failed to import hghaveaddon.py from %r: %s\n' % (path, inst))
--- a/tests/hghave.py Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/hghave.py Fri Jul 01 16:02:56 2016 -0500 @@ -104,7 +104,7 @@ @check("baz", "GNU Arch baz client") def has_baz(): - return matchoutput('baz --version 2>&1', r'baz Bazaar version') + return matchoutput('baz --version 2>&1', br'baz Bazaar version') @check("bzr", "Canonical's Bazaar client") def has_bzr(): @@ -130,27 +130,27 @@ @check("cvs", "cvs client/server") def has_cvs(): - re = r'Concurrent Versions System.*?server' + re = br'Concurrent Versions System.*?server' return matchoutput('cvs --version 2>&1', re) and not has_msys() @check("cvs112", "cvs client/server 1.12.* (not cvsnt)") def has_cvs112(): - re = r'Concurrent Versions System \(CVS\) 1.12.*?server' + re = br'Concurrent Versions System \(CVS\) 1.12.*?server' return matchoutput('cvs --version 2>&1', re) and not has_msys() @check("cvsnt", "cvsnt client/server") def has_cvsnt(): - re = r'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)' + re = br'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)' return matchoutput('cvsnt --version 2>&1', re) @check("darcs", "darcs client") def has_darcs(): - return matchoutput('darcs --version', r'2\.[2-9]', True) + return matchoutput('darcs --version', br'2\.[2-9]', True) @check("mtn", "monotone client (>= 1.0)") def has_mtn(): - return matchoutput('mtn --version', r'monotone', True) and not matchoutput( - 'mtn --version', r'monotone 0\.', True) + return matchoutput('mtn --version', br'monotone', True) and not matchoutput( + 'mtn --version', br'monotone 0\.', True) @check("eol-in-paths", "end-of-lines in paths") def has_eol_in_paths(): @@ -236,7 +236,7 @@ return False def gethgversion(): - m = matchoutput('hg --version --quiet 2>&1', r'(\d+)\.(\d+)') + m = matchoutput('hg --version --quiet 2>&1', br'(\d+)\.(\d+)') if not m: return (0, 0) return (int(m.group(1)), int(m.group(2))) @@ -267,11 +267,11 @@ @check("gettext", "GNU Gettext (msgfmt)") def has_gettext(): - return matchoutput('msgfmt --version', 'GNU gettext-tools') + return matchoutput('msgfmt --version', br'GNU gettext-tools') @check("git", "git command line client") def has_git(): - return matchoutput('git --version 2>&1', r'^git version') + return matchoutput('git --version 2>&1', br'^git version') @check("docutils", "Docutils text processing library") def has_docutils(): @@ -283,7 +283,7 @@ return False def getsvnversion(): - m = matchoutput('svn --version --quiet 2>&1', r'^(\d+)\.(\d+)') + m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)') if not m: return (0, 0) return (int(m.group(1)), int(m.group(2))) @@ -295,8 +295,8 @@ @check("svn", "subversion client and admin tools") def has_svn(): - return matchoutput('svn --version 2>&1', r'^svn, version') and \ - matchoutput('svnadmin --version 2>&1', r'^svnadmin, version') + return matchoutput('svn --version 2>&1', br'^svn, version') and \ + matchoutput('svnadmin --version 2>&1', br'^svnadmin, version') @check("svn-bindings", "subversion python bindings") def has_svn_bindings(): @@ -311,8 +311,8 @@ @check("p4", "Perforce server and client") def has_p4(): - return (matchoutput('p4 -V', r'Rev\. P4/') and - matchoutput('p4d -V', r'Rev\. P4D/')) + return (matchoutput('p4 -V', br'Rev\. P4/') and + matchoutput('p4d -V', br'Rev\. P4D/')) @check("symlink", "symbolic links") def has_symlink(): @@ -343,11 +343,11 @@ @check("tla", "GNU Arch tla client") def has_tla(): - return matchoutput('tla --version 2>&1', r'The GNU Arch Revision') + return matchoutput('tla --version 2>&1', br'The GNU Arch Revision') @check("gpg", "gpg client") def has_gpg(): - return matchoutput('gpg --version 2>&1', r'GnuPG') + return matchoutput('gpg --version 2>&1', br'GnuPG') @check("unix-permissions", "unix-style permissions") def has_unix_permissions(): @@ -377,7 +377,7 @@ @check("pyflakes", "Pyflakes python linter") def has_pyflakes(): return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"", - r"<stdin>:1: 're' imported but unused", + br"<stdin>:1: 're' imported but unused", True) @check("pygments", "Pygments source highlighting library") @@ -393,7 +393,7 @@ def has_outer_repo(): # failing for other reasons than 'no repo' imply that there is a repo return not matchoutput('hg root 2>&1', - r'abort: no repository found', True) + br'abort: no repository found', True) @check("ssl", "ssl module available") def has_ssl(): @@ -416,7 +416,7 @@ @check("defaultcacerts", "can verify SSL certs by system's CA certs store") def has_defaultcacerts(): from mercurial import sslutil - return sslutil._defaultcacerts() != '!' + return sslutil._defaultcacerts() or sslutil._canloaddefaultcerts @check("windows", "Windows") def has_windows(): @@ -440,7 +440,7 @@ try: import curses curses.COLOR_BLUE - return matchoutput('test -x "`which tic`"', '') + return matchoutput('test -x "`which tic`"', br'') except ImportError: return False @@ -459,19 +459,19 @@ @check("osxpackaging", "OS X packaging tools") def has_osxpackaging(): try: - return (matchoutput('pkgbuild', 'Usage: pkgbuild ', ignorestatus=1) + return (matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1) and matchoutput( - 'productbuild', 'Usage: productbuild ', + 'productbuild', br'Usage: productbuild ', ignorestatus=1) - and matchoutput('lsbom', 'Usage: lsbom', ignorestatus=1) + and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1) and matchoutput( - 'xar --help', 'Usage: xar', ignorestatus=1)) + 'xar --help', br'Usage: xar', ignorestatus=1)) except ImportError: return False @check("docker", "docker support") def has_docker(): - pat = r'A self-sufficient runtime for' + pat = br'A self-sufficient runtime for' if matchoutput('docker --help', pat): if 'linux' not in sys.platform: # TODO: in theory we should be able to test docker-based @@ -489,11 +489,11 @@ @check("debhelper", "debian packaging tools") def has_debhelper(): dpkg = matchoutput('dpkg --version', - "Debian `dpkg' package management program") + br"Debian `dpkg' package management program") dh = matchoutput('dh --help', - 'dh is a part of debhelper.', ignorestatus=True) + br'dh is a part of debhelper.', ignorestatus=True) dh_py2 = matchoutput('dh_python2 --help', - 'other supported Python versions') + br'other supported Python versions') return dpkg and dh and dh_py2 @check("absimport", "absolute_import in __future__")
--- a/tests/readlink.py Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/readlink.py Fri Jul 01 16:02:56 2016 -0500 @@ -1,13 +1,15 @@ #!/usr/bin/env python +from __future__ import print_function + import errno, os, sys for f in sys.argv[1:]: try: - print f, '->', os.readlink(f) + print(f, '->', os.readlink(f)) except OSError as err: if err.errno != errno.EINVAL: raise - print f, 'not a symlink' + print(f, '->', f, 'not a symlink') sys.exit(0)
--- a/tests/run-tests.py Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/run-tests.py Fri Jul 01 16:02:56 2016 -0500 @@ -69,6 +69,13 @@ from xml.dom import minidom import unittest +if os.environ.get('RTUNICODEPEDANTRY', False): + try: + reload(sys) + sys.setdefaultencoding("undefined") + except NameError: + pass + osenvironb = getattr(os, 'environb', os.environ) processlock = threading.Lock() @@ -1836,7 +1843,8 @@ tres = {'result': res} outcome[tc.name] = tres - jsonout = json.dumps(outcome, sort_keys=True, indent=4) + jsonout = json.dumps(outcome, sort_keys=True, indent=4, + separators=(',', ': ')) fp.writelines(("testreport =", jsonout)) self._runner._checkhglib('Tested')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/sslcerts/README Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,26 @@ +Certificates created with: + printf '.\n.\n.\n.\n.\nlocalhost\nhg@localhost\n' | \ + openssl req -newkey rsa:512 -keyout priv.pem -nodes -x509 -days 9000 -out pub.pem +Can be dumped with: + openssl x509 -in pub.pem -text + + - priv.pem + - pub.pem + - pub-other.pem + +pub.pem patched with other notBefore / notAfter: + + - pub-not-yet.pem + - pub-expired.pem + +Client certificates created with: + openssl genrsa -aes128 -passout pass:1234 -out client-key.pem 512 + openssl rsa -in client-key.pem -passin pass:1234 -out client-key-decrypted.pem + printf '.\n.\n.\n.\n.\n.\nhg-client@localhost\n.\n.\n' | \ + openssl req -new -key client-key.pem -passin pass:1234 -out client-csr.pem + openssl x509 -req -days 9000 -in client-csr.pem -CA pub.pem -CAkey priv.pem \ + -set_serial 01 -out client-cert.pem + + - client-key.pem + - client-key-decrypted.pem + - client-cert.pem
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/sslcerts/client-cert.pem Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBPjCB6QIBATANBgkqhkiG9w0BAQsFADAxMRIwEAYDVQQDDAlsb2NhbGhvc3Qx +GzAZBgkqhkiG9w0BCQEWDGhnQGxvY2FsaG9zdDAeFw0xNTA1MDcwNjI5NDVaFw0z +OTEyMjcwNjI5NDVaMCQxIjAgBgkqhkiG9w0BCQEWE2hnLWNsaWVudEBsb2NhbGhv +c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAmzgtLeCUBhT3ZuDmQ+BE81bzh7AH +R9Yl8ApxwKnUAIcB1k95opsUKKdUxgoBVtWoGTKtn9PKvxpJ8zPjE7j4qwIDAQAB +MA0GCSqGSIb3DQEBCwUAA0EAfBTqBG5pYhuGk+ZnyUufgS+d7Nk/sZAZjNdCAEj/ +NFPo5fR1jM6jlEWoWbeg298+SkjV7tfO+2nt0otUFkdM6A== +-----END CERTIFICATE-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/sslcerts/client-key-decrypted.pem Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,9 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBOgIBAAJBAJs4LS3glAYU92bg5kPgRPNW84ewB0fWJfAKccCp1ACHAdZPeaKb +FCinVMYKAVbVqBkyrZ/Tyr8aSfMz4xO4+KsCAwEAAQJAeKDr25+Q6jkZHEbkLRP6 +AfMtR+Ixhk6TJT24sbZKIC2V8KuJTDEvUhLU0CAr1nH79bDqiSsecOiVCr2HHyfT +AQIhAM2C5rHbTs9R3PkywFEqq1gU3ztCnpiWglO7/cIkuGBhAiEAwVpMSAf77kop +4h/1kWsgMALQTJNsXd4CEUK4BOxvJIsCIQCbarVAKBQvoT81jfX27AfscsxnKnh5 ++MjSvkanvdFZwQIgbbcTefwt1LV4trtz2SR0i0nNcOZmo40Kl0jIquKO3qkCIH01 +mJHzZr3+jQqeIFtr5P+Xqi30DJxgrnEobbJ0KFjY +-----END RSA PRIVATE KEY-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/sslcerts/client-key.pem Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,12 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,C8B8F103A61A336FB0716D1C0F8BB2E8 + +JolMlCFjEW3q3JJjO9z99NJWeJbFgF5DpUOkfSCxH56hxxtZb9x++rBvBZkxX1bF +BAIe+iI90+jdCLwxbILWuFcrJUaLC5WmO14XDKYVmr2eW9e4MiCYOlO0Q6a9rDFS +jctRCfvubOXFHbBGLH8uKEMpXEkP7Lc60FiIukqjuQEivJjrQirVtZCGwyk3qUi7 +Eyh4Lo63IKGu8T1Bkmn2kaMvFhu7nC/CQLBjSq0YYI1tmCOkVb/3tPrz8oqgDJp2 +u7bLS3q0xDNZ52nVrKIoZC/UlRXGlPyzPpa70/jPIdfCbkwDaBpRVXc+62Pj2n5/ +CnO2xaKwfOG6pDvanBhFD72vuBOkAYlFZPiEku4sc2WlNggsSWCPCIFwzmiHjKIl +bWmdoTq3nb7sNfnBbV0OCa7fS1dFwCm4R1NC7ELENu0= +-----END RSA PRIVATE KEY-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/sslcerts/priv.pem Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,10 @@ +-----BEGIN PRIVATE KEY----- +MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEApjCWeYGrIa/Vo7LH +aRF8ou0tbgHKE33Use/whCnKEUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8 +j/xgSwIDAQABAkBxHC6+Qlf0VJXGlb6NL16yEVVTQxqDS6hA9zqu6TZjrr0YMfzc +EGNIiZGt7HCBL0zO+cPDg/LeCZc6HQhf0KrhAiEAzlJq4hWWzvguWFIJWSoBeBUG +MF1ACazQO7PYE8M0qfECIQDONHHP0SKZzz/ZwBZcAveC5K61f/v9hONFwbeYulzR ++wIgc9SvbtgB/5Yzpp//4ZAEnR7oh5SClCvyB+KSx52K3nECICbhQphhoXmI10wy +aMTellaq0bpNMHFDziqH9RsqAHhjAiEAgYGxfzkftt5IUUn/iFK89aaIpyrpuaAh +HY8gUVkVRVs= +-----END PRIVATE KEY-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/sslcerts/pub-expired.pem Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNVBAMMCWxvY2Fs +aG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEwMTAxNDIwMzAxNFoXDTEwMTAx +NDIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhv +c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnK +EUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA ++ammr24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJfk57DTRf2nUbYaMSlVAARxMNbFGOjQhAUtY400GhKt +2uiKCNGKXVXD3AHWe13yHc5KttzbHQStE5Nm/DlWBWQ= +-----END CERTIFICATE-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/sslcerts/pub-not-yet.pem Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNVBAMMCWxvY2Fs +aG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTM1MDYwNTIwMzAxNFoXDTM1MDYw +NTIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhv +c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnK +EUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA ++ammr24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJXV41gWnkgC7jcpPpFRSUSZaxyzrXmD1CIqQf0WgVDb +/12E0vR2DuZitgzUYtBaofM81aTtc0a2/YsrmqePGm0= +-----END CERTIFICATE-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/sslcerts/pub-other.pem Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBqzCCAVWgAwIBAgIJALwZS731c/ORMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNV +BAMMCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEw +MTAxNDIwNDUxNloXDTM1MDYwNTIwNDUxNlowMTESMBAGA1UEAwwJbG9jYWxob3N0 +MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANL +ADBIAkEAsxsapLbHrqqUKuQBxdpK4G3m2LjtyrTSdpzzzFlecxd5yhNP6AyWrufo +K4VMGo2xlu9xOo88nDSUNSKPuD09MwIDAQABo1AwTjAdBgNVHQ4EFgQUoIB1iMhN +y868rpQ2qk9dHnU6ebswHwYDVR0jBBgwFoAUoIB1iMhNy868rpQ2qk9dHnU6ebsw +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJ544f125CsE7J2t55PdFaF6 +bBlNBb91FCywBgSjhBjf+GG3TNPwrPdc3yqeq+hzJiuInqbOBv9abmMyq8Wsoig= +-----END CERTIFICATE-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/sslcerts/pub.pem Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNV +BAMMCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEw +MTAxNDIwMzAxNFoXDTM1MDYwNTIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0 +MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANL +ADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnKEUm34rDaXQd4lxxX +6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA+amm +r24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQw +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAFArvQFiAZJgQczRsbYlG1xl +t+truk37w5B3m3Ick1ntRcQrqs+hf0CO1q6Squ144geYaQ8CDirSR92fICELI1c= +-----END CERTIFICATE-----
--- a/tests/svn-safe-append.py Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/svn-safe-append.py Fri Jul 01 16:02:56 2016 -0500 @@ -1,9 +1,12 @@ #!/usr/bin/env python +from __future__ import absolute_import + __doc__ = """Same as `echo a >> b`, but ensures a changed mtime of b. Without this svn will not detect workspace changes.""" -import sys, os +import os +import sys text = sys.argv[1] fname = sys.argv[2]
--- a/tests/test-addremove-similar.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-addremove-similar.t Fri Jul 01 16:02:56 2016 -0500 @@ -1,7 +1,7 @@ $ hg init rep; cd rep $ touch empty-file - $ $PYTHON -c 'for x in range(10000): print x' > large-file + $ $PYTHON -c 'for x in range(10000): print(x)' > large-file $ hg addremove adding empty-file @@ -10,7 +10,7 @@ $ hg commit -m A $ rm large-file empty-file - $ $PYTHON -c 'for x in range(10,10000): print x' > another-file + $ $PYTHON -c 'for x in range(10,10000): print(x)' > another-file $ hg addremove -s50 adding another-file @@ -34,8 +34,8 @@ $ hg init rep2; cd rep2 - $ $PYTHON -c 'for x in range(10000): print x' > large-file - $ $PYTHON -c 'for x in range(50): print x' > tiny-file + $ $PYTHON -c 'for x in range(10000): print(x)' > large-file + $ $PYTHON -c 'for x in range(50): print(x)' > tiny-file $ hg addremove adding large-file @@ -43,7 +43,7 @@ $ hg commit -m A - $ $PYTHON -c 'for x in range(70): print x' > small-file + $ $PYTHON -c 'for x in range(70): print(x)' > small-file $ rm tiny-file $ rm large-file
--- a/tests/test-alias.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-alias.t Fri Jul 01 16:02:56 2016 -0500 @@ -525,6 +525,24 @@ (use "hg help" for the full list of commands or "hg -v" for details) [255] +environment variable changes in alias commands + + $ cat > $TESTTMP/setcount.py <<EOF + > import os + > def uisetup(ui): + > os.environ['COUNT'] = '2' + > EOF + + $ cat >> $HGRCPATH <<'EOF' + > [extensions] + > setcount = $TESTTMP/setcount.py + > [alias] + > showcount = log -T "$COUNT\n" -r . + > EOF + + $ COUNT=1 hg showcount + 2 + This should show id: $ hg --config alias.log='id' log
--- a/tests/test-archive.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-archive.t Fri Jul 01 16:02:56 2016 -0500 @@ -69,7 +69,12 @@ $ TIP=`hg id -v | cut -f1 -d' '` $ QTIP=`hg id -q` $ cat > getarchive.py <<EOF - > import os, sys, urllib2 + > from __future__ import absolute_import + > import os + > import sys + > from mercurial import ( + > util, + > ) > try: > # Set stdout to binary mode for win32 platforms > import msvcrt @@ -83,10 +88,14 @@ > node, archive, file = sys.argv[1:] > requeststr = 'cmd=archive;node=%s;type=%s;file=%s' % (node, archive, file) > try: - > f = urllib2.urlopen('http://127.0.0.1:%s/?%s' + > stdout = sys.stdout.buffer + > except AttributeError: + > stdout = sys.stdout + > try: + > f = util.urlreq.urlopen('http://127.0.0.1:%s/?%s' > % (os.environ['HGPORT'], requeststr)) - > sys.stdout.write(f.read()) - > except urllib2.HTTPError, e: + > stdout.write(f.read()) + > except util.urlerr.httperror as e: > sys.stderr.write(str(e) + '\n') > EOF $ python getarchive.py "$TIP" gz | gunzip | tar tf - 2>/dev/null @@ -195,15 +204,16 @@ > done $ cat > md5comp.py <<EOF + > from __future__ import print_function > try: > from hashlib import md5 > except ImportError: > from md5 import md5 > import sys > f1, f2 = sys.argv[1:3] - > h1 = md5(file(f1, 'rb').read()).hexdigest() - > h2 = md5(file(f2, 'rb').read()).hexdigest() - > print h1 == h2 or "md5 differ: " + repr((h1, h2)) + > h1 = md5(open(f1, 'rb').read()).hexdigest() + > h2 = md5(open(f2, 'rb').read()).hexdigest() + > print(h1 == h2 or "md5 differ: " + repr((h1, h2))) > EOF archive name is stored in the archive, so create similar archives and @@ -343,8 +353,9 @@ $ hg -R repo add repo/a $ hg -R repo commit -m '#0' -d '456789012 21600' $ cat > show_mtime.py <<EOF + > from __future__ import print_function > import sys, os - > print int(os.stat(sys.argv[1]).st_mtime) + > print(int(os.stat(sys.argv[1]).st_mtime)) > EOF $ hg -R repo archive --prefix tar-extracted archive.tar
--- a/tests/test-atomictempfile.py Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-atomictempfile.py Fri Jul 01 16:02:56 2016 -0500 @@ -1,42 +1,119 @@ +from __future__ import absolute_import + +import glob import os -import glob +import shutil +import tempfile import unittest -import silenttestrunner -from mercurial.util import atomictempfile +from mercurial import ( + util, +) +atomictempfile = util.atomictempfile class testatomictempfile(unittest.TestCase): - def test1_simple(self): - if os.path.exists('foo'): - os.remove('foo') - file = atomictempfile('foo') - (dir, basename) = os.path.split(file._tempname) - self.assertFalse(os.path.isfile('foo')) - self.assertTrue(basename in glob.glob('.foo-*')) + def setUp(self): + self._testdir = tempfile.mkdtemp('atomictempfiletest') + self._filename = os.path.join(self._testdir, 'testfilename') + + def tearDown(self): + shutil.rmtree(self._testdir, True) - file.write('argh\n') + def testsimple(self): + file = atomictempfile(self._filename) + self.assertFalse(os.path.isfile(self._filename)) + tempfilename = file._tempname + self.assertTrue(tempfilename in glob.glob( + os.path.join(self._testdir, '.testfilename-*'))) + + file.write(b'argh\n') file.close() - self.assertTrue(os.path.isfile('foo')) - self.assertTrue(basename not in glob.glob('.foo-*')) + self.assertTrue(os.path.isfile(self._filename)) + self.assertTrue(tempfilename not in glob.glob( + os.path.join(self._testdir, '.testfilename-*'))) # discard() removes the temp file without making the write permanent - def test2_discard(self): - if os.path.exists('foo'): - os.remove('foo') - file = atomictempfile('foo') + def testdiscard(self): + file = atomictempfile(self._filename) (dir, basename) = os.path.split(file._tempname) - file.write('yo\n') + file.write(b'yo\n') file.discard() - self.assertFalse(os.path.isfile('foo')) + self.assertFalse(os.path.isfile(self._filename)) self.assertTrue(basename not in os.listdir('.')) # if a programmer screws up and passes bad args to atomictempfile, they # get a plain ordinary TypeError, not infinite recursion - def test3_oops(self): + def testoops(self): self.assertRaises(TypeError, atomictempfile) + # checkambig=True avoids ambiguity of timestamp + def testcheckambig(self): + def atomicwrite(checkambig): + f = atomictempfile(self._filename, checkambig=checkambig) + f.write('FOO') + f.close() + + # try some times, because reproduction of ambiguity depends on + # "filesystem time" + for i in xrange(5): + atomicwrite(False) + oldstat = os.stat(self._filename) + if oldstat.st_ctime != oldstat.st_mtime: + # subsequent changing never causes ambiguity + continue + + repetition = 3 + + # repeat atomic write with checkambig=True, to examine + # whether st_mtime is advanced multiple times as expecetd + for j in xrange(repetition): + atomicwrite(True) + newstat = os.stat(self._filename) + if oldstat.st_ctime != newstat.st_ctime: + # timestamp ambiguity was naturally avoided while repetition + continue + + # st_mtime should be advanced "repetition" times, because + # all atomicwrite() occured at same time (in sec) + self.assertTrue(newstat.st_mtime == + ((oldstat.st_mtime + repetition) & 0x7fffffff)) + # no more examination is needed, if assumption above is true + break + else: + # This platform seems too slow to examine anti-ambiguity + # of file timestamp (or test happened to be executed at + # bad timing). Exit silently in this case, because running + # on other faster platforms can detect problems + pass + + def testread(self): + with open(self._filename, 'wb') as f: + f.write(b'foobar\n') + file = atomictempfile(self._filename, mode='rb') + self.assertTrue(file.read(), b'foobar\n') + file.discard() + + def testcontextmanagersuccess(self): + """When the context closes, the file is closed""" + with atomictempfile('foo') as f: + self.assertFalse(os.path.isfile('foo')) + f.write(b'argh\n') + self.assertTrue(os.path.isfile('foo')) + + def testcontextmanagerfailure(self): + """On exception, the file is discarded""" + try: + with atomictempfile('foo') as f: + self.assertFalse(os.path.isfile('foo')) + f.write(b'argh\n') + raise ValueError + except ValueError: + pass + self.assertFalse(os.path.isfile('foo')) + if __name__ == '__main__': + import silenttestrunner silenttestrunner.main(__name__)
--- a/tests/test-bookmarks-pushpull.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-bookmarks-pushpull.t Fri Jul 01 16:02:56 2016 -0500 @@ -113,6 +113,19 @@ exporting bookmark V [1] +exporting the active bookmark with 'push -B .' +demand that one of the bookmarks is activated + + $ hg update -r default + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (leaving bookmark V) + $ hg push -B . ../a + abort: no active bookmark + [255] + $ hg update -r V + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + (activating bookmark V) + delete the bookmark $ hg book -d V @@ -239,7 +252,10 @@ explicit pull should overwrite the local version (issue4439) - $ hg pull --config paths.foo=../a foo -B X + $ hg update -r X + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + (activating bookmark X) + $ hg pull --config paths.foo=../a foo -B . pulling from $TESTTMP/a (glob) no changes found divergent bookmark @ stored as @foo @@ -353,7 +369,10 @@ X 1:0d2164f0ce0d * Y 5:35d1ef0a8d1b Z 1:0d2164f0ce0d - $ hg pull -B Y + $ hg update -r Y + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + (activating bookmark Y) + $ hg pull -B . pulling from http://localhost:$HGPORT/ searching for changes adding changesets @@ -363,9 +382,9 @@ updating bookmark Y (run 'hg update' to get a working copy) $ hg book - * @ 1:0d2164f0ce0d + @ 1:0d2164f0ce0d X 1:0d2164f0ce0d - Y 5:35d1ef0a8d1b + * Y 5:35d1ef0a8d1b Z 1:0d2164f0ce0d (done with this section of the test)
--- a/tests/test-bugzilla.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-bugzilla.t Fri Jul 01 16:02:56 2016 -0500 @@ -54,7 +54,7 @@ $ cat bzmock.log && rm bzmock.log update bugid=123, newstate={}, committer='test' ---- - changeset 7875a8342c6f in repo $TESTTMP/mockremote refers to bug 123. + changeset 7875a8342c6f in repo $TESTTMP/mockremote refers to bug 123. (glob) details: Fixes bug 123 ----
--- a/tests/test-check-code.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-check-code.t Fri Jul 01 16:02:56 2016 -0500 @@ -1,5 +1,6 @@ #require test-repo + $ . "$TESTDIR/helpers-testrepo.sh" $ check_code="$TESTDIR"/../contrib/check-code.py $ cd "$TESTDIR"/.. @@ -16,4 +17,3 @@ Skipping i18n/polib.py it has no-che?k-code (glob) Skipping mercurial/httpclient/__init__.py it has no-che?k-code (glob) Skipping mercurial/httpclient/_readers.py it has no-che?k-code (glob) - Skipping mercurial/httpclient/socketutil.py it has no-che?k-code (glob)
--- a/tests/test-check-commit.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-check-commit.t Fri Jul 01 16:02:56 2016 -0500 @@ -2,10 +2,7 @@ Enable obsolescence to avoid the warning issue when obsmarker are found - $ cat >> $HGRCPATH << EOF - > [experimental] - > evolution=createmarkers - > EOF + $ . "$TESTDIR/helpers-testrepo.sh" Go back in the hg repo
--- a/tests/test-check-config.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-check-config.t Fri Jul 01 16:02:56 2016 -0500 @@ -1,5 +1,6 @@ #require test-repo + $ . "$TESTDIR/helpers-testrepo.sh" $ cd "$TESTDIR"/.. New errors are not allowed. Warnings are strongly discouraged.
--- a/tests/test-check-execute.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-check-execute.t Fri Jul 01 16:02:56 2016 -0500 @@ -1,5 +1,6 @@ #require test-repo execbit + $ . "$TESTDIR/helpers-testrepo.sh" $ cd "`dirname "$TESTDIR"`" look for python scripts without the execute bit
--- a/tests/test-check-module-imports.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-check-module-imports.t Fri Jul 01 16:02:56 2016 -0500 @@ -1,5 +1,6 @@ #require test-repo + $ . "$TESTDIR/helpers-testrepo.sh" $ import_checker="$TESTDIR"/../contrib/import-checker.py Run the doctests from the import checker, and make sure @@ -11,6 +12,7 @@ Run additional tests for the import checker $ mkdir testpackage + $ touch testpackage/__init__.py $ cat > testpackage/multiple.py << EOF > from __future__ import absolute_import @@ -113,7 +115,16 @@ > from testpackage.unsorted import foo > EOF - $ python "$import_checker" testpackage/*.py testpackage/subpackage/*.py + $ mkdir testpackage2 + $ touch testpackage2/__init__.py + + $ cat > testpackage2/latesymbolimport.py << EOF + > from __future__ import absolute_import + > from testpackage import unsorted + > from mercurial.node import hex + > EOF + + $ python "$import_checker" testpackage*/*.py testpackage/subpackage/*.py testpackage/importalias.py:2: ui module must be "as" aliased to uimod testpackage/importfromalias.py:2: ui from testpackage must be "as" aliased to uimod testpackage/importfromrelative.py:2: import should be relative: testpackage.unsorted @@ -131,6 +142,7 @@ testpackage/subpackage/localimport.py:8: import should be relative: testpackage.subpackage.levelpriority testpackage/symbolimport.py:2: direct symbol import foo from testpackage.unsorted testpackage/unsorted.py:3: imports not lexically sorted: os < sys + testpackage2/latesymbolimport.py:3: symbol import follows non-symbol import: mercurial.node [1] $ cd "$TESTDIR"/.. @@ -144,8 +156,13 @@ Known-bad files are excluded by -X as some of them would produce unstable outputs, which should be fixed later. - $ hg locate 'mercurial/**.py' 'hgext/**.py' 'tests/**.py' \ + $ hg locate 'set:**.py or grep(r"^#!.*?python")' \ > 'tests/**.t' \ + > -X contrib/debugshell.py \ + > -X contrib/win32/hgwebdir_wsgi.py \ + > -X doc/gendoc.py \ + > -X doc/hgmanpage.py \ + > -X i18n/posplit \ > -X tests/test-hgweb-auth.py \ > -X tests/hypothesishelpers.py \ > -X tests/test-ctxmanager.py \ @@ -162,5 +179,3 @@ > -X tests/test-hgweb-no-request-uri.t \ > -X tests/test-hgweb-non-interactive.t \ > | sed 's-\\-/-g' | python "$import_checker" - - Import cycle: hgext.largefiles.basestore -> hgext.largefiles.localstore -> hgext.largefiles.basestore - [1]
--- a/tests/test-check-py3-compat.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-check-py3-compat.t Fri Jul 01 16:02:56 2016 -0500 @@ -1,33 +1,15 @@ #require test-repo + $ . "$TESTDIR/helpers-testrepo.sh" $ cd "$TESTDIR"/.. $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py - hgext/fetch.py not using absolute_import hgext/fsmonitor/pywatchman/__init__.py not using absolute_import hgext/fsmonitor/pywatchman/__init__.py requires print_function hgext/fsmonitor/pywatchman/capabilities.py not using absolute_import hgext/fsmonitor/pywatchman/pybser.py not using absolute_import - hgext/gpg.py not using absolute_import - hgext/graphlog.py not using absolute_import - hgext/hgcia.py not using absolute_import - hgext/hgk.py not using absolute_import hgext/highlight/__init__.py not using absolute_import hgext/highlight/highlight.py not using absolute_import - hgext/histedit.py not using absolute_import - hgext/largefiles/__init__.py not using absolute_import - hgext/largefiles/basestore.py not using absolute_import - hgext/largefiles/lfcommands.py not using absolute_import - hgext/largefiles/lfutil.py not using absolute_import - hgext/largefiles/localstore.py not using absolute_import - hgext/largefiles/overrides.py not using absolute_import - hgext/largefiles/proto.py not using absolute_import - hgext/largefiles/remotestore.py not using absolute_import - hgext/largefiles/reposetup.py not using absolute_import - hgext/largefiles/uisetup.py not using absolute_import - hgext/largefiles/wirestore.py not using absolute_import - hgext/mq.py not using absolute_import - hgext/rebase.py not using absolute_import hgext/share.py not using absolute_import hgext/win32text.py not using absolute_import i18n/check-translation.py not using absolute_import @@ -36,30 +18,25 @@ tests/heredoctest.py requires print_function tests/md5sum.py not using absolute_import tests/readlink.py not using absolute_import - tests/readlink.py requires print_function tests/run-tests.py not using absolute_import - tests/svn-safe-append.py not using absolute_import - tests/test-atomictempfile.py not using absolute_import tests/test-demandimport.py not using absolute_import #if py3exe $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs $PYTHON3 contrib/check-py3-compat.py - contrib/check-code.py: invalid syntax: (unicode error) 'unicodeescape' codec can't decode bytes in position *-*: malformed \N character escape (<unknown>, line *) (glob) doc/hgmanpage.py: invalid syntax: invalid syntax (<unknown>, line *) (glob) hgext/automv.py: error importing module: <SyntaxError> invalid syntax (commands.py, line *) (line *) (glob) hgext/blackbox.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) - hgext/bugzilla.py: error importing module: <ImportError> No module named 'urlparse' (line *) (glob) + hgext/bugzilla.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/censor.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) - hgext/chgserver.py: error importing module: <ImportError> No module named 'SocketServer' (line *) (glob) + hgext/chgserver.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/children.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/churn.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/clonebundles.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/color.py: invalid syntax: invalid syntax (<unknown>, line *) (glob) hgext/convert/bzr.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob) - hgext/convert/common.py: error importing module: <ImportError> No module named 'cPickle' (line *) (glob) hgext/convert/convcmd.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob) hgext/convert/cvs.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob) - hgext/convert/cvsps.py: error importing module: <ImportError> No module named 'cPickle' (line *) (glob) + hgext/convert/cvsps.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/convert/darcs.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob) hgext/convert/filemap.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob) hgext/convert/git.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob) @@ -67,35 +44,35 @@ hgext/convert/hg.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/convert/monotone.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob) hgext/convert/p*.py: error importing module: <SystemError> Parent module 'hgext.convert' not loaded, cannot perform relative import (line *) (glob) - hgext/convert/subversion.py: error importing module: <ImportError> No module named 'cPickle' (line *) (glob) + hgext/convert/subversion.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/convert/transport.py: error importing module: <ImportError> No module named 'svn.client' (line *) (glob) hgext/eol.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/extdiff.py: error importing module: <SyntaxError> invalid syntax (archival.py, line *) (line *) (glob) - hgext/factotum.py: error importing: <ImportError> No module named 'httplib' (error at __init__.py:*) (glob) - hgext/fetch.py: error importing module: <SyntaxError> invalid syntax (commands.py, line *) (line *) (glob) + hgext/factotum.py: error importing: <ImportError> No module named 'rfc822' (error at __init__.py:*) (glob) + hgext/fetch.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/fsmonitor/watchmanclient.py: error importing module: <SystemError> Parent module 'hgext.fsmonitor' not loaded, cannot perform relative import (line *) (glob) - hgext/gpg.py: error importing module: <SyntaxError> invalid syntax (commands.py, line *) (line *) (glob) + hgext/gpg.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/graphlog.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) - hgext/hgcia.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/hgk.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/histedit.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob) hgext/keyword.py: error importing: <ImportError> No module named 'BaseHTTPServer' (error at common.py:*) (glob) - hgext/largefiles/basestore.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob) - hgext/largefiles/lfcommands.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob) + hgext/largefiles/basestore.py: error importing module: <SystemError> Parent module 'hgext.largefiles' not loaded, cannot perform relative import (line *) (glob) + hgext/largefiles/lfcommands.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/largefiles/lfutil.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) - hgext/largefiles/localstore.py: error importing module: <ImportError> No module named 'lfutil' (line *) (glob) - hgext/largefiles/overrides.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob) - hgext/largefiles/proto.py: error importing: <ImportError> No module named 'httplib' (error at httppeer.py:*) (glob) + hgext/largefiles/localstore.py: error importing module: <SystemError> Parent module 'hgext.largefiles' not loaded, cannot perform relative import (line *) (glob) + hgext/largefiles/overrides.py: error importing module: <SyntaxError> invalid syntax (archival.py, line *) (line *) (glob) + hgext/largefiles/proto.py: error importing: <SyntaxError> invalid syntax (bundle2.py, line *) (error at httppeer.py:*) (glob) hgext/largefiles/remotestore.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at wireproto.py:*) (glob) hgext/largefiles/reposetup.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) - hgext/largefiles/uisetup.py: error importing module: <SyntaxError> invalid syntax (archival.py, line *) (line *) (glob) - hgext/largefiles/wirestore.py: error importing module: <ImportError> No module named 'lfutil' (line *) (glob) - hgext/mq.py: error importing module: <SyntaxError> invalid syntax (commands.py, line *) (line *) (glob) + hgext/largefiles/storefactory.py: error importing: <SyntaxError> invalid syntax (bundle2.py, line *) (error at bundlerepo.py:*) (glob) + hgext/largefiles/uisetup.py: error importing: <ImportError> No module named 'BaseHTTPServer' (error at common.py:*) (glob) + hgext/largefiles/wirestore.py: error importing module: <SystemError> Parent module 'hgext.largefiles' not loaded, cannot perform relative import (line *) (glob) + hgext/mq.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/notify.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/pager.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/patchbomb.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/purge.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) - hgext/rebase.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob) + hgext/rebase.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/record.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/relink.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) hgext/schemes.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) @@ -111,7 +88,6 @@ mercurial/changelog.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/cmdutil.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/commands.py: invalid syntax: invalid syntax (<unknown>, line *) (glob) - mercurial/commandserver.py: error importing module: <ImportError> No module named 'SocketServer' (line *) (glob) mercurial/context.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/copies.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/crecord.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) @@ -123,7 +99,7 @@ mercurial/filelog.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/filemerge.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/fileset.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) - mercurial/formatter.py: error importing module: <ImportError> No module named 'cPickle' (line *) (glob) + mercurial/formatter.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/graphmod.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/help.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/hg.py: error importing: <SyntaxError> invalid syntax (bundle*.py, line *) (error at bundlerepo.py:*) (glob) @@ -137,10 +113,9 @@ mercurial/hgweb/webutil.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob) mercurial/hgweb/wsgicgi.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob) mercurial/hook.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) - mercurial/httpclient/_readers.py: error importing module: <ImportError> No module named 'httplib' (line *) (glob) - mercurial/httpconnection.py: error importing: <ImportError> No module named 'httplib' (error at __init__.py:*) (glob) - mercurial/httppeer.py: error importing module: <ImportError> No module named 'httplib' (line *) (glob) - mercurial/keepalive.py: error importing module: <ImportError> No module named 'httplib' (line *) (glob) + mercurial/httpconnection.py: error importing: <ImportError> No module named 'rfc822' (error at __init__.py:*) (glob) + mercurial/httppeer.py: error importing module: <SyntaxError> invalid syntax (bundle2.py, line *) (line *) (glob) + mercurial/keepalive.py: error importing module: <ImportError> No module named 'thread' (line *) (glob) mercurial/localrepo.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/mail.py: error importing module: <AttributeError> module 'email' has no attribute 'Header' (line *) (glob) mercurial/manifest.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) @@ -164,13 +139,12 @@ mercurial/templatefilters.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/templatekw.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/templater.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) - mercurial/ui.py: error importing: <ImportError> No module named 'cPickle' (error at formatter.py:*) (glob) + mercurial/ui.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/unionrepo.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) - mercurial/url.py: error importing module: <ImportError> No module named 'httplib' (line *) (glob) + mercurial/url.py: error importing: <ImportError> No module named 'rfc822' (error at __init__.py:*) (glob) mercurial/verify.py: error importing: <AttributeError> 'dict' object has no attribute 'iteritems' (error at revset.py:*) (glob) mercurial/win*.py: error importing module: <ImportError> No module named 'msvcrt' (line *) (glob) mercurial/windows.py: error importing module: <ImportError> No module named '_winreg' (line *) (glob) mercurial/wireproto.py: error importing module: <SyntaxError> invalid syntax (bundle*.py, line *) (line *) (glob) - tests/readlink.py: invalid syntax: invalid syntax (<unknown>, line *) (glob) #endif
--- a/tests/test-check-pyflakes.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-check-pyflakes.t Fri Jul 01 16:02:56 2016 -0500 @@ -1,11 +1,12 @@ -#require test-repo pyflakes +#require test-repo pyflakes hg10 + $ . "$TESTDIR/helpers-testrepo.sh" $ cd "`dirname "$TESTDIR"`" run pyflakes on all tracked files ending in .py or without a file ending (skipping binary file random-seed) - $ hg locate 'set:**.py or grep("^!#.*python")' 2>/dev/null \ + $ hg locate 'set:**.py or grep("^#!.*python")' 2>/dev/null \ > | xargs pyflakes 2>/dev/null | "$TESTDIR/filterpyflakes.py" tests/filterpyflakes.py:61: undefined name 'undefinedname'
--- a/tests/test-check-shbang.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-check-shbang.t Fri Jul 01 16:02:56 2016 -0500 @@ -1,5 +1,6 @@ #require test-repo + $ . "$TESTDIR/helpers-testrepo.sh" $ cd "`dirname "$TESTDIR"`" look for python scripts that do not use /usr/bin/env
--- a/tests/test-chg.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-chg.t Fri Jul 01 16:02:56 2016 -0500 @@ -1,12 +1,86 @@ +#require chg + + $ cp $HGRCPATH $HGRCPATH.orig + init repo - $ hg init foo + $ chg init foo $ cd foo ill-formed config - $ hg status + $ chg status $ echo '=brokenconfig' >> $HGRCPATH - $ hg status + $ chg status hg: parse error at * (glob) [255] + + $ cp $HGRCPATH.orig $HGRCPATH + $ cd .. + +server lifecycle +---------------- + +chg server should be restarted on code change, and old server will shut down +automatically. In this test, we use the following time parameters: + + - "sleep 1" to make mtime different + - "sleep 2" to notice mtime change (polling interval is 1 sec) + +set up repository with an extension: + + $ chg init extreload + $ cd extreload + $ touch dummyext.py + $ cat <<EOF >> .hg/hgrc + > [extensions] + > dummyext = dummyext.py + > EOF + +isolate socket directory for stable result: + + $ OLDCHGSOCKNAME=$CHGSOCKNAME + $ mkdir chgsock + $ CHGSOCKNAME=`pwd`/chgsock/server + +warm up server: + + $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start' + chg: debug: start cmdserver at $TESTTMP/extreload/chgsock/server + +new server should be started if extension modified: + + $ sleep 1 + $ touch dummyext.py + $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start' + chg: debug: instruction: unlink $TESTTMP/extreload/chgsock/server-* (glob) + chg: debug: instruction: reconnect + chg: debug: start cmdserver at $TESTTMP/extreload/chgsock/server + +old server will shut down, while new server should still be reachable: + + $ sleep 2 + $ CHGDEBUG= chg log 2>&1 | (egrep 'instruction|start' || true) + +socket file should never be unlinked by old server: +(simulates unowned socket by updating mtime, which makes sure server exits +at polling cycle) + + $ ls chgsock/server-* + chgsock/server-* (glob) + $ touch chgsock/server-* + $ sleep 2 + $ ls chgsock/server-* + chgsock/server-* (glob) + +since no server is reachable from socket file, new server should be started: +(this test makes sure that old server shut down automatically) + + $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start' + chg: debug: start cmdserver at $TESTTMP/extreload/chgsock/server + +shut down servers and restore environment: + + $ rm -R chgsock + $ CHGSOCKNAME=$OLDCHGSOCKNAME + $ cd ..
--- a/tests/test-command-template.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-command-template.t Fri Jul 01 16:02:56 2016 -0500 @@ -3320,6 +3320,15 @@ hg: parse error: pad() expects an integer width [255] +Test separate function + + $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n' + a-b-c + $ hg log -r 0 -T '{separate(" ", "{rev}:{node|short}", author|user, branch)}\n' + 0:f7769ec2ab97 test default + $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n' + a \x1b[0;31mb\x1b[0m c d (esc) + Test ifcontains function $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n' @@ -3768,10 +3777,10 @@ $ hg debugtemplate --config templatealias.bad='x(' -v '{bad}' (template ('symbol', 'bad')) - abort: failed to parse the definition of template alias "bad": at 2: not a prefix: end + abort: bad definition of template alias "bad": at 2: not a prefix: end [255] $ hg log --config templatealias.bad='x(' -T '{bad}' - abort: failed to parse the definition of template alias "bad": at 2: not a prefix: end + abort: bad definition of template alias "bad": at 2: not a prefix: end [255] $ cd ..
--- a/tests/test-config.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-config.t Fri Jul 01 16:02:56 2016 -0500 @@ -90,3 +90,15 @@ $ hg config Section.idontexist [1] + +sub-options in [paths] aren't expanded + + $ cat > .hg/hgrc << EOF + > [paths] + > foo = ~/foo + > foo:suboption = ~/foo + > EOF + + $ hg showconfig paths + paths.foo:suboption=~/foo + paths.foo=$TESTTMP/foo
--- a/tests/test-context.py Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-context.py Fri Jul 01 16:02:56 2016 -0500 @@ -14,7 +14,7 @@ # create 'foo' with fixed time stamp f = open('foo', 'wb') -f.write('foo\n') +f.write(b'foo\n') f.close() os.utime('foo', (1000, 1000))
--- a/tests/test-contrib-check-code.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-contrib-check-code.t Fri Jul 01 16:02:56 2016 -0500 @@ -248,3 +248,64 @@ > {desc|escape} warning: follow desc keyword with either firstline or websub [1] + +'string join across lines with no space' detection + + $ cat > stringjoin.py <<EOF + > foo = (' foo' + > 'bar foo.' + > 'bar foo:' + > 'bar foo@' + > 'bar foo%' + > 'bar foo*' + > 'bar foo+' + > 'bar foo-' + > 'bar') + > EOF + +'missing _() in ui message' detection + + $ cat > uigettext.py <<EOF + > ui.status("% 10s %05d % -3.2f %*s %%" + > # this use '\\\\' instead of '\\', because the latter in + > # heredoc on shell becomes just '\' + > '\\\\ \n \t \0' + > """12345 + > """ + > '''.:*+-= + > ''' "%-6d \n 123456 .:*+-= foobar") + > EOF + +(Checking multiple invalid files at once examines whether caching +translation table for repquote() works as expected or not. All files +should break rules depending on result of repquote(), in this case) + + $ "$check_code" stringjoin.py uigettext.py + stringjoin.py:1: + > foo = (' foo' + string join across lines with no space + stringjoin.py:2: + > 'bar foo.' + string join across lines with no space + stringjoin.py:3: + > 'bar foo:' + string join across lines with no space + stringjoin.py:4: + > 'bar foo@' + string join across lines with no space + stringjoin.py:5: + > 'bar foo%' + string join across lines with no space + stringjoin.py:6: + > 'bar foo*' + string join across lines with no space + stringjoin.py:7: + > 'bar foo+' + string join across lines with no space + stringjoin.py:8: + > 'bar foo-' + string join across lines with no space + uigettext.py:1: + > ui.status("% 10s %05d % -3.2f %*s %%" + missing _() in ui message (use () to hide false-positives) + [1]
--- a/tests/test-contrib-perf.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-contrib-perf.t Fri Jul 01 16:02:56 2016 -0500 @@ -2,6 +2,7 @@ Set vars: + $ . "$TESTDIR/helpers-testrepo.sh" $ CONTRIBDIR="$TESTDIR/../contrib" Prepare repo:
--- a/tests/test-convert.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-convert.t Fri Jul 01 16:02:56 2016 -0500 @@ -422,7 +422,7 @@ assuming destination emptydir-hg initializing destination emptydir-hg repository emptydir does not look like a CVS checkout - $TESTTMP/emptydir does not look like a Git repository + $TESTTMP/emptydir does not look like a Git repository (glob) emptydir does not look like a Subversion repository emptydir is not a local Mercurial repository emptydir does not look like a darcs repository
--- a/tests/test-debian-packages.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-debian-packages.t Fri Jul 01 16:02:56 2016 -0500 @@ -1,5 +1,7 @@ #require test-repo slow debhelper + $ . "$TESTDIR/helpers-testrepo.sh" + Ensure debuild doesn't run the testsuite, as that could get silly. $ DEB_BUILD_OPTIONS=nocheck $ export DEB_BUILD_OPTIONS
--- a/tests/test-debugbundle.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-debugbundle.t Fri Jul 01 16:02:56 2016 -0500 @@ -13,6 +13,13 @@ 282 (manifests) 93 b 93 c + $ hg bundle --base 0 --rev tip bundle2.hg -v --type none-v2 + 2 changesets found + uncompressed size of bundle content: + 372 (changelog) + 322 (manifests) + 113 b + 113 c Terse output: @@ -20,6 +27,14 @@ 0e067c57feba1a5694ca4844f05588bb1bf82342 991a3460af53952d10ec8a295d3d2cc2e5fa9690 +Terse output: + + $ hg debugbundle bundle2.hg + Stream params: {} + changegroup -- "{'version': '02'}" + 0e067c57feba1a5694ca4844f05588bb1bf82342 + 991a3460af53952d10ec8a295d3d2cc2e5fa9690 + Verbose output: $ hg debugbundle --all bundle.hg @@ -39,4 +54,23 @@ c b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0000000000000000000000000000000000000000 0 + $ hg debugbundle --all bundle2.hg + Stream params: {} + changegroup -- "{'version': '02'}" + format: id, p1, p2, cset, delta base, len(delta) + + changelog + 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a 80 + 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 80 + + manifest + 686dbf0aeca417636fa26a9121c681eabbb15a20 8515d4bfda768e04af4c13a69a72e28c7effbea7 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 8515d4bfda768e04af4c13a69a72e28c7effbea7 55 + ae25a31b30b3490a981e7b96a3238cc69583fda1 686dbf0aeca417636fa26a9121c681eabbb15a20 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 686dbf0aeca417636fa26a9121c681eabbb15a20 55 + + b + b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 0000000000000000000000000000000000000000 0 + + c + b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0000000000000000000000000000000000000000 0 + $ cd ..
--- a/tests/test-default-push.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-default-push.t Fri Jul 01 16:02:56 2016 -0500 @@ -105,4 +105,43 @@ adding file changes added 1 changesets with 1 changes to 1 files +:pushrev is used when no -r is passed + + $ cat >> .hg/hgrc << EOF + > default:pushrev = . + > EOF + $ hg -q up -r 0 + $ echo head1 > foo + $ hg -q commit -A -m head1 + $ hg -q up -r 0 + $ echo head2 > foo + $ hg -q commit -A -m head2 + $ hg push -f + pushing to file:/*/$TESTTMP/pushurlsource/../pushurldest (glob) + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + + $ hg --config 'paths.default:pushrev=draft()' push -f + pushing to file:/*/$TESTTMP/pushurlsource/../pushurldest (glob) + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + +Invalid :pushrev raises appropriately + + $ hg --config 'paths.default:pushrev=notdefined()' push + pushing to file:/*/$TESTTMP/pushurlsource/../pushurldest (glob) + hg: parse error: unknown identifier: notdefined + [255] + + $ hg --config 'paths.default:pushrev=(' push + pushing to file:/*/$TESTTMP/pushurlsource/../pushurldest (glob) + hg: parse error at 1: not a prefix: end + [255] + $ cd ..
--- a/tests/test-devel-warnings.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-devel-warnings.t Fri Jul 01 16:02:56 2016 -0500 @@ -10,14 +10,17 @@ > > @command('buggylocking', [], '') > def buggylocking(ui, repo): - > tr = repo.transaction('buggy') - > # make sure we rollback the transaction as we don't want to rely on the__del__ - > tr.release() > lo = repo.lock() > wl = repo.wlock() > wl.release() > lo.release() > + > @command('buggytransaction', [], '') + > def buggylocking(ui, repo): + > tr = repo.transaction('buggy') + > # make sure we rollback the transaction as we don't want to rely on the__del__ + > tr.release() + > > @command('properlocking', [], '') > def properlocking(ui, repo): > """check that reentrance is fine""" @@ -74,7 +77,6 @@ $ hg init lock-checker $ cd lock-checker $ hg buggylocking - devel-warn: transaction with no lock at: $TESTTMP/buggylocking.py:* (buggylocking) (glob) devel-warn: "wlock" acquired after "lock" at: $TESTTMP/buggylocking.py:* (buggylocking) (glob) $ cat << EOF >> $HGRCPATH > [devel] @@ -82,21 +84,8 @@ > check-locks=1 > EOF $ hg buggylocking - devel-warn: transaction with no lock at: $TESTTMP/buggylocking.py:* (buggylocking) (glob) devel-warn: "wlock" acquired after "lock" at: $TESTTMP/buggylocking.py:* (buggylocking) (glob) $ hg buggylocking --traceback - devel-warn: transaction with no lock at: - */hg:* in * (glob) - */mercurial/dispatch.py:* in run (glob) - */mercurial/dispatch.py:* in dispatch (glob) - */mercurial/dispatch.py:* in _runcatch (glob) - */mercurial/dispatch.py:* in _dispatch (glob) - */mercurial/dispatch.py:* in runcommand (glob) - */mercurial/dispatch.py:* in _runcommand (glob) - */mercurial/dispatch.py:* in checkargs (glob) - */mercurial/dispatch.py:* in <lambda> (glob) - */mercurial/util.py:* in check (glob) - $TESTTMP/buggylocking.py:* in buggylocking (glob) devel-warn: "wlock" acquired after "lock" at: */hg:* in * (glob) */mercurial/dispatch.py:* in run (glob) @@ -122,7 +111,8 @@ [255] $ hg log -r "oldstyle()" -T '{rev}\n' - devel-warn: revset "oldstyle" use list instead of smartset, (upgrade your code) at: */mercurial/revset.py:* (mfunc) (glob) + devel-warn: revset "oldstyle" uses list instead of smartset + (compatibility will be dropped after Mercurial-3.9, update your code.) at: *mercurial/revset.py:* (mfunc) (glob) 0 $ hg oldanddeprecated devel-warn: foorbar is deprecated, go shopping @@ -143,7 +133,8 @@ */mercurial/util.py:* in check (glob) $TESTTMP/buggylocking.py:* in oldanddeprecated (glob) $ hg blackbox -l 9 - 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> devel-warn: revset "oldstyle" use list instead of smartset, (upgrade your code) at: */mercurial/revset.py:* (mfunc) (glob) + 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> devel-warn: revset "oldstyle" uses list instead of smartset + (compatibility will be dropped after Mercurial-3.9, update your code.) at: *mercurial/revset.py:* (mfunc) (glob) 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> log -r oldstyle() -T {rev}\n exited 0 after * seconds (glob) 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> oldanddeprecated 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> devel-warn: foorbar is deprecated, go shopping @@ -165,4 +156,18 @@ $TESTTMP/buggylocking.py:* in oldanddeprecated (glob) 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> oldanddeprecated --traceback exited 0 after * seconds (glob) 1970/01/01 00:00:00 bob @cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b (5000)> blackbox -l 9 + +Test programming error failure: + + $ hg buggytransaction 2>&1 | egrep -v '^ ' + ** Unknown exception encountered with possibly-broken third-party extension buggylocking + ** which supports versions unknown of Mercurial. + ** Please disable buggylocking and try your action again. + ** If that fixes the bug please report it to the extension author. + ** Python * (glob) + ** Mercurial Distributed SCM (*) (glob) + ** Extensions loaded: * (glob) + Traceback (most recent call last): + RuntimeError: programming error: transaction requires locking + $ cd ..
--- a/tests/test-docker-packaging.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-docker-packaging.t Fri Jul 01 16:02:56 2016 -0500 @@ -1,5 +1,7 @@ #require test-repo slow docker + $ . "$TESTDIR/helpers-testrepo.sh" + Ensure debuild doesn't run the testsuite, as that could get silly. $ DEB_BUILD_OPTIONS=nocheck $ export DEB_BUILD_OPTIONS
--- a/tests/test-extension.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-extension.t Fri Jul 01 16:02:56 2016 -0500 @@ -249,6 +249,191 @@ $TESTTMP/a (glob) #endif +#if absimport + +Examine whether module loading is delayed until actual refering, even +though module is imported with "absolute_import" feature. + +Files below in each packages are used for descirbed purpose: + +- "called": examine whether "from MODULE import ATTR" works correctly +- "unused": examine whether loading is delayed correctly +- "used": examine whether "from PACKAGE import MODULE" works correctly + +Package hierarchy is needed to examine whether demand importing works +as expected for "from SUB.PACK.AGE import MODULE". + +Setup "external library" to be imported with "absolute_import" +feature. + + $ mkdir -p $TESTTMP/extlibroot/lsub1/lsub2 + $ touch $TESTTMP/extlibroot/__init__.py + $ touch $TESTTMP/extlibroot/lsub1/__init__.py + $ touch $TESTTMP/extlibroot/lsub1/lsub2/__init__.py + + $ cat > $TESTTMP/extlibroot/lsub1/lsub2/called.py <<EOF + > def func(): + > return "this is extlibroot.lsub1.lsub2.called.func()" + > EOF + $ cat > $TESTTMP/extlibroot/lsub1/lsub2/unused.py <<EOF + > raise Exception("extlibroot.lsub1.lsub2.unused is loaded unintentionally") + > EOF + $ cat > $TESTTMP/extlibroot/lsub1/lsub2/used.py <<EOF + > detail = "this is extlibroot.lsub1.lsub2.used" + > EOF + +Setup sub-package of "external library", which causes instantiation of +demandmod in "recurse down the module chain" code path. Relative +importing with "absolute_import" feature isn't tested, because "level +>=1 " doesn't cause instantiation of demandmod. + + $ mkdir -p $TESTTMP/extlibroot/recursedown/abs + $ cat > $TESTTMP/extlibroot/recursedown/abs/used.py <<EOF + > detail = "this is extlibroot.recursedown.abs.used" + > EOF + $ cat > $TESTTMP/extlibroot/recursedown/abs/__init__.py <<EOF + > from __future__ import absolute_import + > from extlibroot.recursedown.abs.used import detail + > EOF + + $ mkdir -p $TESTTMP/extlibroot/recursedown/legacy + $ cat > $TESTTMP/extlibroot/recursedown/legacy/used.py <<EOF + > detail = "this is extlibroot.recursedown.legacy.used" + > EOF + $ cat > $TESTTMP/extlibroot/recursedown/legacy/__init__.py <<EOF + > # legacy style (level == -1) import + > from extlibroot.recursedown.legacy.used import detail + > EOF + + $ cat > $TESTTMP/extlibroot/recursedown/__init__.py <<EOF + > from __future__ import absolute_import + > from extlibroot.recursedown.abs import detail as absdetail + > from .legacy import detail as legacydetail + > EOF + +Setup extension local modules to be imported with "absolute_import" +feature. + + $ mkdir -p $TESTTMP/absextroot/xsub1/xsub2 + $ touch $TESTTMP/absextroot/xsub1/__init__.py + $ touch $TESTTMP/absextroot/xsub1/xsub2/__init__.py + + $ cat > $TESTTMP/absextroot/xsub1/xsub2/called.py <<EOF + > def func(): + > return "this is absextroot.xsub1.xsub2.called.func()" + > EOF + $ cat > $TESTTMP/absextroot/xsub1/xsub2/unused.py <<EOF + > raise Exception("absextroot.xsub1.xsub2.unused is loaded unintentionally") + > EOF + $ cat > $TESTTMP/absextroot/xsub1/xsub2/used.py <<EOF + > detail = "this is absextroot.xsub1.xsub2.used" + > EOF + +Setup extension local modules to examine whether demand importing +works as expected in "level > 1" case. + + $ cat > $TESTTMP/absextroot/relimportee.py <<EOF + > detail = "this is absextroot.relimportee" + > EOF + $ cat > $TESTTMP/absextroot/xsub1/xsub2/relimporter.py <<EOF + > from __future__ import absolute_import + > from ... import relimportee + > detail = "this relimporter imports %r" % (relimportee.detail) + > EOF + +Setup modules, which actually import extension local modules at +runtime. + + $ cat > $TESTTMP/absextroot/absolute.py << EOF + > from __future__ import absolute_import + > + > # import extension local modules absolutely (level = 0) + > from absextroot.xsub1.xsub2 import used, unused + > from absextroot.xsub1.xsub2.called import func + > + > def getresult(): + > result = [] + > result.append(used.detail) + > result.append(func()) + > return result + > EOF + + $ cat > $TESTTMP/absextroot/relative.py << EOF + > from __future__ import absolute_import + > + > # import extension local modules relatively (level == 1) + > from .xsub1.xsub2 import used, unused + > from .xsub1.xsub2.called import func + > + > # import a module, which implies "importing with level > 1" + > from .xsub1.xsub2 import relimporter + > + > def getresult(): + > result = [] + > result.append(used.detail) + > result.append(func()) + > result.append(relimporter.detail) + > return result + > EOF + +Setup main procedure of extension. + + $ cat > $TESTTMP/absextroot/__init__.py <<EOF + > from __future__ import absolute_import + > from mercurial import cmdutil + > cmdtable = {} + > command = cmdutil.command(cmdtable) + > + > # "absolute" and "relative" shouldn't be imported before actual + > # command execution, because (1) they import same modules, and (2) + > # preceding import (= instantiate "demandmod" object instead of + > # real "module" object) might hide problem of succeeding import. + > + > @command('showabsolute', [], norepo=True) + > def showabsolute(ui, *args, **opts): + > from absextroot import absolute + > ui.write('ABS: %s\n' % '\nABS: '.join(absolute.getresult())) + > + > @command('showrelative', [], norepo=True) + > def showrelative(ui, *args, **opts): + > from . import relative + > ui.write('REL: %s\n' % '\nREL: '.join(relative.getresult())) + > + > # import modules from external library + > from extlibroot.lsub1.lsub2 import used as lused, unused as lunused + > from extlibroot.lsub1.lsub2.called import func as lfunc + > from extlibroot.recursedown import absdetail, legacydetail + > + > def uisetup(ui): + > result = [] + > result.append(lused.detail) + > result.append(lfunc()) + > result.append(absdetail) + > result.append(legacydetail) + > ui.write('LIB: %s\n' % '\nLIB: '.join(result)) + > EOF + +Examine module importing. + + $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showabsolute) + LIB: this is extlibroot.lsub1.lsub2.used + LIB: this is extlibroot.lsub1.lsub2.called.func() + LIB: this is extlibroot.recursedown.abs.used + LIB: this is extlibroot.recursedown.legacy.used + ABS: this is absextroot.xsub1.xsub2.used + ABS: this is absextroot.xsub1.xsub2.called.func() + + $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.absextroot=$TESTTMP/absextroot showrelative) + LIB: this is extlibroot.lsub1.lsub2.used + LIB: this is extlibroot.lsub1.lsub2.called.func() + LIB: this is extlibroot.recursedown.abs.used + LIB: this is extlibroot.recursedown.legacy.used + REL: this is absextroot.xsub1.xsub2.used + REL: this is absextroot.xsub1.xsub2.called.func() + REL: this relimporter imports 'this is absextroot.relimportee' + +#endif + $ cd .. hide outer repo @@ -958,7 +1143,7 @@ ** Extensions loaded: throw, older Declare the version as supporting this hg version, show regular bts link: - $ hgver=`$PYTHON -c 'from mercurial import util; print util.version().split("+")[0]'` + $ hgver=`hg debuginstall -T '{hgver}'` $ echo 'testedwith = """'"$hgver"'"""' >> throw.py $ if [ -z "$hgver" ]; then > echo "unable to fetch a mercurial version. Make sure __version__ is correct";
--- a/tests/test-filelog.py Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-filelog.py Fri Jul 01 16:02:56 2016 -0500 @@ -3,14 +3,15 @@ Tests the behavior of filelog w.r.t. data starting with '\1\n' """ from __future__ import absolute_import, print_function + +from mercurial.node import ( + hex, + nullid, +) from mercurial import ( hg, ui as uimod, ) -from mercurial.node import ( - hex, - nullid, -) myui = uimod.ui() repo = hg.repository(myui, path='.', create=True)
--- a/tests/test-glog-topological.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-glog-topological.t Fri Jul 01 16:02:56 2016 -0500 @@ -40,7 +40,7 @@ (display all nodes) - $ hg --config experimental.graph-group-branches=1 log -G + $ hg log -G -r 'sort(all(), topo)' o 8 | o 3 @@ -62,7 +62,7 @@ (revset skipping nodes) - $ hg --config experimental.graph-group-branches=1 log -G --rev 'not (2+6)' + $ hg log -G --rev 'sort(not (2+6), topo)' o 8 | o 3 @@ -80,7 +80,7 @@ (begin) from the other branch - $ hg --config experimental.graph-group-branches=1 --config experimental.graph-group-branches.firstbranch=5 log -G + $ hg log -G -r 'sort(all(), topo, topo.firstbranch=5)' o 7 | o 6
--- a/tests/test-glog.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-glog.t Fri Jul 01 16:02:56 2016 -0500 @@ -3036,7 +3036,229 @@ date: Thu Jan 01 00:00:04 1970 +0000 summary: (4) merge two known; one immediate left, one immediate right +Draw only part of a grandparent line differently with "<N><char>"; only the +last N lines (for positive N) or everything but the first N lines (for +negative N) along the current node use the style, the rest of the edge uses +the parent edge styling. +Last 3 lines: + + $ cat << EOF >> $HGRCPATH + > [experimental] + > graphstyle.parent = ! + > graphstyle.grandparent = 3. + > graphstyle.missing = + > EOF + $ hg log -G -r '36:18 & file("a")' -m + @ changeset: 36:08a19a744424 + ! branch: branch + ! tag: tip + ! parent: 35:9159c3644c5e + ! parent: 35:9159c3644c5e + ! user: test + . date: Thu Jan 01 00:00:36 1970 +0000 + . summary: (36) buggy merge: identical parents + . + o changeset: 32:d06dffa21a31 + !\ parent: 27:886ed638191b + ! ! parent: 31:621d83e11f67 + ! ! user: test + ! . date: Thu Jan 01 00:00:32 1970 +0000 + ! . summary: (32) expand + ! . + o ! changeset: 31:621d83e11f67 + !\! parent: 21:d42a756af44d + ! ! parent: 30:6e11cd4b648f + ! ! user: test + ! ! date: Thu Jan 01 00:00:31 1970 +0000 + ! ! summary: (31) expand + ! ! + o ! changeset: 30:6e11cd4b648f + !\ \ parent: 28:44ecd0b9ae99 + ! ~ ! parent: 29:cd9bb2be7593 + ! ! user: test + ! ! date: Thu Jan 01 00:00:30 1970 +0000 + ! ! summary: (30) expand + ! / + o ! changeset: 28:44ecd0b9ae99 + !\ \ parent: 1:6db2ef61d156 + ! ~ ! parent: 26:7f25b6c2f0b9 + ! ! user: test + ! ! date: Thu Jan 01 00:00:28 1970 +0000 + ! ! summary: (28) merge zero known + ! / + o ! changeset: 26:7f25b6c2f0b9 + !\ \ parent: 18:1aa84d96232a + ! ! ! parent: 25:91da8ed57247 + ! ! ! user: test + ! ! ! date: Thu Jan 01 00:00:26 1970 +0000 + ! ! ! summary: (26) merge one known; far right + ! ! ! + ! o ! changeset: 25:91da8ed57247 + ! !\! parent: 21:d42a756af44d + ! ! ! parent: 24:a9c19a3d96b7 + ! ! ! user: test + ! ! ! date: Thu Jan 01 00:00:25 1970 +0000 + ! ! ! summary: (25) merge one known; far left + ! ! ! + ! o ! changeset: 24:a9c19a3d96b7 + ! !\ \ parent: 0:e6eb3150255d + ! ! ~ ! parent: 23:a01cddf0766d + ! ! ! user: test + ! ! ! date: Thu Jan 01 00:00:24 1970 +0000 + ! ! ! summary: (24) merge one known; immediate right + ! ! / + ! o ! changeset: 23:a01cddf0766d + ! !\ \ parent: 1:6db2ef61d156 + ! ! ~ ! parent: 22:e0d9cccacb5d + ! ! ! user: test + ! ! ! date: Thu Jan 01 00:00:23 1970 +0000 + ! ! ! summary: (23) merge one known; immediate left + ! ! / + ! o ! changeset: 22:e0d9cccacb5d + !/!/ parent: 18:1aa84d96232a + ! ! parent: 21:d42a756af44d + ! ! user: test + ! ! date: Thu Jan 01 00:00:22 1970 +0000 + ! ! summary: (22) merge two known; one far left, one far right + ! ! + ! o changeset: 21:d42a756af44d + ! !\ parent: 19:31ddc2c1573b + ! ! ! parent: 20:d30ed6450e32 + ! ! ! user: test + ! ! ! date: Thu Jan 01 00:00:21 1970 +0000 + ! ! ! summary: (21) expand + ! ! ! + +---o changeset: 20:d30ed6450e32 + ! ! | parent: 0:e6eb3150255d + ! ! ~ parent: 18:1aa84d96232a + ! ! user: test + ! ! date: Thu Jan 01 00:00:20 1970 +0000 + ! ! summary: (20) merge two known; two far right + ! ! + ! o changeset: 19:31ddc2c1573b + ! |\ parent: 15:1dda3f72782d + ! ~ ~ parent: 17:44765d7c06e0 + ! user: test + ! date: Thu Jan 01 00:00:19 1970 +0000 + ! summary: (19) expand + ! + o changeset: 18:1aa84d96232a + |\ parent: 1:6db2ef61d156 + ~ ~ parent: 15:1dda3f72782d + user: test + date: Thu Jan 01 00:00:18 1970 +0000 + summary: (18) merge two known; two far left + +All but the first 3 lines: + + $ cat << EOF >> $HGRCPATH + > [experimental] + > graphstyle.parent = ! + > graphstyle.grandparent = -3. + > graphstyle.missing = + > EOF + $ hg log -G -r '36:18 & file("a")' -m + @ changeset: 36:08a19a744424 + ! branch: branch + ! tag: tip + . parent: 35:9159c3644c5e + . parent: 35:9159c3644c5e + . user: test + . date: Thu Jan 01 00:00:36 1970 +0000 + . summary: (36) buggy merge: identical parents + . + o changeset: 32:d06dffa21a31 + !\ parent: 27:886ed638191b + ! ! parent: 31:621d83e11f67 + ! . user: test + ! . date: Thu Jan 01 00:00:32 1970 +0000 + ! . summary: (32) expand + ! . + o ! changeset: 31:621d83e11f67 + !\! parent: 21:d42a756af44d + ! ! parent: 30:6e11cd4b648f + ! ! user: test + ! ! date: Thu Jan 01 00:00:31 1970 +0000 + ! ! summary: (31) expand + ! ! + o ! changeset: 30:6e11cd4b648f + !\ \ parent: 28:44ecd0b9ae99 + ! ~ ! parent: 29:cd9bb2be7593 + ! ! user: test + ! ! date: Thu Jan 01 00:00:30 1970 +0000 + ! ! summary: (30) expand + ! / + o ! changeset: 28:44ecd0b9ae99 + !\ \ parent: 1:6db2ef61d156 + ! ~ ! parent: 26:7f25b6c2f0b9 + ! ! user: test + ! ! date: Thu Jan 01 00:00:28 1970 +0000 + ! ! summary: (28) merge zero known + ! / + o ! changeset: 26:7f25b6c2f0b9 + !\ \ parent: 18:1aa84d96232a + ! ! ! parent: 25:91da8ed57247 + ! ! ! user: test + ! ! ! date: Thu Jan 01 00:00:26 1970 +0000 + ! ! ! summary: (26) merge one known; far right + ! ! ! + ! o ! changeset: 25:91da8ed57247 + ! !\! parent: 21:d42a756af44d + ! ! ! parent: 24:a9c19a3d96b7 + ! ! ! user: test + ! ! ! date: Thu Jan 01 00:00:25 1970 +0000 + ! ! ! summary: (25) merge one known; far left + ! ! ! + ! o ! changeset: 24:a9c19a3d96b7 + ! !\ \ parent: 0:e6eb3150255d + ! ! ~ ! parent: 23:a01cddf0766d + ! ! ! user: test + ! ! ! date: Thu Jan 01 00:00:24 1970 +0000 + ! ! ! summary: (24) merge one known; immediate right + ! ! / + ! o ! changeset: 23:a01cddf0766d + ! !\ \ parent: 1:6db2ef61d156 + ! ! ~ ! parent: 22:e0d9cccacb5d + ! ! ! user: test + ! ! ! date: Thu Jan 01 00:00:23 1970 +0000 + ! ! ! summary: (23) merge one known; immediate left + ! ! / + ! o ! changeset: 22:e0d9cccacb5d + !/!/ parent: 18:1aa84d96232a + ! ! parent: 21:d42a756af44d + ! ! user: test + ! ! date: Thu Jan 01 00:00:22 1970 +0000 + ! ! summary: (22) merge two known; one far left, one far right + ! ! + ! o changeset: 21:d42a756af44d + ! !\ parent: 19:31ddc2c1573b + ! ! ! parent: 20:d30ed6450e32 + ! ! ! user: test + ! ! ! date: Thu Jan 01 00:00:21 1970 +0000 + ! ! ! summary: (21) expand + ! ! ! + +---o changeset: 20:d30ed6450e32 + ! ! | parent: 0:e6eb3150255d + ! ! ~ parent: 18:1aa84d96232a + ! ! user: test + ! ! date: Thu Jan 01 00:00:20 1970 +0000 + ! ! summary: (20) merge two known; two far right + ! ! + ! o changeset: 19:31ddc2c1573b + ! |\ parent: 15:1dda3f72782d + ! ~ ~ parent: 17:44765d7c06e0 + ! user: test + ! date: Thu Jan 01 00:00:19 1970 +0000 + ! summary: (19) expand + ! + o changeset: 18:1aa84d96232a + |\ parent: 1:6db2ef61d156 + ~ ~ parent: 15:1dda3f72782d + user: test + date: Thu Jan 01 00:00:18 1970 +0000 + summary: (18) merge two known; two far left + $ cd .. Change graph shorten, test better with graphstyle.missing not none
--- a/tests/test-help.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-help.t Fri Jul 01 16:02:56 2016 -0500 @@ -260,7 +260,6 @@ extdiff command to allow external programs to compare revisions factotum http authentication with factotum gpg commands to sign and verify changesets - hgcia hooks for integrating with the CIA.vc notification service hgk browse the repository in a graphical way highlight syntax highlighting for hgweb (requires Pygments) histedit interactive history editing @@ -429,6 +428,22 @@ -h --help display help and exit --hidden consider hidden changesets +Test the textwidth config option + + $ hg root -h --config ui.textwidth=50 + hg root + + print the root (top) of the current working + directory + + Print the root directory of the current + repository. + + Returns 0 on success. + + (some details hidden, use --verbose to show + complete help) + Test help option with version option $ hg add -h --version @@ -1500,6 +1515,18 @@ The URL to use for push operations. If not defined, the location defined by the path's main entry is used. + "pushrev" + A revset defining which revisions to push by default. + + When 'hg push' is executed without a "-r" argument, the revset defined + by this sub-option is evaluated to determine what to push. + + For example, a value of "." will push the working directory's revision + by default. + + Revsets specifying bookmarks will not result in the bookmark being + pushed. + The following special named paths exist: "default"
--- a/tests/test-hgcia.t Fri Jul 01 15:12:33 2016 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -Test the CIA extension - - $ cat >> $HGRCPATH <<EOF - > [extensions] - > hgcia= - > - > [hooks] - > changegroup.cia = python:hgext.hgcia.hook - > - > [web] - > baseurl = http://hgserver/ - > - > [cia] - > user = testuser - > project = testproject - > test = True - > EOF - - $ hg init src - $ hg init cia - $ cd src - $ echo foo > foo - $ hg ci -Amfoo - adding foo - $ hg push ../cia - pushing to ../cia - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - - <message> - <generator> - <name>Mercurial (hgcia)</name> - <version>0.1</version> - <url>http://hg.kublai.com/mercurial/hgcia</url> - <user>testuser</user> - </generator> - <source> - <project>testproject</project> - <branch>default</branch> - </source> - <body> - <commit> - <author>test</author> - <version>0:e63c23eaa88a</version> - <log>foo</log> - <url>http://hgserver/rev/e63c23eaa88a</url> - <files><file uri="http://hgserver/file/e63c23eaa88a/foo" action="add">foo</file></files> - </commit> - </body> - <timestamp>0</timestamp> - </message> - - $ cat >> $HGRCPATH <<EOF - > strip = 0 - > EOF - - $ echo bar > bar - $ hg ci -Ambar - adding bar - $ hg push ../cia - pushing to ../cia - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - - <message> - <generator> - <name>Mercurial (hgcia)</name> - <version>0.1</version> - <url>http://hg.kublai.com/mercurial/hgcia</url> - <user>testuser</user> - </generator> - <source> - <project>testproject</project> - <branch>default</branch> - </source> - <body> - <commit> - <author>test</author> - <version>1:c0c7cf58edc5</version> - <log>bar</log> - <url>http://hgserver/$TESTTMP/cia/rev/c0c7cf58edc5</url> - <files><file uri="http://hgserver/$TESTTMP/cia/file/c0c7cf58edc5/bar" action="add">bar</file></files> - </commit> - </body> - <timestamp>0</timestamp> - </message> - - $ cd ..
--- a/tests/test-hgweb-auth.py Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-hgweb-auth.py Fri Jul 01 16:02:56 2016 -0500 @@ -43,7 +43,7 @@ def _test(uri): print('URI:', uri) try: - pm = url.passwordmgr(ui) + pm = url.passwordmgr(ui, urlreq.httppasswordmgrwithdefaultrealm()) u, authinfo = util.url(uri).authinfo() if authinfo is not None: pm.add_password(*authinfo)
--- a/tests/test-hgweb-commands.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-hgweb-commands.t Fri Jul 01 16:02:56 2016 -0500 @@ -81,36 +81,36 @@ <updated>1970-01-01T00:00:00+00:00</updated> <published>1970-01-01T00:00:00+00:00</published> <content type="xhtml"> - <table xmlns="http://www.w3.org/1999/xhtml"> - <tr> - <th style="text-align:left;">changeset</th> - <td>cad8025a2e87</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>unstable</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td>something</td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td>tip</td> - </tr> - <tr> - <th style="text-align:left;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>branch commit with null character: (websub)</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td></td> - </tr> - </table> + <table xmlns="http://www.w3.org/1999/xhtml"> + <tr> + <th style="text-align:left;">changeset</th> + <td>cad8025a2e87</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>unstable</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td>something</td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td>tip</td> + </tr> + <tr> + <th style="text-align:left;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>branch commit with null character: (websub)</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td></td> + </tr> + </table> </content> </entry> <entry> @@ -124,36 +124,36 @@ <updated>1970-01-01T00:00:00+00:00</updated> <published>1970-01-01T00:00:00+00:00</published> <content type="xhtml"> - <table xmlns="http://www.w3.org/1999/xhtml"> - <tr> - <th style="text-align:left;">changeset</th> - <td>1d22e65f027e</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>stable</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>branch(websub)</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>foo<br /></td> - </tr> - </table> + <table xmlns="http://www.w3.org/1999/xhtml"> + <tr> + <th style="text-align:left;">changeset</th> + <td>1d22e65f027e</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>stable</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>branch(websub)</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>foo<br /></td> + </tr> + </table> </content> </entry> <entry> @@ -167,36 +167,36 @@ <updated>1970-01-01T00:00:00+00:00</updated> <published>1970-01-01T00:00:00+00:00</published> <content type="xhtml"> - <table xmlns="http://www.w3.org/1999/xhtml"> - <tr> - <th style="text-align:left;">changeset</th> - <td>a4f92ed23982</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>default</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>Added tag 1.0 for changeset 2ef0ac749a14(websub)</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>.hgtags<br /></td> - </tr> - </table> + <table xmlns="http://www.w3.org/1999/xhtml"> + <tr> + <th style="text-align:left;">changeset</th> + <td>a4f92ed23982</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>default</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>Added tag 1.0 for changeset 2ef0ac749a14(websub)</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>.hgtags<br /></td> + </tr> + </table> </content> </entry> <entry> @@ -210,36 +210,36 @@ <updated>1970-01-01T00:00:00+00:00</updated> <published>1970-01-01T00:00:00+00:00</published> <content type="xhtml"> - <table xmlns="http://www.w3.org/1999/xhtml"> - <tr> - <th style="text-align:left;">changeset</th> - <td>2ef0ac749a14</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td>anotherthing</td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td>1.0</td> - </tr> - <tr> - <th style="text-align:left;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>base(websub)</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>da/foo<br />foo<br /></td> - </tr> - </table> + <table xmlns="http://www.w3.org/1999/xhtml"> + <tr> + <th style="text-align:left;">changeset</th> + <td>2ef0ac749a14</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td>anotherthing</td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td>1.0</td> + </tr> + <tr> + <th style="text-align:left;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>base(websub)</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>da/foo<br />foo<br /></td> + </tr> + </table> </content> </entry> @@ -258,160 +258,164 @@ <item> <title>[unstable] branch commit with null character: </title> <guid isPermaLink="true">http://*:$HGPORT/rev/cad8025a2e87</guid> (glob) - <link>http://*:$HGPORT/rev/cad8025a2e87</link> (glob) + <link>http://*:$HGPORT/rev/cad8025a2e87</link> (glob) <description> - <![CDATA[ - <table> - <tr> - <th style="text-align:left;">changeset</th> - <td>cad8025a2e87</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>unstable</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td>something</td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td>tip</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>branch commit with null character: (websub)</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td></td> - </tr> - </table> - ]]></description> + <![CDATA[ + <table> + <tr> + <th style="text-align:left;">changeset</th> + <td>cad8025a2e87</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>unstable</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td>something</td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td>tip</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>branch commit with null character: (websub)</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td></td> + </tr> + </table> + ]]> + </description> <author>test</author> <pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> </item> <item> <title>[stable] branch</title> <guid isPermaLink="true">http://*:$HGPORT/rev/1d22e65f027e</guid> (glob) - <link>http://*:$HGPORT/rev/1d22e65f027e</link> (glob) + <link>http://*:$HGPORT/rev/1d22e65f027e</link> (glob) <description> - <![CDATA[ - <table> - <tr> - <th style="text-align:left;">changeset</th> - <td>1d22e65f027e</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>stable</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>branch(websub)</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>foo<br /></td> - </tr> - </table> - ]]></description> + <![CDATA[ + <table> + <tr> + <th style="text-align:left;">changeset</th> + <td>1d22e65f027e</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>stable</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>branch(websub)</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>foo<br /></td> + </tr> + </table> + ]]> + </description> <author>test</author> <pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> </item> <item> <title>[default] Added tag 1.0 for changeset 2ef0ac749a14</title> <guid isPermaLink="true">http://*:$HGPORT/rev/a4f92ed23982</guid> (glob) - <link>http://*:$HGPORT/rev/a4f92ed23982</link> (glob) + <link>http://*:$HGPORT/rev/a4f92ed23982</link> (glob) <description> - <![CDATA[ - <table> - <tr> - <th style="text-align:left;">changeset</th> - <td>a4f92ed23982</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>default</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>Added tag 1.0 for changeset 2ef0ac749a14(websub)</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>.hgtags<br /></td> - </tr> - </table> - ]]></description> + <![CDATA[ + <table> + <tr> + <th style="text-align:left;">changeset</th> + <td>a4f92ed23982</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>default</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>Added tag 1.0 for changeset 2ef0ac749a14(websub)</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>.hgtags<br /></td> + </tr> + </table> + ]]> + </description> <author>test</author> <pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> </item> <item> <title>base</title> <guid isPermaLink="true">http://*:$HGPORT/rev/2ef0ac749a14</guid> (glob) - <link>http://*:$HGPORT/rev/2ef0ac749a14</link> (glob) + <link>http://*:$HGPORT/rev/2ef0ac749a14</link> (glob) <description> - <![CDATA[ - <table> - <tr> - <th style="text-align:left;">changeset</th> - <td>2ef0ac749a14</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td>anotherthing</td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td>1.0</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>base(websub)</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>da/foo<br />foo<br /></td> - </tr> - </table> - ]]></description> + <![CDATA[ + <table> + <tr> + <th style="text-align:left;">changeset</th> + <td>2ef0ac749a14</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td>anotherthing</td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td>1.0</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>base(websub)</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>da/foo<br />foo<br /></td> + </tr> + </table> + ]]> + </description> <author>test</author> <pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> </item> @@ -441,36 +445,36 @@ <updated>1970-01-01T00:00:00+00:00</updated> <published>1970-01-01T00:00:00+00:00</published> <content type="xhtml"> - <table xmlns="http://www.w3.org/1999/xhtml"> - <tr> - <th style="text-align:left;">changeset</th> - <td>a4f92ed23982</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>default</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>Added tag 1.0 for changeset 2ef0ac749a14(websub)</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>.hgtags<br /></td> - </tr> - </table> + <table xmlns="http://www.w3.org/1999/xhtml"> + <tr> + <th style="text-align:left;">changeset</th> + <td>a4f92ed23982</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>default</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>Added tag 1.0 for changeset 2ef0ac749a14(websub)</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>.hgtags<br /></td> + </tr> + </table> </content> </entry> <entry> @@ -484,36 +488,36 @@ <updated>1970-01-01T00:00:00+00:00</updated> <published>1970-01-01T00:00:00+00:00</published> <content type="xhtml"> - <table xmlns="http://www.w3.org/1999/xhtml"> - <tr> - <th style="text-align:left;">changeset</th> - <td>2ef0ac749a14</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td>anotherthing</td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td>1.0</td> - </tr> - <tr> - <th style="text-align:left;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>base(websub)</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>da/foo<br />foo<br /></td> - </tr> - </table> + <table xmlns="http://www.w3.org/1999/xhtml"> + <tr> + <th style="text-align:left;">changeset</th> + <td>2ef0ac749a14</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td>anotherthing</td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td>1.0</td> + </tr> + <tr> + <th style="text-align:left;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>base(websub)</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>da/foo<br />foo<br /></td> + </tr> + </table> </content> </entry> @@ -532,80 +536,82 @@ <item> <title>[default] Added tag 1.0 for changeset 2ef0ac749a14</title> <guid isPermaLink="true">http://*:$HGPORT/rev/a4f92ed23982</guid> (glob) - <link>http://*:$HGPORT/rev/a4f92ed23982</link> (glob) + <link>http://*:$HGPORT/rev/a4f92ed23982</link> (glob) <description> - <![CDATA[ - <table> - <tr> - <th style="text-align:left;">changeset</th> - <td>a4f92ed23982</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>default</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>Added tag 1.0 for changeset 2ef0ac749a14(websub)</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>.hgtags<br /></td> - </tr> - </table> - ]]></description> + <![CDATA[ + <table> + <tr> + <th style="text-align:left;">changeset</th> + <td>a4f92ed23982</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>default</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>Added tag 1.0 for changeset 2ef0ac749a14(websub)</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>.hgtags<br /></td> + </tr> + </table> + ]]> + </description> <author>test</author> <pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> </item> <item> <title>base</title> <guid isPermaLink="true">http://*:$HGPORT/rev/2ef0ac749a14</guid> (glob) - <link>http://*:$HGPORT/rev/2ef0ac749a14</link> (glob) + <link>http://*:$HGPORT/rev/2ef0ac749a14</link> (glob) <description> - <![CDATA[ - <table> - <tr> - <th style="text-align:left;">changeset</th> - <td>2ef0ac749a14</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td>anotherthing</td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td>1.0</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>base(websub)</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>da/foo<br />foo<br /></td> - </tr> - </table> - ]]></description> + <![CDATA[ + <table> + <tr> + <th style="text-align:left;">changeset</th> + <td>2ef0ac749a14</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td>anotherthing</td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td>1.0</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>base(websub)</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>da/foo<br />foo<br /></td> + </tr> + </table> + ]]> + </description> <author>test</author> <pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> </item> @@ -633,36 +639,36 @@ <updated>1970-01-01T00:00:00+00:00</updated> <published>1970-01-01T00:00:00+00:00</published> <content type="xhtml"> - <table xmlns="http://www.w3.org/1999/xhtml"> - <tr> - <th style="text-align:left;">changeset</th> - <td>2ef0ac749a14</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td>anotherthing</td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td>1.0</td> - </tr> - <tr> - <th style="text-align:left;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>base(websub)</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td></td> - </tr> - </table> + <table xmlns="http://www.w3.org/1999/xhtml"> + <tr> + <th style="text-align:left;">changeset</th> + <td>2ef0ac749a14</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td>anotherthing</td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td>1.0</td> + </tr> + <tr> + <th style="text-align:left;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>base(websub)</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td></td> + </tr> + </table> </content> </entry> @@ -1957,6 +1963,8 @@ .minusline { color: red; } .atline { color: purple; } .annotate { font-size: smaller; text-align: right; padding-right: 1em; } + tr.thisrev a { color:#999999; text-decoration: none; } + tr.thisrev pre { color:#009900; } .buttons a { background-color: #666; padding: 2pt;
--- a/tests/test-hgweb-filelog.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-hgweb-filelog.t Fri Jul 01 16:02:56 2016 -0500 @@ -822,36 +822,36 @@ <updated>1970-01-01T00:00:00+00:00</updated> <published>1970-01-01T00:00:00+00:00</published> <content type="xhtml"> - <table xmlns="http://www.w3.org/1999/xhtml"> - <tr> - <th style="text-align:left;">changeset</th> - <td>3f41bc784e7e</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>a-branch</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>second a</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td></td> - </tr> - </table> + <table xmlns="http://www.w3.org/1999/xhtml"> + <tr> + <th style="text-align:left;">changeset</th> + <td>3f41bc784e7e</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>a-branch</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>second a</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td></td> + </tr> + </table> </content> </entry> <entry> @@ -865,36 +865,36 @@ <updated>1970-01-01T00:00:00+00:00</updated> <published>1970-01-01T00:00:00+00:00</published> <content type="xhtml"> - <table xmlns="http://www.w3.org/1999/xhtml"> - <tr> - <th style="text-align:left;">changeset</th> - <td>5ed941583260</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td>a-bookmark</td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td>a-tag</td> - </tr> - <tr> - <th style="text-align:left;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>first a</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td></td> - </tr> - </table> + <table xmlns="http://www.w3.org/1999/xhtml"> + <tr> + <th style="text-align:left;">changeset</th> + <td>5ed941583260</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td>a-bookmark</td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td>a-tag</td> + </tr> + <tr> + <th style="text-align:left;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>first a</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td></td> + </tr> + </table> </content> </entry>
--- a/tests/test-hgweb-json.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-hgweb-json.t Fri Jul 01 16:02:56 2016 -0500 @@ -100,6 +100,8 @@ summary: initial + $ echo '[web]' >> .hg/hgrc + $ echo 'allow_archive = bz2' >> .hg/hgrc $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E error.log $ cat hg.pid >> $DAEMON_PIDS @@ -111,10 +113,33 @@ file/{revision}/{path} shows file revision - $ request json-file/06e557f3edf6/foo + $ request json-file/78896eb0e102/foo-new 200 Script output follows - "not yet implemented" + { + "bookmarks": [], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "move foo", + "lines": [ + { + "line": "bar\n" + } + ], + "node": "78896eb0e102174ce9278438a95e12543e4367a7", + "parents": [ + "f8bbb9024b10f93cdbb8d940337398291d40dea8" + ], + "path": "foo-new", + "phase": "public", + "tags": [ + "tag1" + ], + "user": "test" + } file/{revision} shows root directory info @@ -169,6 +194,7 @@ "changesets": [ { "bookmarks": [], + "branch": "default", "date": [ 0.0, 0 @@ -179,6 +205,7 @@ "ceed296fe500c3fac9541e31dad860cb49c89e45", "ed66c30e87eb65337c05a4229efaa5f1d5285a90" ], + "phase": "draft", "tags": [ "tip" ], @@ -186,6 +213,7 @@ }, { "bookmarks": [], + "branch": "test-branch", "date": [ 0.0, 0 @@ -195,11 +223,13 @@ "parents": [ "6ab967a8ab3489227a83f80e920faa039a71819f" ], + "phase": "draft", "tags": [], "user": "test" }, { "bookmarks": [], + "branch": "test-branch", "date": [ 0.0, 0 @@ -209,6 +239,7 @@ "parents": [ "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e" ], + "phase": "draft", "tags": [], "user": "test" }, @@ -216,6 +247,7 @@ "bookmarks": [ "bookmark2" ], + "branch": "default", "date": [ 0.0, 0 @@ -225,11 +257,13 @@ "parents": [ "f2890a05fea49bfaf9fb27ed5490894eba32da78" ], + "phase": "draft", "tags": [], "user": "test" }, { "bookmarks": [], + "branch": "default", "date": [ 0.0, 0 @@ -239,6 +273,7 @@ "parents": [ "93a8ce14f89156426b7fa981af8042da53f03aa0" ], + "phase": "draft", "tags": [ "tag2" ], @@ -246,6 +281,7 @@ }, { "bookmarks": [], + "branch": "default", "date": [ 0.0, 0 @@ -255,11 +291,13 @@ "parents": [ "78896eb0e102174ce9278438a95e12543e4367a7" ], + "phase": "public", "tags": [], "user": "test" }, { "bookmarks": [], + "branch": "default", "date": [ 0.0, 0 @@ -269,6 +307,7 @@ "parents": [ "8d7c456572acf3557e8ed8a07286b10c408bcec5" ], + "phase": "public", "tags": [ "tag1" ], @@ -278,6 +317,7 @@ "bookmarks": [ "bookmark1" ], + "branch": "default", "date": [ 0.0, 0 @@ -287,11 +327,13 @@ "parents": [ "f8bbb9024b10f93cdbb8d940337398291d40dea8" ], + "phase": "public", "tags": [], "user": "test" }, { "bookmarks": [], + "branch": "default", "date": [ 0.0, 0 @@ -301,11 +343,13 @@ "parents": [ "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e" ], + "phase": "public", "tags": [], "user": "test" }, { "bookmarks": [], + "branch": "default", "date": [ 0.0, 0 @@ -313,6 +357,7 @@ "desc": "initial", "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e", "parents": [], + "phase": "public", "tags": [], "user": "test" } @@ -330,6 +375,7 @@ "changesets": [ { "bookmarks": [], + "branch": "default", "date": [ 0.0, 0 @@ -339,11 +385,13 @@ "parents": [ "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e" ], + "phase": "public", "tags": [], "user": "test" }, { "bookmarks": [], + "branch": "default", "date": [ 0.0, 0 @@ -351,6 +399,7 @@ "desc": "initial", "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e", "parents": [], + "phase": "public", "tags": [], "user": "test" } @@ -368,6 +417,7 @@ "changesets": [ { "bookmarks": [], + "branch": "default", "date": [ 0.0, 0 @@ -378,6 +428,7 @@ "ceed296fe500c3fac9541e31dad860cb49c89e45", "ed66c30e87eb65337c05a4229efaa5f1d5285a90" ], + "phase": "draft", "tags": [ "tip" ], @@ -385,6 +436,7 @@ }, { "bookmarks": [], + "branch": "test-branch", "date": [ 0.0, 0 @@ -394,11 +446,13 @@ "parents": [ "6ab967a8ab3489227a83f80e920faa039a71819f" ], + "phase": "draft", "tags": [], "user": "test" }, { "bookmarks": [], + "branch": "test-branch", "date": [ 0.0, 0 @@ -408,6 +462,7 @@ "parents": [ "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e" ], + "phase": "draft", "tags": [], "user": "test" }, @@ -415,6 +470,7 @@ "bookmarks": [ "bookmark2" ], + "branch": "default", "date": [ 0.0, 0 @@ -424,11 +480,13 @@ "parents": [ "f2890a05fea49bfaf9fb27ed5490894eba32da78" ], + "phase": "draft", "tags": [], "user": "test" }, { "bookmarks": [], + "branch": "default", "date": [ 0.0, 0 @@ -438,6 +496,7 @@ "parents": [ "93a8ce14f89156426b7fa981af8042da53f03aa0" ], + "phase": "draft", "tags": [ "tag2" ], @@ -445,6 +504,7 @@ }, { "bookmarks": [], + "branch": "default", "date": [ 0.0, 0 @@ -454,11 +514,13 @@ "parents": [ "78896eb0e102174ce9278438a95e12543e4367a7" ], + "phase": "public", "tags": [], "user": "test" }, { "bookmarks": [], + "branch": "default", "date": [ 0.0, 0 @@ -468,6 +530,7 @@ "parents": [ "8d7c456572acf3557e8ed8a07286b10c408bcec5" ], + "phase": "public", "tags": [ "tag1" ], @@ -477,6 +540,7 @@ "bookmarks": [ "bookmark1" ], + "branch": "default", "date": [ 0.0, 0 @@ -486,11 +550,13 @@ "parents": [ "f8bbb9024b10f93cdbb8d940337398291d40dea8" ], + "phase": "public", "tags": [], "user": "test" }, { "bookmarks": [], + "branch": "default", "date": [ 0.0, 0 @@ -500,11 +566,13 @@ "parents": [ "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e" ], + "phase": "public", "tags": [], "user": "test" }, { "bookmarks": [], + "branch": "default", "date": [ 0.0, 0 @@ -512,6 +580,7 @@ "desc": "initial", "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e", "parents": [], + "phase": "public", "tags": [], "user": "test" } @@ -732,7 +801,308 @@ $ request json-summary 200 Script output follows - "not yet implemented" + { + "archives": [ + { + "extension": ".tar.bz2", + "node": "tip", + "type": "bz2", + "url": "http://*:$HGPORT/archive/tip.tar.bz2" (glob) + } + ], + "bookmarks": [ + { + "bookmark": "bookmark2", + "date": [ + 0.0, + 0 + ], + "node": "ceed296fe500c3fac9541e31dad860cb49c89e45" + }, + { + "bookmark": "bookmark1", + "date": [ + 0.0, + 0 + ], + "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5" + } + ], + "branches": [ + { + "branch": "default", + "date": [ + 0.0, + 0 + ], + "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7", + "status": "open" + }, + { + "branch": "test-branch", + "date": [ + 0.0, + 0 + ], + "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90", + "status": "inactive" + } + ], + "lastchange": [ + 0.0, + 0 + ], + "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7", + "shortlog": [ + { + "bookmarks": [], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "merge test-branch into default", + "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7", + "parents": [ + "ceed296fe500c3fac9541e31dad860cb49c89e45", + "ed66c30e87eb65337c05a4229efaa5f1d5285a90" + ], + "phase": "draft", + "tags": [ + "tip" + ], + "user": "test" + }, + { + "bookmarks": [], + "branch": "test-branch", + "date": [ + 0.0, + 0 + ], + "desc": "another commit in test-branch", + "node": "ed66c30e87eb65337c05a4229efaa5f1d5285a90", + "parents": [ + "6ab967a8ab3489227a83f80e920faa039a71819f" + ], + "phase": "draft", + "tags": [], + "user": "test" + }, + { + "bookmarks": [], + "branch": "test-branch", + "date": [ + 0.0, + 0 + ], + "desc": "create test branch", + "node": "6ab967a8ab3489227a83f80e920faa039a71819f", + "parents": [ + "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e" + ], + "phase": "draft", + "tags": [], + "user": "test" + }, + { + "bookmarks": [ + "bookmark2" + ], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "create tag2", + "node": "ceed296fe500c3fac9541e31dad860cb49c89e45", + "parents": [ + "f2890a05fea49bfaf9fb27ed5490894eba32da78" + ], + "phase": "draft", + "tags": [], + "user": "test" + }, + { + "bookmarks": [], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "another commit to da/foo", + "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78", + "parents": [ + "93a8ce14f89156426b7fa981af8042da53f03aa0" + ], + "phase": "draft", + "tags": [ + "tag2" + ], + "user": "test" + }, + { + "bookmarks": [], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "create tag", + "node": "93a8ce14f89156426b7fa981af8042da53f03aa0", + "parents": [ + "78896eb0e102174ce9278438a95e12543e4367a7" + ], + "phase": "public", + "tags": [], + "user": "test" + }, + { + "bookmarks": [], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "move foo", + "node": "78896eb0e102174ce9278438a95e12543e4367a7", + "parents": [ + "8d7c456572acf3557e8ed8a07286b10c408bcec5" + ], + "phase": "public", + "tags": [ + "tag1" + ], + "user": "test" + }, + { + "bookmarks": [ + "bookmark1" + ], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "modify da/foo", + "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5", + "parents": [ + "f8bbb9024b10f93cdbb8d940337398291d40dea8" + ], + "phase": "public", + "tags": [], + "user": "test" + }, + { + "bookmarks": [], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "modify foo", + "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8", + "parents": [ + "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e" + ], + "phase": "public", + "tags": [], + "user": "test" + }, + { + "bookmarks": [], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "initial", + "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e", + "parents": [], + "phase": "public", + "tags": [], + "user": "test" + } + ], + "tags": [ + { + "date": [ + 0.0, + 0 + ], + "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78", + "tag": "tag2" + }, + { + "date": [ + 0.0, + 0 + ], + "node": "78896eb0e102174ce9278438a95e12543e4367a7", + "tag": "tag1" + } + ] + } + + $ request json-changelog?rev=create + 200 Script output follows + + { + "entries": [ + { + "bookmarks": [], + "branch": "test-branch", + "date": [ + 0.0, + 0 + ], + "desc": "create test branch", + "node": "6ab967a8ab3489227a83f80e920faa039a71819f", + "parents": [ + "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e" + ], + "phase": "draft", + "tags": [], + "user": "test" + }, + { + "bookmarks": [ + "bookmark2" + ], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "create tag2", + "node": "ceed296fe500c3fac9541e31dad860cb49c89e45", + "parents": [ + "f2890a05fea49bfaf9fb27ed5490894eba32da78" + ], + "phase": "draft", + "tags": [], + "user": "test" + }, + { + "bookmarks": [], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "create tag", + "node": "93a8ce14f89156426b7fa981af8042da53f03aa0", + "parents": [ + "78896eb0e102174ce9278438a95e12543e4367a7" + ], + "phase": "public", + "tags": [], + "user": "test" + } + ], + "node": "cc725e08502a79dd1eda913760fbe06ed7a9abc7", + "query": "create" + } filediff/{revision}/{path} shows changes to a file in a revision @@ -862,7 +1232,98 @@ $ request json-filelog/f8bbb9024b10/foo 200 Script output follows - "not yet implemented" + { + "entries": [ + { + "bookmarks": [], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "modify foo", + "node": "f8bbb9024b10f93cdbb8d940337398291d40dea8", + "parents": [ + "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e" + ], + "phase": "public", + "tags": [], + "user": "test" + }, + { + "bookmarks": [], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "initial", + "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e", + "parents": [], + "phase": "public", + "tags": [], + "user": "test" + } + ] + } + + $ request json-filelog/cc725e08502a/da/foo + 200 Script output follows + + { + "entries": [ + { + "bookmarks": [], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "another commit to da/foo", + "node": "f2890a05fea49bfaf9fb27ed5490894eba32da78", + "parents": [ + "8d7c456572acf3557e8ed8a07286b10c408bcec5" + ], + "phase": "draft", + "tags": [ + "tag2" + ], + "user": "test" + }, + { + "bookmarks": [ + "bookmark1" + ], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "modify da/foo", + "node": "8d7c456572acf3557e8ed8a07286b10c408bcec5", + "parents": [ + "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e" + ], + "phase": "public", + "tags": [], + "user": "test" + }, + { + "bookmarks": [], + "branch": "default", + "date": [ + 0.0, + 0 + ], + "desc": "initial", + "node": "06e557f3edf66faa1ccaba5dd8c203c21cc79f1e", + "parents": [], + "phase": "public", + "tags": [], + "user": "test" + } + ] + } (archive/ doesn't use templating, so ignore it)
--- a/tests/test-hgweb-no-path-info.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-hgweb-no-path-info.t Fri Jul 01 16:02:56 2016 -0500 @@ -96,36 +96,36 @@ <updated>1970-01-01T00:00:00+00:00</updated> <published>1970-01-01T00:00:00+00:00</published> <content type="xhtml"> - <table xmlns="http://www.w3.org/1999/xhtml"> - <tr> - <th style="text-align:left;">changeset</th> - <td>61c9426e69fe</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>default</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td>tip</td> - </tr> - <tr> - <th style="text-align:left;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>bar<br /></td> - </tr> - </table> + <table xmlns="http://www.w3.org/1999/xhtml"> + <tr> + <th style="text-align:left;">changeset</th> + <td>61c9426e69fe</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>default</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td>tip</td> + </tr> + <tr> + <th style="text-align:left;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>bar<br /></td> + </tr> + </table> </content> </entry>
--- a/tests/test-hgweb-no-request-uri.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-hgweb-no-request-uri.t Fri Jul 01 16:02:56 2016 -0500 @@ -107,36 +107,36 @@ <updated>1970-01-01T00:00:00+00:00</updated> <published>1970-01-01T00:00:00+00:00</published> <content type="xhtml"> - <table xmlns="http://www.w3.org/1999/xhtml"> - <tr> - <th style="text-align:left;">changeset</th> - <td>61c9426e69fe</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>default</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td>tip</td> - </tr> - <tr> - <th style="text-align:left;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>bar<br /></td> - </tr> - </table> + <table xmlns="http://www.w3.org/1999/xhtml"> + <tr> + <th style="text-align:left;">changeset</th> + <td>61c9426e69fe</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>default</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td>tip</td> + </tr> + <tr> + <th style="text-align:left;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>bar<br /></td> + </tr> + </table> </content> </entry>
--- a/tests/test-hgweb.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-hgweb.t Fri Jul 01 16:02:56 2016 -0500 @@ -340,7 +340,7 @@ $ get-with-headers.py --twice localhost:$HGPORT 'static/style-gitweb.css' - date etag server 200 Script output follows - content-length: 6521 + content-length: 6610 content-type: text/css body { font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; margin:10px; background: white; color: black; } @@ -397,6 +397,8 @@ div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; } div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; } div.search { margin:4px 8px; position:absolute; top:56px; right:12px } + tr.thisrev a { color:#999999; text-decoration: none; } + tr.thisrev pre { color:#009900; } .linenr { color:#999999; text-decoration:none } div.rss_logo { float: right; white-space: nowrap; } div.rss_logo a {
--- a/tests/test-hgwebdir.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-hgwebdir.t Fri Jul 01 16:02:56 2016 -0500 @@ -783,36 +783,36 @@ <updated>1970-01-01T00:00:01+00:00</updated> <published>1970-01-01T00:00:01+00:00</published> <content type="xhtml"> - <table xmlns="http://www.w3.org/1999/xhtml"> - <tr> - <th style="text-align:left;">changeset</th> - <td>8580ff50825a</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>default</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td>tip</td> - </tr> - <tr> - <th style="text-align:left;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>a</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>a<br /></td> - </tr> - </table> + <table xmlns="http://www.w3.org/1999/xhtml"> + <tr> + <th style="text-align:left;">changeset</th> + <td>8580ff50825a</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>default</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td>tip</td> + </tr> + <tr> + <th style="text-align:left;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>a</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>a<br /></td> + </tr> + </table> </content> </entry> @@ -840,36 +840,36 @@ <updated>1970-01-01T00:00:01+00:00</updated> <published>1970-01-01T00:00:01+00:00</published> <content type="xhtml"> - <table xmlns="http://www.w3.org/1999/xhtml"> - <tr> - <th style="text-align:left;">changeset</th> - <td>8580ff50825a</td> - </tr> - <tr> - <th style="text-align:left;">branch</th> - <td>default</td> - </tr> - <tr> - <th style="text-align:left;">bookmark</th> - <td></td> - </tr> - <tr> - <th style="text-align:left;">tag</th> - <td>tip</td> - </tr> - <tr> - <th style="text-align:left;">user</th> - <td>test</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">description</th> - <td>a</td> - </tr> - <tr> - <th style="text-align:left;vertical-align:top;">files</th> - <td>a<br /></td> - </tr> - </table> + <table xmlns="http://www.w3.org/1999/xhtml"> + <tr> + <th style="text-align:left;">changeset</th> + <td>8580ff50825a</td> + </tr> + <tr> + <th style="text-align:left;">branch</th> + <td>default</td> + </tr> + <tr> + <th style="text-align:left;">bookmark</th> + <td></td> + </tr> + <tr> + <th style="text-align:left;">tag</th> + <td>tip</td> + </tr> + <tr> + <th style="text-align:left;">user</th> + <td>test</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">description</th> + <td>a</td> + </tr> + <tr> + <th style="text-align:left;vertical-align:top;">files</th> + <td>a<br /></td> + </tr> + </table> </content> </entry>
--- a/tests/test-highlight.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-highlight.t Fri Jul 01 16:02:56 2016 -0500 @@ -288,234 +288,202 @@ </thead> <tbody class="stripes2"> - <tr id="l1"> + <tr id="l1" class="thisrev"> <td class="annotate"> <a href="/annotate/06824edf55d0/primes.py#l1" title="06824edf55d0: a">test@0</a> </td> <td class="source"><a href="#l1"> 1</a> <span class="c">#!/usr/bin/env python</span></td> </tr> - <tr id="l2"> + <tr id="l2" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l2" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l2"> 2</a> </td> </tr> - <tr id="l3"> + <tr id="l3" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l3" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l3"> 3</a> <span class="sd">"""Fun with generators. Corresponding Haskell implementation:</span></td> </tr> - <tr id="l4"> + <tr id="l4" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l4" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l4"> 4</a> </td> </tr> - <tr id="l5"> + <tr id="l5" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l5" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l5"> 5</a> <span class="sd">primes = 2 : sieve [3, 5..]</span></td> </tr> - <tr id="l6"> + <tr id="l6" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l6" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l6"> 6</a> <span class="sd"> where sieve (p:ns) = p : sieve [n | n <- ns, mod n p /= 0]</span></td> </tr> - <tr id="l7"> + <tr id="l7" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l7" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l7"> 7</a> <span class="sd">"""</span></td> </tr> - <tr id="l8"> + <tr id="l8" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l8" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l8"> 8</a> </td> </tr> - <tr id="l9"> + <tr id="l9" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l9" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l9"> 9</a> <span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">dropwhile</span><span class="p">,</span> <span class="n">ifilter</span><span class="p">,</span> <span class="n">islice</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">chain</span></td> </tr> - <tr id="l10"> + <tr id="l10" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l10" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l10"> 10</a> </td> </tr> - <tr id="l11"> + <tr id="l11" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l11" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l11"> 11</a> <span class="kn">def</span> <span class="nf">primes</span><span class="p">():</span></td> </tr> - <tr id="l12"> + <tr id="l12" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l12" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l12"> 12</a> <span class="sd">"""Generate all primes."""</span></td> </tr> - <tr id="l13"> + <tr id="l13" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l13" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l13"> 13</a> <span class="kn">def</span> <span class="nf">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td> </tr> - <tr id="l14"> + <tr id="l14" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l14" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l14"> 14</a> <span class="n">p</span> <span class="o">=</span> <span class="n">ns</span><span class="o">.</span><span class="n">next</span><span class="p">()</span></td> </tr> - <tr id="l15"> + <tr id="l15" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l15" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l15"> 15</a> <span class="c"># It is important to yield *here* in order to stop the</span></td> </tr> - <tr id="l16"> + <tr id="l16" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l16" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l16"> 16</a> <span class="c"># infinite recursion.</span></td> </tr> - <tr id="l17"> + <tr id="l17" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l17" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l17"> 17</a> <span class="kn">yield</span> <span class="n">p</span></td> </tr> - <tr id="l18"> + <tr id="l18" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l18" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l18"> 18</a> <span class="n">ns</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o">%</span> <span class="n">p</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ns</span><span class="p">)</span></td> </tr> - <tr id="l19"> + <tr id="l19" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l19" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l19"> 19</a> <span class="kn">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">sieve</span><span class="p">(</span><span class="n">ns</span><span class="p">):</span></td> </tr> - <tr id="l20"> + <tr id="l20" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l20" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l20"> 20</a> <span class="kn">yield</span> <span class="n">n</span></td> </tr> - <tr id="l21"> + <tr id="l21" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l21" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l21"> 21</a> </td> </tr> - <tr id="l22"> + <tr id="l22" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l22" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l22"> 22</a> <span class="n">odds</span> <span class="o">=</span> <span class="n">ifilter</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">i</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">,</span> <span class="n">count</span><span class="p">())</span></td> </tr> - <tr id="l23"> + <tr id="l23" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l23" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l23"> 23</a> <span class="kn">return</span> <span class="n">chain</span><span class="p">([</span><span class="mi">2</span><span class="p">],</span> <span class="n">sieve</span><span class="p">(</span><span class="n">dropwhile</span><span class="p">(</span><span class="kn">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="o"><</span> <span class="mi">3</span><span class="p">,</span> <span class="n">odds</span><span class="p">)))</span></td> </tr> - <tr id="l24"> + <tr id="l24" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l24" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l24"> 24</a> </td> </tr> - <tr id="l25"> + <tr id="l25" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l25" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l25"> 25</a> <span class="kn">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span></td> </tr> - <tr id="l26"> + <tr id="l26" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l26" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l26"> 26</a> <span class="kn">import</span> <span class="nn">sys</span></td> </tr> - <tr id="l27"> + <tr id="l27" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l27" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l27"> 27</a> <span class="kn">try</span><span class="p">:</span></td> </tr> - <tr id="l28"> + <tr id="l28" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l28" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l28"> 28</a> <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span></td> </tr> - <tr id="l29"> + <tr id="l29" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l29" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l29"> 29</a> <span class="kn">except</span> <span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="ne">IndexError</span><span class="p">):</span></td> </tr> - <tr id="l30"> + <tr id="l30" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l30" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l30"> 30</a> <span class="n">n</span> <span class="o">=</span> <span class="mi">10</span></td> </tr> - <tr id="l31"> + <tr id="l31" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l31" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l31"> 31</a> <span class="n">p</span> <span class="o">=</span> <span class="n">primes</span><span class="p">()</span></td> </tr> - <tr id="l32"> + <tr id="l32" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l32" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l32"> 32</a> <span class="kn">print</span> <span class="s">"The first </span><span class="si">%d</span><span class="s"> primes: </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="nb">list</span><span class="p">(</span><span class="n">islice</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">n</span><span class="p">)))</span></td> </tr> - <tr id="l33"> + <tr id="l33" class="thisrev"> <td class="annotate"> - <a href="/annotate/06824edf55d0/primes.py#l33" - title="06824edf55d0: a">test@0</a> + </td> <td class="source"><a href="#l33"> 33</a> </td> </tr>
--- a/tests/test-histedit-arguments.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-histedit-arguments.t Fri Jul 01 16:02:56 2016 -0500 @@ -499,3 +499,53 @@ > EOF $ hg commit --amend -m 'allow this fold' $ hg histedit --continue + + $ cd .. + +Test autoverb feature + + $ hg init autoverb + $ cd autoverb + $ echo alpha >> alpha + $ hg addr + adding alpha + $ hg ci -m one + $ echo alpha >> alpha + $ hg ci -m two + $ echo alpha >> alpha + $ hg ci -m "roll! three" + + $ hg log --style compact --graph + @ 2[tip] 1b0b0b04c8fe 1970-01-01 00:00 +0000 test + | roll! three + | + o 1 579e40513370 1970-01-01 00:00 +0000 test + | two + | + o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test + one + + +Check that 'roll' is selected by default + + $ HGEDITOR=cat hg histedit 1 --config experimental.histedit.autoverb=True + pick 579e40513370 1 two + roll 1b0b0b04c8fe 2 roll! three + + # Edit history between 579e40513370 and 1b0b0b04c8fe + # + # Commits are listed from least to most recent + # + # You can reorder changesets by reordering the lines + # + # Commands: + # + # e, edit = use commit, but stop for amending + # m, mess = edit commit message without changing commit content + # p, pick = use commit + # d, drop = remove commit from history + # f, fold = use commit, but combine it with the one above + # r, roll = like fold, but discard this commit's description + # + + $ cd ..
--- a/tests/test-histedit-base.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-histedit-base.t Fri Jul 01 16:02:56 2016 -0500 @@ -39,7 +39,10 @@ |/ o 0:cd010b8cd998f3981a5a8115f94f8da4ab506089:draft 'A' +Verify that implicit base command and help are listed + $ HGEDITOR=cat hg histedit |grep base + # b, base = checkout changeset and apply further changesets from there Go to D $ hg update 3
--- a/tests/test-https.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-https.t Fri Jul 01 16:02:56 2016 -0500 @@ -2,131 +2,13 @@ Proper https client requires the built-in ssl from Python 2.6. -Certificates created with: - printf '.\n.\n.\n.\n.\nlocalhost\nhg@localhost\n' | \ - openssl req -newkey rsa:512 -keyout priv.pem -nodes -x509 -days 9000 -out pub.pem -Can be dumped with: - openssl x509 -in pub.pem -text - - $ cat << EOT > priv.pem - > -----BEGIN PRIVATE KEY----- - > MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEApjCWeYGrIa/Vo7LH - > aRF8ou0tbgHKE33Use/whCnKEUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8 - > j/xgSwIDAQABAkBxHC6+Qlf0VJXGlb6NL16yEVVTQxqDS6hA9zqu6TZjrr0YMfzc - > EGNIiZGt7HCBL0zO+cPDg/LeCZc6HQhf0KrhAiEAzlJq4hWWzvguWFIJWSoBeBUG - > MF1ACazQO7PYE8M0qfECIQDONHHP0SKZzz/ZwBZcAveC5K61f/v9hONFwbeYulzR - > +wIgc9SvbtgB/5Yzpp//4ZAEnR7oh5SClCvyB+KSx52K3nECICbhQphhoXmI10wy - > aMTellaq0bpNMHFDziqH9RsqAHhjAiEAgYGxfzkftt5IUUn/iFK89aaIpyrpuaAh - > HY8gUVkVRVs= - > -----END PRIVATE KEY----- - > EOT - - $ cat << EOT > pub.pem - > -----BEGIN CERTIFICATE----- - > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNV - > BAMMCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEw - > MTAxNDIwMzAxNFoXDTM1MDYwNTIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0 - > MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANL - > ADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnKEUm34rDaXQd4lxxX - > 6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA+amm - > r24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQw - > DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAFArvQFiAZJgQczRsbYlG1xl - > t+truk37w5B3m3Ick1ntRcQrqs+hf0CO1q6Squ144geYaQ8CDirSR92fICELI1c= - > -----END CERTIFICATE----- - > EOT - $ cat priv.pem pub.pem >> server.pem - $ PRIV=`pwd`/server.pem - - $ cat << EOT > pub-other.pem - > -----BEGIN CERTIFICATE----- - > MIIBqzCCAVWgAwIBAgIJALwZS731c/ORMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNV - > BAMMCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEw - > MTAxNDIwNDUxNloXDTM1MDYwNTIwNDUxNlowMTESMBAGA1UEAwwJbG9jYWxob3N0 - > MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANL - > ADBIAkEAsxsapLbHrqqUKuQBxdpK4G3m2LjtyrTSdpzzzFlecxd5yhNP6AyWrufo - > K4VMGo2xlu9xOo88nDSUNSKPuD09MwIDAQABo1AwTjAdBgNVHQ4EFgQUoIB1iMhN - > y868rpQ2qk9dHnU6ebswHwYDVR0jBBgwFoAUoIB1iMhNy868rpQ2qk9dHnU6ebsw - > DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJ544f125CsE7J2t55PdFaF6 - > bBlNBb91FCywBgSjhBjf+GG3TNPwrPdc3yqeq+hzJiuInqbOBv9abmMyq8Wsoig= - > -----END CERTIFICATE----- - > EOT - -pub.pem patched with other notBefore / notAfter: +Make server certificates: - $ cat << EOT > pub-not-yet.pem - > -----BEGIN CERTIFICATE----- - > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNVBAMMCWxvY2Fs - > aG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTM1MDYwNTIwMzAxNFoXDTM1MDYw - > NTIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhv - > c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnK - > EUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA - > +ammr24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQwDAYDVR0T - > BAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJXV41gWnkgC7jcpPpFRSUSZaxyzrXmD1CIqQf0WgVDb - > /12E0vR2DuZitgzUYtBaofM81aTtc0a2/YsrmqePGm0= - > -----END CERTIFICATE----- - > EOT - $ cat priv.pem pub-not-yet.pem > server-not-yet.pem - - $ cat << EOT > pub-expired.pem - > -----BEGIN CERTIFICATE----- - > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNVBAMMCWxvY2Fs - > aG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEwMTAxNDIwMzAxNFoXDTEwMTAx - > NDIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhv - > c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnK - > EUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA - > +ammr24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQwDAYDVR0T - > BAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJfk57DTRf2nUbYaMSlVAARxMNbFGOjQhAUtY400GhKt - > 2uiKCNGKXVXD3AHWe13yHc5KttzbHQStE5Nm/DlWBWQ= - > -----END CERTIFICATE----- - > EOT - $ cat priv.pem pub-expired.pem > server-expired.pem - -Client certificates created with: - openssl genrsa -aes128 -passout pass:1234 -out client-key.pem 512 - openssl rsa -in client-key.pem -passin pass:1234 -out client-key-decrypted.pem - printf '.\n.\n.\n.\n.\n.\nhg-client@localhost\n.\n.\n' | \ - openssl req -new -key client-key.pem -passin pass:1234 -out client-csr.pem - openssl x509 -req -days 9000 -in client-csr.pem -CA pub.pem -CAkey priv.pem \ - -set_serial 01 -out client-cert.pem - - $ cat << EOT > client-key.pem - > -----BEGIN RSA PRIVATE KEY----- - > Proc-Type: 4,ENCRYPTED - > DEK-Info: AES-128-CBC,C8B8F103A61A336FB0716D1C0F8BB2E8 - > - > JolMlCFjEW3q3JJjO9z99NJWeJbFgF5DpUOkfSCxH56hxxtZb9x++rBvBZkxX1bF - > BAIe+iI90+jdCLwxbILWuFcrJUaLC5WmO14XDKYVmr2eW9e4MiCYOlO0Q6a9rDFS - > jctRCfvubOXFHbBGLH8uKEMpXEkP7Lc60FiIukqjuQEivJjrQirVtZCGwyk3qUi7 - > Eyh4Lo63IKGu8T1Bkmn2kaMvFhu7nC/CQLBjSq0YYI1tmCOkVb/3tPrz8oqgDJp2 - > u7bLS3q0xDNZ52nVrKIoZC/UlRXGlPyzPpa70/jPIdfCbkwDaBpRVXc+62Pj2n5/ - > CnO2xaKwfOG6pDvanBhFD72vuBOkAYlFZPiEku4sc2WlNggsSWCPCIFwzmiHjKIl - > bWmdoTq3nb7sNfnBbV0OCa7fS1dFwCm4R1NC7ELENu0= - > -----END RSA PRIVATE KEY----- - > EOT - - $ cat << EOT > client-key-decrypted.pem - > -----BEGIN RSA PRIVATE KEY----- - > MIIBOgIBAAJBAJs4LS3glAYU92bg5kPgRPNW84ewB0fWJfAKccCp1ACHAdZPeaKb - > FCinVMYKAVbVqBkyrZ/Tyr8aSfMz4xO4+KsCAwEAAQJAeKDr25+Q6jkZHEbkLRP6 - > AfMtR+Ixhk6TJT24sbZKIC2V8KuJTDEvUhLU0CAr1nH79bDqiSsecOiVCr2HHyfT - > AQIhAM2C5rHbTs9R3PkywFEqq1gU3ztCnpiWglO7/cIkuGBhAiEAwVpMSAf77kop - > 4h/1kWsgMALQTJNsXd4CEUK4BOxvJIsCIQCbarVAKBQvoT81jfX27AfscsxnKnh5 - > +MjSvkanvdFZwQIgbbcTefwt1LV4trtz2SR0i0nNcOZmo40Kl0jIquKO3qkCIH01 - > mJHzZr3+jQqeIFtr5P+Xqi30DJxgrnEobbJ0KFjY - > -----END RSA PRIVATE KEY----- - > EOT - - $ cat << EOT > client-cert.pem - > -----BEGIN CERTIFICATE----- - > MIIBPjCB6QIBATANBgkqhkiG9w0BAQsFADAxMRIwEAYDVQQDDAlsb2NhbGhvc3Qx - > GzAZBgkqhkiG9w0BCQEWDGhnQGxvY2FsaG9zdDAeFw0xNTA1MDcwNjI5NDVaFw0z - > OTEyMjcwNjI5NDVaMCQxIjAgBgkqhkiG9w0BCQEWE2hnLWNsaWVudEBsb2NhbGhv - > c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAmzgtLeCUBhT3ZuDmQ+BE81bzh7AH - > R9Yl8ApxwKnUAIcB1k95opsUKKdUxgoBVtWoGTKtn9PKvxpJ8zPjE7j4qwIDAQAB - > MA0GCSqGSIb3DQEBCwUAA0EAfBTqBG5pYhuGk+ZnyUufgS+d7Nk/sZAZjNdCAEj/ - > NFPo5fR1jM6jlEWoWbeg298+SkjV7tfO+2nt0otUFkdM6A== - > -----END CERTIFICATE----- - > EOT + $ CERTSDIR="$TESTDIR/sslcerts" + $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub.pem" >> server.pem + $ PRIV=`pwd`/server.pem + $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-not-yet.pem" > server-not-yet.pem + $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-expired.pem" > server-expired.pem $ hg init test $ cd test @@ -162,22 +44,95 @@ #endif $ cd .. -OS X has a dummy CA cert that enables use of the system CA store when using -Apple's OpenSSL. This trick do not work with plain OpenSSL. +Our test cert is not signed by a trusted CA. It should fail to verify if +we are able to load CA certs. - $ DISABLEOSXDUMMYCERT= #if defaultcacerts $ hg clone https://localhost:$HGPORT/ copy-pull + (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error) + abort: error: *certificate verify failed* (glob) + [255] +#else + $ hg clone https://localhost:$HGPORT/ copy-pull + abort: localhost certificate error: no certificate received + (set hostsecurity.localhost:certfingerprints=sha256:62:09:97:2f:97:60:e3:65:8f:12:5d:78:9e:35:a1:36:7a:65:4b:0e:9f:ac:db:c3:bc:6e:b6:a3:c0:16:e0:30 config setting or use --insecure to connect insecurely) + [255] +#endif + +Specifying a per-host certificate file that doesn't exist will abort + + $ hg --config hostsecurity.localhost:verifycertsfile=/does/not/exist clone https://localhost:$HGPORT/ + abort: path specified by hostsecurity.localhost:verifycertsfile does not exist: /does/not/exist + [255] + +A malformed per-host certificate file will raise an error + + $ echo baddata > badca.pem +#if sslcontext + $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/ + abort: error loading CA file badca.pem: * (glob) + (file is empty or malformed?) + [255] +#else + $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/ + abort: error: * (glob) + [255] +#endif + +A per-host certificate mismatching the server will fail verification + +(modern ssl is able to discern whether the loaded cert is a CA cert) +#if sslcontext + $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/ + (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error) abort: error: *certificate verify failed* (glob) [255] - - $ DISABLEOSXDUMMYCERT="--insecure" +#else + $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/ + abort: error: *certificate verify failed* (glob) + [255] #endif -clone via pull +A per-host certificate matching the server's cert will be accepted + + $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" clone -U https://localhost:$HGPORT/ perhostgood1 + requesting all changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 4 changes to 4 files + +A per-host certificate with multiple certs and one matching will be accepted + + $ cat "$CERTSDIR/client-cert.pem" "$CERTSDIR/pub.pem" > perhost.pem + $ hg --config hostsecurity.localhost:verifycertsfile=perhost.pem clone -U https://localhost:$HGPORT/ perhostgood2 + requesting all changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 4 changes to 4 files - $ hg clone https://localhost:$HGPORT/ copy-pull $DISABLEOSXDUMMYCERT - warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) +Defining both per-host certificate and a fingerprint will print a warning + + $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" --config hostsecurity.localhost:fingerprints=sha1:914f1aff87249c09b6859b88b1906d30756491ca clone -U https://localhost:$HGPORT/ caandfingerwarning + (hostsecurity.localhost:verifycertsfile ignored when host fingerprints defined; using host fingerprints for verification) + requesting all changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 4 changes to 4 files + + $ DISABLECACERTS="--config devel.disableloaddefaultcerts=true" + +Inability to verify peer certificate will result in abort + + $ hg clone https://localhost:$HGPORT/ copy-pull $DISABLECACERTS + abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect + (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:62:09:97:2f:97:60:e3:65:8f:12:5d:78:9e:35:a1:36:7a:65:4b:0e:9f:ac:db:c3:bc:6e:b6:a3:c0:16:e0:30 to trust this server) + [255] + + $ hg clone --insecure https://localhost:$HGPORT/ copy-pull + warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering requesting all changes adding changesets adding manifests @@ -202,9 +157,15 @@ $ cd copy-pull $ echo '[hooks]' >> .hg/hgrc $ echo "changegroup = printenv.py changegroup" >> .hg/hgrc - $ hg pull $DISABLEOSXDUMMYCERT + $ hg pull $DISABLECACERTS pulling from https://localhost:$HGPORT/ - warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) + abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect + (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:62:09:97:2f:97:60:e3:65:8f:12:5d:78:9e:35:a1:36:7a:65:4b:0e:9f:ac:db:c3:bc:6e:b6:a3:c0:16:e0:30 to trust this server) + [255] + + $ hg pull --insecure + pulling from https://localhost:$HGPORT/ + warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering searching for changes adding changesets adding manifests @@ -218,7 +179,7 @@ $ cp copy-pull/.hg/hgrc copy-pull/.hg/hgrc.bu $ echo "[web]" >> copy-pull/.hg/hgrc - $ echo "cacerts=`pwd`/pub.pem" >> copy-pull/.hg/hgrc + $ echo "cacerts=$CERTSDIR/pub.pem" >> copy-pull/.hg/hgrc $ hg -R copy-pull pull --traceback pulling from https://localhost:$HGPORT/ searching for changes @@ -230,35 +191,55 @@ $ echo "[web]" >> $HGRCPATH $ echo 'cacerts=$P/pub.pem' >> $HGRCPATH - $ P=`pwd` hg -R copy-pull pull + $ P="$CERTSDIR" hg -R copy-pull pull pulling from https://localhost:$HGPORT/ searching for changes no changes found - $ P=`pwd` hg -R copy-pull pull --insecure + $ P="$CERTSDIR" hg -R copy-pull pull --insecure pulling from https://localhost:$HGPORT/ - warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) + warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering searching for changes no changes found +empty cacert file + + $ touch emptycafile + +#if sslcontext + $ hg --config web.cacerts=emptycafile -R copy-pull pull + pulling from https://localhost:$HGPORT/ + abort: error loading CA file emptycafile: * (glob) + (file is empty or malformed?) + [255] +#else + $ hg --config web.cacerts=emptycafile -R copy-pull pull + pulling from https://localhost:$HGPORT/ + abort: error: * (glob) + [255] +#endif + cacert mismatch - $ hg -R copy-pull pull --config web.cacerts=pub.pem https://127.0.0.1:$HGPORT/ + $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \ + > https://127.0.0.1:$HGPORT/ pulling from https://127.0.0.1:$HGPORT/ abort: 127.0.0.1 certificate error: certificate is for localhost - (configure hostfingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca or use --insecure to connect insecurely) + (set hostsecurity.127.0.0.1:certfingerprints=sha256:62:09:97:2f:97:60:e3:65:8f:12:5d:78:9e:35:a1:36:7a:65:4b:0e:9f:ac:db:c3:bc:6e:b6:a3:c0:16:e0:30 config setting or use --insecure to connect insecurely) [255] - $ hg -R copy-pull pull --config web.cacerts=pub.pem https://127.0.0.1:$HGPORT/ --insecure + $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \ + > https://127.0.0.1:$HGPORT/ --insecure pulling from https://127.0.0.1:$HGPORT/ - warning: 127.0.0.1 certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) + warning: connection security to 127.0.0.1 is disabled per current settings; communication is susceptible to eavesdropping and tampering searching for changes no changes found - $ hg -R copy-pull pull --config web.cacerts=pub-other.pem + $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem" pulling from https://localhost:$HGPORT/ abort: error: *certificate verify failed* (glob) [255] - $ hg -R copy-pull pull --config web.cacerts=pub-other.pem --insecure + $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem" \ + > --insecure pulling from https://localhost:$HGPORT/ - warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) + warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering searching for changes no changes found @@ -266,7 +247,8 @@ $ hg serve -R test -p $HGPORT1 -d --pid-file=hg1.pid --certificate=server-not-yet.pem $ cat hg1.pid >> $DAEMON_PIDS - $ hg -R copy-pull pull --config web.cacerts=pub-not-yet.pem https://localhost:$HGPORT1/ + $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-not-yet.pem" \ + > https://localhost:$HGPORT1/ pulling from https://localhost:$HGPORT1/ abort: error: *certificate verify failed* (glob) [255] @@ -275,29 +257,39 @@ $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem $ cat hg2.pid >> $DAEMON_PIDS - $ hg -R copy-pull pull --config web.cacerts=pub-expired.pem https://localhost:$HGPORT2/ + $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-expired.pem" \ + > https://localhost:$HGPORT2/ pulling from https://localhost:$HGPORT2/ abort: error: *certificate verify failed* (glob) [255] Fingerprints - $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc - $ echo "localhost = 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca" >> copy-pull/.hg/hgrc - $ echo "127.0.0.1 = 914f1aff87249c09b6859b88b1906d30756491ca" >> copy-pull/.hg/hgrc +- works without cacerts (hostkeyfingerprints) + $ hg -R copy-pull id https://localhost:$HGPORT/ --insecure --config hostfingerprints.localhost=91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca + 5fed3813f7f5 -- works without cacerts - $ hg -R copy-pull id https://localhost:$HGPORT/ --insecure +- works without cacerts (hostsecurity) + $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha1:914f1aff87249c09b6859b88b1906d30756491ca + 5fed3813f7f5 + + $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha256:62:09:97:2f:97:60:e3:65:8f:12:5d:78:9e:35:a1:36:7a:65:4b:0e:9f:ac:db:c3:bc:6e:b6:a3:c0:16:e0:30 5fed3813f7f5 - multiple fingerprints specified and first matches $ hg --config 'hostfingerprints.localhost=914f1aff87249c09b6859b88b1906d30756491ca, deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure 5fed3813f7f5 + $ hg --config 'hostsecurity.localhost:fingerprints=sha1:914f1aff87249c09b6859b88b1906d30756491ca, sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ + 5fed3813f7f5 + - multiple fingerprints specified and last matches $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, 914f1aff87249c09b6859b88b1906d30756491ca' -R copy-pull id https://localhost:$HGPORT/ --insecure 5fed3813f7f5 + $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:914f1aff87249c09b6859b88b1906d30756491ca' -R copy-pull id https://localhost:$HGPORT/ + 5fed3813f7f5 + - multiple fingerprints specified and none match $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure @@ -305,15 +297,20 @@ (check hostfingerprint configuration) [255] + $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ + abort: certificate for localhost has unexpected fingerprint sha1:91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca + (check hostsecurity configuration) + [255] + - fails when cert doesn't match hostname (port is ignored) - $ hg -R copy-pull id https://localhost:$HGPORT1/ + $ hg -R copy-pull id https://localhost:$HGPORT1/ --config hostfingerprints.localhost=914f1aff87249c09b6859b88b1906d30756491ca abort: certificate for localhost has unexpected fingerprint 28:ff:71:bf:65:31:14:23:ad:62:92:b4:0e:31:99:18:fc:83:e3:9b (check hostfingerprint configuration) [255] - ignores that certificate doesn't match hostname - $ hg -R copy-pull id https://127.0.0.1:$HGPORT/ + $ hg -R copy-pull id https://127.0.0.1:$HGPORT/ --config hostfingerprints.127.0.0.1=914f1aff87249c09b6859b88b1906d30756491ca 5fed3813f7f5 HGPORT1 is reused below for tinyproxy tests. Kill that server. @@ -334,28 +331,31 @@ $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure --traceback pulling from https://localhost:$HGPORT/ - warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) + warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering searching for changes no changes found Test https with cacert and fingerprint through proxy - $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub.pem + $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \ + > --config web.cacerts="$CERTSDIR/pub.pem" pulling from https://localhost:$HGPORT/ searching for changes no changes found - $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull https://127.0.0.1:$HGPORT/ + $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull https://127.0.0.1:$HGPORT/ --config hostfingerprints.127.0.0.1=914f1aff87249c09b6859b88b1906d30756491ca pulling from https://127.0.0.1:$HGPORT/ searching for changes no changes found Test https with cert problems through proxy - $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub-other.pem + $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \ + > --config web.cacerts="$CERTSDIR/pub-other.pem" pulling from https://localhost:$HGPORT/ abort: error: *certificate verify failed* (glob) [255] - $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub-expired.pem https://localhost:$HGPORT2/ + $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \ + > --config web.cacerts="$CERTSDIR/pub-expired.pem" https://localhost:$HGPORT2/ pulling from https://localhost:$HGPORT2/ abort: error: *certificate verify failed* (glob) [255] @@ -390,7 +390,7 @@ without client certificate: - $ P=`pwd` hg id https://localhost:$HGPORT/ + $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ abort: error: *handshake failure* (glob) [255] @@ -399,19 +399,19 @@ $ cat << EOT >> $HGRCPATH > [auth] > l.prefix = localhost - > l.cert = client-cert.pem - > l.key = client-key.pem + > l.cert = $CERTSDIR/client-cert.pem + > l.key = $CERTSDIR/client-key.pem > EOT - $ P=`pwd` hg id https://localhost:$HGPORT/ \ - > --config auth.l.key=client-key-decrypted.pem + $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \ + > --config auth.l.key="$CERTSDIR/client-key-decrypted.pem" 5fed3813f7f5 - $ printf '1234\n' | env P=`pwd` hg id https://localhost:$HGPORT/ \ + $ printf '1234\n' | env P="$CERTSDIR" hg id https://localhost:$HGPORT/ \ > --config ui.interactive=True --config ui.nontty=True - passphrase for client-key.pem: 5fed3813f7f5 + passphrase for */client-key.pem: 5fed3813f7f5 (glob) - $ env P=`pwd` hg id https://localhost:$HGPORT/ + $ env P="$CERTSDIR" hg id https://localhost:$HGPORT/ abort: error: * (glob) [255]
--- a/tests/test-inherit-mode.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-inherit-mode.t Fri Jul 01 16:02:56 2016 -0500 @@ -117,6 +117,7 @@ 00660 ../push/.hg/cache/branch2-base 00660 ../push/.hg/cache/rbc-names-v1 00660 ../push/.hg/cache/rbc-revs-v1 + 00660 ../push/.hg/dirstate 00660 ../push/.hg/requires 00770 ../push/.hg/store/ 00660 ../push/.hg/store/00changelog.i
--- a/tests/test-install.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-install.t Fri Jul 01 16:02:56 2016 -0500 @@ -4,6 +4,9 @@ checking Python executable (*) (glob) checking Python version (2.*) (glob) checking Python lib (*lib*)... (glob) + checking Mercurial version (*) (glob) + checking Mercurial custom build (*) (glob) + checking module policy (*) (glob) checking installed modules (*mercurial)... (glob) checking templates (*mercurial?templates)... (glob) checking default template (*mercurial?templates?map-cmdline.default) (glob) @@ -23,7 +26,10 @@ "encoding": "ascii", "encodingerror": null, "extensionserror": null, + "hgmodulepolicy": "*", (glob) "hgmodules": "*mercurial", (glob) + "hgver": "*", (glob) + "hgverextra": "*", (glob) "problems": 0, "pythonexe": "*", (glob) "pythonlib": "*", (glob) @@ -41,6 +47,9 @@ checking Python executable (*) (glob) checking Python version (2.*) (glob) checking Python lib (*lib*)... (glob) + checking Mercurial version (*) (glob) + checking Mercurial custom build (*) (glob) + checking module policy (*) (glob) checking installed modules (*mercurial)... (glob) checking templates (*mercurial?templates)... (glob) checking default template (*mercurial?templates?map-cmdline.default) (glob) @@ -62,6 +71,9 @@ checking Python executable (*) (glob) checking Python version (*) (glob) checking Python lib (*lib*)... (glob) + checking Mercurial version (*) (glob) + checking Mercurial custom build (*) (glob) + checking module policy (*) (glob) checking installed modules (*mercurial)... (glob) checking templates (*mercurial?templates)... (glob) checking default template (*mercurial?templates?map-cmdline.default) (glob) @@ -70,6 +82,8 @@ no problems detected #if test-repo + $ . "$TESTDIR/helpers-testrepo.sh" + $ cat >> wixxml.py << EOF > import os, subprocess, sys > import xml.etree.ElementTree as ET
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-journal.t Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,148 @@ +Tests for the journal extension; records bookmark locations. + + $ cat >> testmocks.py << EOF + > # mock out util.getuser() and util.makedate() to supply testable values + > import os + > from mercurial import util + > def mockgetuser(): + > return 'foobar' + > + > def mockmakedate(): + > filename = os.path.join(os.environ['TESTTMP'], 'testtime') + > try: + > with open(filename, 'rb') as timef: + > time = float(timef.read()) + 1 + > except IOError: + > time = 0.0 + > with open(filename, 'wb') as timef: + > timef.write(str(time)) + > return (time, 0) + > + > util.getuser = mockgetuser + > util.makedate = mockmakedate + > EOF + + $ cat >> $HGRCPATH << EOF + > [extensions] + > journal= + > testmocks=`pwd`/testmocks.py + > EOF + +Setup repo + + $ hg init repo + $ cd repo + $ echo a > a + $ hg commit -Aqm a + $ echo b > a + $ hg commit -Aqm b + $ hg up 0 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + +Test empty journal + + $ hg journal + previous locations of all bookmarks: + no recorded locations + $ hg journal foo + previous locations of 'foo': + no recorded locations + +Test that bookmarks are tracked + + $ hg book -r tip bar + $ hg journal bar + previous locations of 'bar': + 1e6c11564562 book -r tip bar + $ hg book -f bar + $ hg journal bar + previous locations of 'bar': + cb9a9f314b8b book -f bar + 1e6c11564562 book -r tip bar + $ hg up + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + updating bookmark bar + $ hg journal bar + previous locations of 'bar': + 1e6c11564562 up + cb9a9f314b8b book -f bar + 1e6c11564562 book -r tip bar + +Test that you can list all bookmarks as well as limit the list or filter on them + + $ hg book -r tip baz + $ hg journal + previous locations of all bookmarks: + 1e6c11564562 book -r tip baz + 1e6c11564562 up + cb9a9f314b8b book -f bar + 1e6c11564562 book -r tip bar + $ hg journal --limit 2 + previous locations of all bookmarks: + 1e6c11564562 book -r tip baz + 1e6c11564562 up + $ hg journal baz + previous locations of 'baz': + 1e6c11564562 book -r tip baz + $ hg journal bar + previous locations of 'bar': + 1e6c11564562 up + cb9a9f314b8b book -f bar + 1e6c11564562 book -r tip bar + $ hg journal foo + previous locations of 'foo': + no recorded locations + +Test that verbose and commit output work + + $ hg journal --verbose + previous locations of all bookmarks: + 000000000000 -> 1e6c11564562 foobar 1970-01-01 00:00 +0000 book -r tip baz + cb9a9f314b8b -> 1e6c11564562 foobar 1970-01-01 00:00 +0000 up + 1e6c11564562 -> cb9a9f314b8b foobar 1970-01-01 00:00 +0000 book -f bar + 000000000000 -> 1e6c11564562 foobar 1970-01-01 00:00 +0000 book -r tip bar + $ hg journal --commit + previous locations of all bookmarks: + 1e6c11564562 book -r tip baz + changeset: 1:1e6c11564562 + bookmark: bar + bookmark: baz + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: b + + 1e6c11564562 up + changeset: 1:1e6c11564562 + bookmark: bar + bookmark: baz + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: b + + cb9a9f314b8b book -f bar + changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + 1e6c11564562 book -r tip bar + changeset: 1:1e6c11564562 + bookmark: bar + bookmark: baz + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: b + + +Test for behaviour on unexpected storage version information + + $ printf '42\0' > .hg/journal + $ hg journal + previous locations of all bookmarks: + abort: unknown journal file version '42' + [255] + $ hg book -r tip doomed + unsupported journal file version '42'
--- a/tests/test-largefiles-wireproto.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-largefiles-wireproto.t Fri Jul 01 16:02:56 2016 -0500 @@ -149,6 +149,14 @@ $ hg commit -m "m2" Invoking status precommit hook A f2 + $ hg verify --large + checking changesets + checking manifests + crosschecking files in changesets and manifests + checking files + 2 files, 2 changesets, 2 total revisions + searching 1 changesets for largefiles + verified existence of 1 revisions of 1 largefiles $ hg serve --config extensions.largefiles=! -R ../r6 -d -p $HGPORT --pid-file ../hg.pid $ cat ../hg.pid >> $DAEMON_PIDS $ hg push http://localhost:$HGPORT @@ -306,4 +314,134 @@ used all HGPORTs, kill all daemons $ killdaemons.py +largefiles should batch verify remote calls + + $ hg init batchverifymain + $ cd batchverifymain + $ echo "aaa" >> a + $ hg add --large a + $ hg commit -m "a" + Invoking status precommit hook + A a + $ echo "bbb" >> b + $ hg add --large b + $ hg commit -m "b" + Invoking status precommit hook + A b + $ cd .. + $ hg serve -R batchverifymain -d -p $HGPORT --pid-file hg.pid \ + > -A access.log + $ cat hg.pid >> $DAEMON_PIDS + $ hg clone --noupdate http://localhost:$HGPORT batchverifyclone + requesting all changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files + $ hg -R batchverifyclone verify --large --lfa + checking changesets + checking manifests + crosschecking files in changesets and manifests + checking files + 2 files, 2 changesets, 2 total revisions + searching 2 changesets for largefiles + verified existence of 2 revisions of 2 largefiles + $ tail -1 access.log + 127.0.0.1 - - [*] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=statlfile+sha%3D972a1a11f19934401291cc99117ec614933374ce%3Bstatlfile+sha%3Dc801c9cfe94400963fcb683246217d5db77f9a9a (glob) + $ hg -R batchverifyclone update + getting changed largefiles + 2 largefiles updated, 0 removed + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + +Clear log file before next test + + $ printf "" > access.log + +Verify should check file on remote server only when file is not +available locally. + + $ echo "ccc" >> batchverifymain/c + $ hg -R batchverifymain status + ? c + $ hg -R batchverifymain add --large batchverifymain/c + $ hg -R batchverifymain commit -m "c" + Invoking status precommit hook + A c + $ hg -R batchverifyclone pull + pulling from http://localhost:$HGPORT/ + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + (run 'hg update' to get a working copy) + $ hg -R batchverifyclone verify --lfa + checking changesets + checking manifests + crosschecking files in changesets and manifests + checking files + 3 files, 3 changesets, 3 total revisions + searching 3 changesets for largefiles + verified existence of 3 revisions of 3 largefiles + $ tail -1 access.log + 127.0.0.1 - - [*] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=statlfile+sha%3Dc8559c3c9cfb42131794b7d8009230403b9b454c (glob) + + $ killdaemons.py + +largefiles should not ask for password again after succesfull authorization + + $ hg init credentialmain + $ cd credentialmain + $ echo "aaa" >> a + $ hg add --large a + $ hg commit -m "a" + Invoking status precommit hook + A a + +Before running server clear the user cache to force clone to download +a large file from the server rather than to get it from the cache + + $ rm "${USERCACHE}"/* + + $ cd .. + $ cat << EOT > userpass.py + > import base64 + > from mercurial.hgweb import common + > def perform_authentication(hgweb, req, op): + > auth = req.env.get('HTTP_AUTHORIZATION') + > if not auth: + > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, 'who', + > [('WWW-Authenticate', 'Basic Realm="mercurial"')]) + > if base64.b64decode(auth.split()[1]).split(':', 1) != ['user', 'pass']: + > raise common.ErrorResponse(common.HTTP_FORBIDDEN, 'no') + > def extsetup(): + > common.permhooks.insert(0, perform_authentication) + > EOT + $ hg serve --config extensions.x=userpass.py -R credentialmain \ + > -d -p $HGPORT --pid-file hg.pid -A access.log + $ cat hg.pid >> $DAEMON_PIDS + $ cat << EOF > get_pass.py + > import getpass + > def newgetpass(arg): + > return "pass" + > getpass.getpass = newgetpass + > EOF + $ hg clone --config ui.interactive=true --config extensions.getpass=get_pass.py \ + > http://user@localhost:$HGPORT credentialclone + requesting all changes + http authorization required for http://localhost:$HGPORT/ + realm: mercurial + user: user + password: adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + updating to branch default + getting changed largefiles + 1 largefiles updated, 0 removed + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ rm hg.pid access.log + $ killdaemons.py + #endif
--- a/tests/test-largefiles.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-largefiles.t Fri Jul 01 16:02:56 2016 -0500 @@ -1536,8 +1536,11 @@ searching 1 changesets for largefiles verified existence of 3 revisions of 3 largefiles -- introduce missing blob in local store repo and make sure that this is caught: +- introduce missing blob in local store repo and remote store +and make sure that this is caught: + $ mv $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 . + $ rm .hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 $ hg verify --large checking changesets checking manifests @@ -1556,7 +1559,8 @@ [1] - cleanup - $ mv e166e74c7303192238d60af5a9c4ce9bef0b7928 $TESTTMP/d/.hg/largefiles/ + $ cp e166e74c7303192238d60af5a9c4ce9bef0b7928 $TESTTMP/d/.hg/largefiles/ + $ mv e166e74c7303192238d60af5a9c4ce9bef0b7928 .hg/largefiles/ - verifying all revisions will fail because we didn't clone all largefiles to d: $ echo 'T-shirt' > $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
--- a/tests/test-mac-packages.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-mac-packages.t Fri Jul 01 16:02:56 2016 -0500 @@ -1,4 +1,7 @@ #require test-repo slow osx osxpackaging + + $ . "$TESTDIR/helpers-testrepo.sh" + $ OUTPUTDIR=`pwd` $ export OUTPUTDIR $ KEEPMPKG=yes
--- a/tests/test-newbranch.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-newbranch.t Fri Jul 01 16:02:56 2016 -0500 @@ -463,3 +463,72 @@ -1 new $ cd .. + +We expect that update --clean discard changes in working directory, +and updates to the head of parent branch. + + $ hg init updatebareclean + $ cd updatebareclean + $ hg update --clean + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ touch a + $ hg commit -A -m "a" + adding a + $ touch b + $ hg commit -A -m "b" + adding b + $ touch c + $ hg commit -A -m "c" + adding c + $ hg log + changeset: 2:991a3460af53 + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: c + + changeset: 1:0e067c57feba + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: b + + changeset: 0:3903775176ed + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + $ hg update -r 1 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg branch new-branch + marked working directory as branch new-branch + (branches are permanent and global, did you want a bookmark?) + $ echo "aa" >> a + $ hg update --clean + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg status + $ hg branch + default + $ hg parent + changeset: 2:991a3460af53 + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: c + +We expect that update --clean on non existing parent discards a new branch +and updates to the tipmost non-closed branch head + + $ hg update null + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + $ hg branch newbranch + marked working directory as branch newbranch + (branches are permanent and global, did you want a bookmark?) + $ hg update -C + 3 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg summary + parent: 2:991a3460af53 tip + c + branch: default + commit: (clean) + update: (current) + phases: 3 draft
--- a/tests/test-notify.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-notify.t Fri Jul 01 16:02:56 2016 -0500 @@ -571,7 +571,7 @@ Message-Id: <hg.3548c9e294b6.*.*@*> (glob) To: baz@test.com, foo@bar - changeset 3548c9e294b6 in $TESTTMP/b + changeset 3548c9e294b6 in $TESTTMP/b (glob) details: http://test/b?cmd=changeset;node=3548c9e294b6 description: default template
--- a/tests/test-pager.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-pager.t Fri Jul 01 16:02:56 2016 -0500 @@ -177,3 +177,48 @@ paged! 'date: Thu Jan 01 00:00:00 1970 +0000\n' paged! 'summary: modify a 8\n' paged! '\n' + +Pager works with shell aliases. + + $ cat >> $HGRCPATH <<EOF + > [alias] + > echoa = !echo a + > EOF + + $ hg echoa + a + $ hg --config pager.attend-echoa=yes echoa + paged! 'a\n' + +Pager works with hg aliases including environment variables. + + $ cat >> $HGRCPATH <<'EOF' + > [alias] + > printa = log -T "$A\n" -r 0 + > EOF + + $ A=1 hg --config pager.attend-printa=yes printa + paged! '1\n' + $ A=2 hg --config pager.attend-printa=yes printa + paged! '2\n' + +Pager should not override the exit code of other commands + + $ cat >> $TESTTMP/fortytwo.py <<'EOF' + > from mercurial import cmdutil, commands + > cmdtable = {} + > command = cmdutil.command(cmdtable) + > @command('fortytwo', [], 'fortytwo', norepo=True) + > def fortytwo(ui, *opts): + > ui.write('42\n') + > return 42 + > EOF + + $ cat >> $HGRCPATH <<'EOF' + > [extensions] + > fortytwo = $TESTTMP/fortytwo.py + > EOF + + $ hg fortytwo --pager=on + paged! '42\n' + [42]
--- a/tests/test-parseindex2.py Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-parseindex2.py Fri Jul 01 16:02:56 2016 -0500 @@ -9,13 +9,13 @@ import subprocess import sys -from mercurial import ( - parsers, -) from mercurial.node import ( nullid, nullrev, ) +from mercurial import ( + parsers, +) # original python implementation def gettype(q):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-patchbomb-tls.t Fri Jul 01 16:02:56 2016 -0500 @@ -0,0 +1,91 @@ +#require serve ssl + +Set up SMTP server: + + $ CERTSDIR="$TESTDIR/sslcerts" + $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub.pem" >> server.pem + + $ python "$TESTDIR/dummysmtpd.py" -p $HGPORT --pid-file a.pid -d \ + > --tls smtps --certificate `pwd`/server.pem + listening at localhost:$HGPORT + $ cat a.pid >> $DAEMON_PIDS + +Ensure hg email output is sent to stdout: + + $ unset PAGER + +Set up repository: + + $ hg init t + $ cd t + $ cat <<EOF >> .hg/hgrc + > [extensions] + > patchbomb = + > [email] + > method = smtp + > [smtp] + > host = localhost + > port = $HGPORT + > tls = smtps + > EOF + + $ echo a > a + $ hg commit -Ama -d '1 0' + adding a + +Utility functions: + + $ DISABLECACERTS= + $ try () { + > hg email $DISABLECACERTS -f quux -t foo -c bar -r tip "$@" + > } + +Our test cert is not signed by a trusted CA. It should fail to verify if +we are able to load CA certs: + +#if defaultcacerts + $ try + this patch series consists of 1 patches. + + + (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error) + (?i)abort: .*?certificate.verify.failed.* (re) + [255] +#endif + + $ DISABLECACERTS="--config devel.disableloaddefaultcerts=true" + +Without certificates: + + $ try --debug + this patch series consists of 1 patches. + + + (using smtps) + sending mail: smtp host localhost, port * (glob) + (verifying remote certificate) + abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect + (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:62:09:97:2f:97:60:e3:65:8f:12:5d:78:9e:35:a1:36:7a:65:4b:0e:9f:ac:db:c3:bc:6e:b6:a3:c0:16:e0:30 to trust this server) + [255] + +With global certificates: + + $ try --debug --config web.cacerts="$CERTSDIR/pub.pem" + this patch series consists of 1 patches. + + + (using smtps) + sending mail: smtp host localhost, port * (glob) + (verifying remote certificate) + sending [PATCH] a ... + +With invalid certificates: + + $ try --config web.cacerts="$CERTSDIR/pub-other.pem" + this patch series consists of 1 patches. + + + (?i)abort: .*?certificate.verify.failed.* (re) + [255] + + $ cd ..
--- a/tests/test-push-warn.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-push-warn.t Fri Jul 01 16:02:56 2016 -0500 @@ -785,4 +785,14 @@ no changes found [1] +Test fail hook + + $ hg push inner --config hooks.fail-push="echo running fail-push hook" + pushing to inner + searching for changes + running fail-push hook + abort: push creates new remote head 7d0f4fb6cf04 on branch 'A'! + (merge or see "hg help push" for details about pushing new heads) + [255] + $ cd ..
--- a/tests/test-rebase-obsolete.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-rebase-obsolete.t Fri Jul 01 16:02:56 2016 -0500 @@ -709,9 +709,7 @@ created new head $ hg debugobsolete `hg log -r 11 -T '{node}\n'` --config experimental.evolution=all $ hg rebase -r . -d 10 - abort: all requested changesets have equivalents or were marked as obsolete - (to force the rebase, set the config experimental.rebaseskipobsolete to False) - [255] + note: not rebasing 11:f44da1f4954c "nonrelevant" (tip), it has no successor If a rebase is going to create divergence, it should abort @@ -863,3 +861,91 @@ rebasing 20:b82fb57ea638 "willconflict second version" note: not rebasing 21:8b31da3c4919 "dummy change", already in destination as 19:601db7a18f51 "dummy change successor" rebasing 22:7bdc8a87673d "dummy change" (tip) + $ cd .. + +rebase source is obsoleted (issue5198) +--------------------------------- + + $ hg clone base amended + updating to branch default + 3 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd amended + $ hg up 9520eea781bc + 1 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ echo 1 >> E + $ hg commit --amend -m "E'" + $ hg log -G + @ 9:69abe8906104 E' + | + | o 7:02de42196ebe H + | | + | | o 6:eea13746799a G + | |/| + | o | 5:24b6387c8c8c F + |/ / + | x 4:9520eea781bc E + |/ + | o 3:32af7686d403 D + | | + | o 2:5fddd98957c8 C + | | + | o 1:42ccdea3bb16 B + |/ + o 0:cd010b8cd998 A + + $ hg rebase -d . -s 9520eea781bc + note: not rebasing 4:9520eea781bc "E", already in destination as 9:69abe8906104 "E'" + rebasing 6:eea13746799a "G" + $ hg log -G + o 10:17be06e82e95 G + |\ + | @ 9:69abe8906104 E' + | | + +---o 7:02de42196ebe H + | | + o | 5:24b6387c8c8c F + |/ + | o 3:32af7686d403 D + | | + | o 2:5fddd98957c8 C + | | + | o 1:42ccdea3bb16 B + |/ + o 0:cd010b8cd998 A + + $ cd .. + +Test that bookmark is moved and working dir is updated when all changesets have +equivalents in destination + $ hg init rbsrepo && cd rbsrepo + $ echo "[experimental]" > .hg/hgrc + $ echo "evolution=all" >> .hg/hgrc + $ echo "rebaseskipobsolete=on" >> .hg/hgrc + $ echo root > root && hg ci -Am root + adding root + $ echo a > a && hg ci -Am a + adding a + $ hg up 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo b > b && hg ci -Am b + adding b + created new head + $ hg rebase -r 2 -d 1 + rebasing 2:1e9a3c00cbe9 "b" (tip) + $ hg log -r . # working dir is at rev 3 (successor of 2) + 3:be1832deae9a b (no-eol) + $ hg book -r 2 mybook --hidden # rev 2 has a bookmark on it now + $ hg up 2 && hg log -r . # working dir is at rev 2 again + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + 2:1e9a3c00cbe9 b (no-eol) + $ hg rebase -r 2 -d 3 + note: not rebasing 2:1e9a3c00cbe9 "b" (mybook), already in destination as 3:be1832deae9a "b" +Check that working directory was updated to rev 3 although rev 2 was skipped +during the rebase operation + $ hg log -r . + 3:be1832deae9a b (no-eol) + +Check that bookmark was moved to rev 3 although rev 2 was skipped +during the rebase operation + $ hg bookmarks + mybook 3:be1832deae9a
--- a/tests/test-revert-interactive.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-revert-interactive.t Fri Jul 01 16:02:56 2016 -0500 @@ -64,7 +64,7 @@ 3 4 5 - record change 1/6 to 'f'? [Ynesfdaq?] y + revert change 1/6 to 'f'? [Ynesfdaq?] y @@ -1,5 +2,6 @@ 1 @@ -73,7 +73,7 @@ 4 5 +b - record change 2/6 to 'f'? [Ynesfdaq?] y + revert change 2/6 to 'f'? [Ynesfdaq?] y diff --git a/folder1/g b/folder1/g 2 hunks, 2 lines changed @@ -86,7 +86,7 @@ 3 4 5 - record change 3/6 to 'folder1/g'? [Ynesfdaq?] y + revert change 3/6 to 'folder1/g'? [Ynesfdaq?] y @@ -1,5 +2,6 @@ 1 @@ -95,7 +95,7 @@ 4 5 +d - record change 4/6 to 'folder1/g'? [Ynesfdaq?] n + revert change 4/6 to 'folder1/g'? [Ynesfdaq?] n diff --git a/folder2/h b/folder2/h 2 hunks, 2 lines changed @@ -163,7 +163,7 @@ 3 4 5 - record change 1/6 to 'f'? [Ynesfdaq?] y + revert change 1/6 to 'f'? [Ynesfdaq?] y @@ -1,5 +2,6 @@ 1 @@ -172,7 +172,7 @@ 4 5 +b - record change 2/6 to 'f'? [Ynesfdaq?] y + revert change 2/6 to 'f'? [Ynesfdaq?] y diff --git a/folder1/g b/folder1/g 2 hunks, 2 lines changed @@ -185,7 +185,7 @@ 3 4 5 - record change 3/6 to 'folder1/g'? [Ynesfdaq?] y + revert change 3/6 to 'folder1/g'? [Ynesfdaq?] y @@ -1,5 +2,6 @@ 1 @@ -194,7 +194,7 @@ 4 5 +d - record change 4/6 to 'folder1/g'? [Ynesfdaq?] n + revert change 4/6 to 'folder1/g'? [Ynesfdaq?] n diff --git a/folder2/h b/folder2/h 2 hunks, 2 lines changed @@ -242,7 +242,7 @@ 3 4 5 - record change 1/2 to 'f'? [Ynesfdaq?] y + discard change 1/2 to 'f'? [Ynesfdaq?] y @@ -2,6 +1,5 @@ 1 @@ -251,7 +251,7 @@ 4 5 -b - record change 2/2 to 'f'? [Ynesfdaq?] n + discard change 2/2 to 'f'? [Ynesfdaq?] n $ hg st M f @@ -303,7 +303,7 @@ -1 +0 +2 - record this change to 'k'? [Ynesfdaq?] e + discard this change to 'k'? [Ynesfdaq?] e $ cat k 42 @@ -350,7 +350,7 @@ 1 2 3 - record change 1/3 to 'folder1/g'? [Ynesfdaq?] y + discard change 1/3 to 'folder1/g'? [Ynesfdaq?] y @@ -2,7 +1,7 @@ c @@ -361,13 +361,13 @@ +4 5 d - record change 2/3 to 'folder1/g'? [Ynesfdaq?] y + discard change 2/3 to 'folder1/g'? [Ynesfdaq?] y @@ -7,3 +6,2 @@ 5 d -lastline - record change 3/3 to 'folder1/g'? [Ynesfdaq?] n + discard change 3/3 to 'folder1/g'? [Ynesfdaq?] n $ hg diff --nodates diff -r a3d963a027aa folder1/g
--- a/tests/test-revset.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-revset.t Fri Jul 01 16:02:56 2016 -0500 @@ -31,6 +31,46 @@ > hg log --template '{rev}\n' -r "$1" > } +extension to build '_intlist()' and '_hexlist()', which is necessary because +these predicates use '\0' as a separator: + + $ cat <<EOF > debugrevlistspec.py + > from __future__ import absolute_import + > from mercurial import ( + > cmdutil, + > node as nodemod, + > revset, + > ) + > cmdtable = {} + > command = cmdutil.command(cmdtable) + > @command('debugrevlistspec', + > [('', 'optimize', None, 'print parsed tree after optimizing'), + > ('', 'bin', None, 'unhexlify arguments')]) + > def debugrevlistspec(ui, repo, fmt, *args, **opts): + > if opts['bin']: + > args = map(nodemod.bin, args) + > expr = revset.formatspec(fmt, list(args)) + > if ui.verbose: + > tree = revset.parse(expr, lookup=repo.__contains__) + > ui.note(revset.prettyformat(tree), "\n") + > if opts["optimize"]: + > opttree = revset.optimize(tree) + > ui.note("* optimized:\n", revset.prettyformat(opttree), "\n") + > func = revset.match(ui, expr, repo) + > revs = func(repo) + > if ui.verbose: + > ui.note("* set:\n", revset.prettyformatset(revs), "\n") + > for c in revs: + > ui.write("%s\n" % c) + > EOF + $ cat <<EOF >> $HGRCPATH + > [extensions] + > debugrevlistspec = $TESTTMP/debugrevlistspec.py + > EOF + $ trylist() { + > hg debugrevlistspec --debug "$@" + > } + $ hg init repo $ cd repo @@ -394,6 +434,12 @@ 4 $ hg book -d date +function name should be a symbol + + $ log '"date"(2005)' + hg: parse error: not a symbol + [255] + keyword arguments $ log 'extra(branch, value=a)' @@ -898,6 +944,445 @@ $ log 'tag(tip)' 9 +Test order of revisions in compound expression +---------------------------------------------- + +The general rule is that only the outermost (= leftmost) predicate can +enforce its ordering requirement. The other predicates should take the +ordering defined by it. + + 'A & B' should follow the order of 'A': + + $ log '2:0 & 0::2' + 2 + 1 + 0 + + 'head()' combines sets in right order: + + $ log '2:0 & head()' + 2 + 1 + 0 + + 'a + b', which is optimized to '_list(a b)', should take the ordering of + the left expression: + + $ try --optimize '2:0 & (0 + 1 + 2)' + (and + (range + ('symbol', '2') + ('symbol', '0')) + (group + (or + ('symbol', '0') + ('symbol', '1') + ('symbol', '2')))) + * optimized: + (and + (range + ('symbol', '2') + ('symbol', '0')) + (func + ('symbol', '_list') + ('string', '0\x001\x002'))) + * set: + <baseset [0, 1, 2]> + 0 + 1 + 2 + BROKEN: should be '2 1 0' + + 'A + B' should take the ordering of the left expression: + + $ try --optimize '2:0 & (0:1 + 2)' + (and + (range + ('symbol', '2') + ('symbol', '0')) + (group + (or + (range + ('symbol', '0') + ('symbol', '1')) + ('symbol', '2')))) + * optimized: + (and + (range + ('symbol', '2') + ('symbol', '0')) + (or + (range + ('symbol', '0') + ('symbol', '1')) + ('symbol', '2'))) + * set: + <addset + <filteredset + <spanset+ 0:1>, + <spanset- 0:2>>, + <baseset [2]>> + 0 + 1 + 2 + BROKEN: should be '2 1 0' + + '_intlist(a b)' should behave like 'a + b': + + $ trylist --optimize '2:0 & %ld' 0 1 2 + (and + (range + ('symbol', '2') + ('symbol', '0')) + (func + ('symbol', '_intlist') + ('string', '0\x001\x002'))) + * optimized: + (and + (func + ('symbol', '_intlist') + ('string', '0\x001\x002')) + (range + ('symbol', '2') + ('symbol', '0'))) + * set: + <filteredset + <spanset- 0:2>, + <baseset [0, 1, 2]>> + 2 + 1 + 0 + + $ trylist --optimize '%ld & 2:0' 0 2 1 + (and + (func + ('symbol', '_intlist') + ('string', '0\x002\x001')) + (range + ('symbol', '2') + ('symbol', '0'))) + * optimized: + (and + (func + ('symbol', '_intlist') + ('string', '0\x002\x001')) + (range + ('symbol', '2') + ('symbol', '0'))) + * set: + <filteredset + <spanset- 0:2>, + <baseset [0, 2, 1]>> + 2 + 1 + 0 + BROKEN: should be '0 2 1' + + '_hexlist(a b)' should behave like 'a + b': + + $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2` + (and + (range + ('symbol', '2') + ('symbol', '0')) + (func + ('symbol', '_hexlist') + ('string', '*'))) (glob) + * optimized: + (and + (range + ('symbol', '2') + ('symbol', '0')) + (func + ('symbol', '_hexlist') + ('string', '*'))) (glob) + * set: + <baseset [0, 1, 2]> + 0 + 1 + 2 + BROKEN: should be '2 1 0' + + $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1` + (and + (func + ('symbol', '_hexlist') + ('string', '*')) (glob) + (range + ('symbol', '2') + ('symbol', '0'))) + * optimized: + (and + (range + ('symbol', '2') + ('symbol', '0')) + (func + ('symbol', '_hexlist') + ('string', '*'))) (glob) + * set: + <baseset [0, 2, 1]> + 0 + 2 + 1 + + 'present()' should do nothing other than suppressing an error: + + $ try --optimize '2:0 & present(0 + 1 + 2)' + (and + (range + ('symbol', '2') + ('symbol', '0')) + (func + ('symbol', 'present') + (or + ('symbol', '0') + ('symbol', '1') + ('symbol', '2')))) + * optimized: + (and + (range + ('symbol', '2') + ('symbol', '0')) + (func + ('symbol', 'present') + (func + ('symbol', '_list') + ('string', '0\x001\x002')))) + * set: + <baseset [0, 1, 2]> + 0 + 1 + 2 + BROKEN: should be '2 1 0' + + 'reverse()' should take effect only if it is the outermost expression: + + $ try --optimize '0:2 & reverse(all())' + (and + (range + ('symbol', '0') + ('symbol', '2')) + (func + ('symbol', 'reverse') + (func + ('symbol', 'all') + None))) + * optimized: + (and + (range + ('symbol', '0') + ('symbol', '2')) + (func + ('symbol', 'reverse') + (func + ('symbol', 'all') + None))) + * set: + <filteredset + <spanset- 0:2>, + <spanset+ 0:9>> + 2 + 1 + 0 + BROKEN: should be '0 1 2' + + 'sort()' should take effect only if it is the outermost expression: + + $ try --optimize '0:2 & sort(all(), -rev)' + (and + (range + ('symbol', '0') + ('symbol', '2')) + (func + ('symbol', 'sort') + (list + (func + ('symbol', 'all') + None) + (negate + ('symbol', 'rev'))))) + * optimized: + (and + (range + ('symbol', '0') + ('symbol', '2')) + (func + ('symbol', 'sort') + (list + (func + ('symbol', 'all') + None) + ('string', '-rev')))) + * set: + <filteredset + <spanset- 0:2>, + <spanset+ 0:9>> + 2 + 1 + 0 + BROKEN: should be '0 1 2' + + for 'A & f(B)', 'B' should not be affected by the order of 'A': + + $ try --optimize '2:0 & first(1 + 0 + 2)' + (and + (range + ('symbol', '2') + ('symbol', '0')) + (func + ('symbol', 'first') + (or + ('symbol', '1') + ('symbol', '0') + ('symbol', '2')))) + * optimized: + (and + (range + ('symbol', '2') + ('symbol', '0')) + (func + ('symbol', 'first') + (func + ('symbol', '_list') + ('string', '1\x000\x002')))) + * set: + <baseset + <limit n=1, offset=0, + <spanset- 0:2>, + <baseset [1, 0, 2]>>> + 1 + + $ try --optimize '2:0 & not last(0 + 2 + 1)' + (and + (range + ('symbol', '2') + ('symbol', '0')) + (not + (func + ('symbol', 'last') + (or + ('symbol', '0') + ('symbol', '2') + ('symbol', '1'))))) + * optimized: + (difference + (range + ('symbol', '2') + ('symbol', '0')) + (func + ('symbol', 'last') + (func + ('symbol', '_list') + ('string', '0\x002\x001')))) + * set: + <filteredset + <spanset- 0:2>, + <not + <baseset + <last n=1, + <fullreposet+ 0:9>, + <baseset [1, 2, 0]>>>>> + 2 + 0 + + for 'A & (op)(B)', 'B' should not be affected by the order of 'A': + + $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)' + (and + (range + ('symbol', '2') + ('symbol', '0')) + (range + (group + (or + ('symbol', '1') + ('symbol', '0') + ('symbol', '2'))) + (group + (or + ('symbol', '0') + ('symbol', '2') + ('symbol', '1'))))) + * optimized: + (and + (range + ('symbol', '2') + ('symbol', '0')) + (range + (func + ('symbol', '_list') + ('string', '1\x000\x002')) + (func + ('symbol', '_list') + ('string', '0\x002\x001')))) + * set: + <filteredset + <baseset [1]>, + <spanset- 0:2>> + 1 + + 'A & B' can be rewritten as 'B & A' by weight, but the ordering rule should + be determined before the optimization (i.e. 'B' should take the ordering of + 'A'): + + $ try --optimize 'contains("glob:*") & (2 + 0 + 1)' + (and + (func + ('symbol', 'contains') + ('string', 'glob:*')) + (group + (or + ('symbol', '2') + ('symbol', '0') + ('symbol', '1')))) + * optimized: + (and + (func + ('symbol', '_list') + ('string', '2\x000\x001')) + (func + ('symbol', 'contains') + ('string', 'glob:*'))) + * set: + <filteredset + <baseset [2, 0, 1]>, + <contains 'glob:*'>> + 2 + 0 + 1 + BROKEN: should be '0 1 2' + + $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)' + (and + (func + ('symbol', 'reverse') + (func + ('symbol', 'contains') + ('string', 'glob:*'))) + (group + (or + ('symbol', '0') + ('symbol', '2') + ('symbol', '1')))) + * optimized: + (and + (func + ('symbol', '_list') + ('string', '0\x002\x001')) + (func + ('symbol', 'reverse') + (func + ('symbol', 'contains') + ('string', 'glob:*')))) + * set: + <filteredset + <baseset [1, 2, 0]>, + <contains 'glob:*'>> + 1 + 2 + 0 + BROKEN: should be '2 1 0' + test sort revset -------------------------------------------- @@ -952,6 +1437,19 @@ 6 2 +test empty sort key which is noop + + $ log 'sort(0 + 2 + 1, "")' + 0 + 2 + 1 + +test invalid sort keys + + $ log 'sort(all(), -invalid)' + hg: parse error: unknown sort key '-invalid' + [255] + $ cd .. test sorting by multiple keys including variable-length strings @@ -1090,6 +1588,67 @@ 0 b12 m111 u112 111 10800 2 b111 m11 u12 111 3600 + toposort prioritises graph branches + + $ hg up 2 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ touch a + $ hg addremove + adding a + $ hg ci -m 't1' -u 'tu' -d '130 0' + created new head + $ echo 'a' >> a + $ hg ci -m 't2' -u 'tu' -d '130 0' + $ hg book book1 + $ hg up 4 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + (leaving bookmark book1) + $ touch a + $ hg addremove + adding a + $ hg ci -m 't3' -u 'tu' -d '130 0' + + $ hg log -r 'sort(all(), topo)' + 7 b111 t3 tu 130 0 + 4 b111 m112 u111 110 14400 + 3 b112 m111 u11 120 0 + 6 b111 t2 tu 130 0 + 5 b111 t1 tu 130 0 + 2 b111 m11 u12 111 3600 + 1 b11 m12 u111 112 7200 + 0 b12 m111 u112 111 10800 + + $ hg log -r 'sort(all(), -topo)' + 0 b12 m111 u112 111 10800 + 1 b11 m12 u111 112 7200 + 2 b111 m11 u12 111 3600 + 5 b111 t1 tu 130 0 + 6 b111 t2 tu 130 0 + 3 b112 m111 u11 120 0 + 4 b111 m112 u111 110 14400 + 7 b111 t3 tu 130 0 + + $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)' + 6 b111 t2 tu 130 0 + 5 b111 t1 tu 130 0 + 7 b111 t3 tu 130 0 + 4 b111 m112 u111 110 14400 + 3 b112 m111 u11 120 0 + 2 b111 m11 u12 111 3600 + 1 b11 m12 u111 112 7200 + 0 b12 m111 u112 111 10800 + +topographical sorting can't be combined with other sort keys, and you can't +use the topo.firstbranch option when topo sort is not active: + + $ hg log -r 'sort(all(), "topo user")' + hg: parse error: topo sort order cannot be combined with other sort keys + [255] + + $ hg log -r 'sort(all(), user, topo.firstbranch=book1)' + hg: parse error: topo.firstbranch can only be used when using the topo sort key + [255] + $ cd .. $ cd repo @@ -1480,6 +2039,16 @@ hg: parse error: missing argument [255] +invalid function call should not be optimized to only() + + $ log '"ancestors"(6) and not ancestors(4)' + hg: parse error: not a symbol + [255] + + $ log 'ancestors(6) and not "ancestors"(4)' + hg: parse error: not a symbol + [255] + we can use patterns when searching for tags $ log 'tag("1..*")' @@ -1550,7 +2119,10 @@ 0 $ log '4::8 - 8' 4 - $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)' + +matching() should preserve the order of the input set: + + $ log '(2 or 3 or 1) and matching(1 or 2 or 3)' 2 3 1 @@ -1967,12 +2539,12 @@ (func ('symbol', 'unknownref') ('symbol', '0')) - abort: failed to parse the definition of revset alias "unknownref": '$' not for alias arguments + abort: bad definition of revset alias "unknownref": invalid symbol '$2' [255] $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip" ('symbol', 'tip') - warning: failed to parse the definition of revset alias "anotherbadone": at 7: not a prefix: end + warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end * set: <baseset [9]> 9 @@ -1985,7 +2557,7 @@ $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip" ('symbol', 'tip') - warning: failed to parse the declaration of revset alias "bad name": at 4: invalid token + warning: bad declaration of revset alias "bad name": at 4: invalid token * set: <baseset [9]> 9
--- a/tests/test-rollback.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-rollback.t Fri Jul 01 16:02:56 2016 -0500 @@ -196,3 +196,15 @@ checking files 1 files, 2 changesets, 2 total revisions +rollback disabled by config + $ cat >> $HGRCPATH <<EOF + > [ui] + > rollback = false + > EOF + $ echo narf >> pinky-sayings.txt + $ hg add pinky-sayings.txt + $ hg ci -m 'First one.' + $ hg rollback + abort: rollback is disabled because it is unsafe + (see `hg help -v rollback` for information) + [255]
--- a/tests/test-run-tests.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-run-tests.t Fri Jul 01 16:02:56 2016 -0500 @@ -2,10 +2,7 @@ Avoid interference from actual test env: - $ unset HGTEST_JOBS - $ unset HGTEST_TIMEOUT - $ unset HGTEST_PORT - $ unset HGTEST_SHELL + $ . "$TESTDIR/helper-runtests.sh" Smoke test with install ============ @@ -196,6 +193,10 @@ ]]> </testcase> </testsuite> + $ cat .testtimes + test-failure-unicode.t * (glob) + test-failure.t * (glob) + test-success.t * (glob) $ rm test-failure-unicode.t test for --retest @@ -304,6 +305,8 @@ . # Ran 1 tests, 0 skipped, 0 warned, 0 failed. $ rm test-serve-inuse.t + $ killdaemons.py $DAEMON_PIDS + $ rm $DAEMON_PIDS Running In Debug Mode ====================== @@ -586,11 +589,35 @@ testreport ={ "test-bogus.t": { "result": "skip" - }, + }, "test-failure.t": { "result": "skip" } } (no-eol) + +Whitelist trumps blacklist + $ echo test-failure.t > whitelist + $ rt --blacklist=blacklist --whitelist=whitelist --json\ + > test-failure.t test-bogus.t + s + --- $TESTTMP/test-failure.t + +++ $TESTTMP/test-failure.t.err + @@ -1,5 +1,5 @@ + $ echo babar + - rataxes + + babar + This is a noop statement so that + this test is still more bytes than success. + pad pad pad pad............................................................ + + ERROR: test-failure.t output changed + ! + Skipped test-bogus.t: Doesn't exist + Failed test-failure.t: output changed + # Ran 1 tests, 1 skipped, 0 warned, 1 failed. + python hash seed: * (glob) + [1] + test for --json ==================
--- a/tests/test-subrepo-deep-nested-change.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-subrepo-deep-nested-change.t Fri Jul 01 16:02:56 2016 -0500 @@ -303,9 +303,9 @@ archiving (sub1) [===================================>] 4/4\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) - archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc) - archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc) - archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc) + archiving (sub1/sub2) [ ] 0/2\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (glob) (esc) \r (no-eol) (esc) $ diff -r . ../wdir | egrep -v '\.hg$|^Common subdirectories:' Only in ../wdir: .hg_archival.txt @@ -347,9 +347,9 @@ archiving (sub1) [===================================>] 3/3\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) - archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc) - archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc) - archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc) + archiving (sub1/sub2) [ ] 0/2\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (glob) (esc) \r (no-eol) (esc) $ find ../wdir -type f | sort ../wdir/.hg_archival.txt @@ -379,10 +379,10 @@ archiving (sub1) [===================================>] 3/3\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) - archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc) - archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc) - archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc) - archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc) + archiving (sub1/sub2) [ ] 0/3\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (glob) (esc) \r (no-eol) (esc) $ cat ../wdir/.hg_archival.txt repo: 7f491f53a367861f47ee64a80eb997d1f341b77a @@ -510,10 +510,10 @@ archiving (sub1) [===================================>] 3/3\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) - archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc) - archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc) - archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc) - archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc) + archiving (sub1/sub2) [ ] 0/3\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (glob) (esc) \r (no-eol) (esc) $ find ../archive_all | sort ../archive_all @@ -547,8 +547,8 @@ archiving (sub1) [===================================>] 3/3\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) - archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc) - archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc) + archiving (sub1/sub2) [ ] 0/1\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (glob) (esc) \r (no-eol) (esc) $ find ../archive_exclude | sort ../archive_exclude @@ -568,9 +568,9 @@ archiving (sub1) [ <=> ] 0\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) - archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc) - archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc) - archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc) + archiving (sub1/sub2) [ ] 0/2\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (glob) (esc) \r (no-eol) (esc) $ find ../archive_include | sort ../archive_include @@ -945,7 +945,7 @@ archiving (sub1) [ <=> ] 0\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) - archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (esc) + archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (glob) (esc) \r (no-eol) (esc) \r (no-eol) (esc) archiving (sub3) [ <=> ] 0\r (no-eol) (esc) @@ -959,7 +959,7 @@ archiving (sub1) [ <=> ] 0\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) - archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (esc) + archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (glob) (esc) \r (no-eol) (esc) diff -Nru cloned.*/.hgsub cloned/.hgsub (glob) --- cloned.*/.hgsub * (glob) @@ -987,8 +987,8 @@ archiving (sub1) [===================================>] 1/1\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) - archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc) - archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc) + archiving (sub1/sub2) [ ] 0/1\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (glob) (esc) \r (no-eol) (esc) \r (no-eol) (esc) archiving [ ] 0/8\r (no-eol) (esc) @@ -1006,10 +1006,10 @@ archiving (sub1) [===================================>] 1/1\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) - archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc) - archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc) - archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc) - archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc) + archiving (sub1/sub2) [ ] 0/3\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (glob) (esc) \r (no-eol) (esc) \r (no-eol) (esc) archiving (sub3) [ ] 0/1\r (no-eol) (esc) @@ -1084,8 +1084,8 @@ archiving (sub1) [ <=> ] 0\r (no-eol) (esc) \r (no-eol) (esc) \r (no-eol) (esc) - archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc) - archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc) + archiving (sub1/sub2) [ ] 0/1\r (no-eol) (glob) (esc) + archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (glob) (esc) \r (no-eol) (esc) --- */cloned.*/sub1/sub2/sub2 * (glob) +++ */cloned/sub1/sub2/sub2 * (glob)
--- a/tests/test-subrepo-git.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-subrepo-git.t Fri Jul 01 16:02:56 2016 -0500 @@ -1146,8 +1146,8 @@ $ hg commit -m "add subrepo" $ cd .. $ rm -f pwned.txt - $ env -u GIT_ALLOW_PROTOCOL \ - > PWNED_MSG="your git is too old or mercurial has regressed" hg clone \ + $ unset GIT_ALLOW_PROTOCOL + $ PWNED_MSG="your git is too old or mercurial has regressed" hg clone \ > malicious-subrepository malicious-subrepository-protected Cloning into '$TESTTMP/tc/malicious-subrepository-protected/s'... (glob) fatal: transport 'ext' not allowed
--- a/tests/test-treemanifest.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-treemanifest.t Fri Jul 01 16:02:56 2016 -0500 @@ -742,3 +742,45 @@ $ hg -R deeprepo bundle --all -t v2 deeprepo.bundle abort: repository does not support bundle version 02 [255] + +Pull does not include changegroup for manifest the client already has from +other branch + + $ mkdir grafted-dir-repo + $ cd grafted-dir-repo + $ hg --config experimental.treemanifest=1 init + $ mkdir dir + $ echo a > dir/file + $ echo a > file + $ hg ci -Am initial + adding dir/file + adding file + $ echo b > dir/file + $ hg ci -m updated + $ hg co '.^' + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg revert -r tip dir/ + reverting dir/file (glob) + $ echo b > file # to make sure root manifest is sent + $ hg ci -m grafted + created new head + $ cd .. + + $ hg --config experimental.treemanifest=1 clone --pull -r 1 \ + > grafted-dir-repo grafted-dir-repo-clone + adding changesets + adding manifests + adding file changes + added 2 changesets with 3 changes to 2 files + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd grafted-dir-repo-clone + $ hg pull -r 2 + pulling from $TESTTMP/grafted-dir-repo (glob) + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + (run 'hg heads' to see heads, 'hg merge' to merge) +
--- a/tests/test-up-local-change.t Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/test-up-local-change.t Fri Jul 01 16:02:56 2016 -0500 @@ -90,12 +90,6 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: 1 - $ hg parents - changeset: 0:c19d34741b0a - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: 1 - $ hg --debug up searching for copies back to rev 1 unmatched files in other:
--- a/tests/tinyproxy.py Fri Jul 01 15:12:33 2016 -0500 +++ b/tests/tinyproxy.py Fri Jul 01 16:02:56 2016 -0500 @@ -15,12 +15,15 @@ __version__ = "0.2.1" import BaseHTTPServer -import SocketServer import os import select import socket import sys -import urlparse + +from mercurial import util + +urlparse = util.urlparse +socketserver = util.socketserver class ProxyHandler (BaseHTTPServer.BaseHTTPRequestHandler): __base = BaseHTTPServer.BaseHTTPRequestHandler @@ -132,7 +135,7 @@ do_PUT = do_GET do_DELETE = do_GET -class ThreadingHTTPServer (SocketServer.ThreadingMixIn, +class ThreadingHTTPServer (socketserver.ThreadingMixIn, BaseHTTPServer.HTTPServer): def __init__(self, *args, **kwargs): BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)